Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b942100
wip
Lms24 Sep 26, 2025
0bc1c64
types, serialization, integration WIP
Lms24 Oct 1, 2025
c3fd146
create span v2 envelope
Lms24 Oct 1, 2025
089346b
exports
Lms24 Oct 2, 2025
1ef681b
apply ignorespans, improve beforesendspan, handle segment span being …
Lms24 Oct 2, 2025
475edff
apply common attributes
Lms24 Oct 3, 2025
4d50189
linter really doesn't like me and I can't blame him
Lms24 Oct 3, 2025
7bed83f
apply scope contexts, extras, request data attributes
Lms24 Oct 3, 2025
fdf0ca9
cleanup
Lms24 Oct 14, 2025
577e0df
changelog entry
Lms24 Oct 14, 2025
fbac928
size-limit bumps
Lms24 Oct 14, 2025
3978fdb
fix lint, circular deps, size limit
Lms24 Oct 14, 2025
b2b03f6
bump preview version
Lms24 Oct 15, 2025
db28863
s/makeV2Callback/withStreamSpan
Lms24 Oct 15, 2025
af37ab1
add todos for event processors and integration hooks
Lms24 Oct 15, 2025
ea970e2
changelog
Lms24 Oct 16, 2025
2dc4c92
export withStreamSpan from browser
Lms24 Oct 16, 2025
efef485
changelog
Lms24 Oct 16, 2025
67897f3
fix some attribute mishaps
Lms24 Oct 31, 2025
826a237
remove is_remote, add is_segment
Lms24 Nov 10, 2025
51c8de1
add `sentry.segment.id` common span attribute
Lms24 Nov 10, 2025
4b4bea4
rip span kind
Lms24 Nov 12, 2025
c4c5bdd
restart ci
Lms24 Nov 12, 2025
2c8f0d6
does this fix size limit?
Lms24 Nov 21, 2025
09e6d7e
kk limits fixed but raise limits :(
Lms24 Nov 21, 2025
7856594
size limit once more
Lms24 Nov 21, 2025
25b73c6
s/user.username/user.name
Lms24 Nov 25, 2025
1b6d231
one more limit bump
Lms24 Nov 25, 2025
109b060
changelog
Lms24 Nov 26, 2025
cf256ff
change version
Lms24 Nov 26, 2025
ba4b5a6
release: 10.28.0-alpha.0
getsentry-bot Nov 26, 2025
1973b14
Merge branch 'release/10.28.0-alpha.0' into lms/feat-span-streaming-poc
Nov 26, 2025
227cc9f
rewrite to `captureSpan`
Lms24 Nov 28, 2025
15f79f5
capturespan
Lms24 Nov 28, 2025
6d178bc
add integration test for pageload span
Lms24 Dec 1, 2025
9c2c219
more integration tests
Lms24 Dec 1, 2025
f5f6f89
span links test
Lms24 Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,19 @@ module.exports = [
gzip: true,
limit: '48 KB',
},
// {
// name: '@sentry/browser (incl. Tracing Span-First)',
// path: 'packages/browser/build/npm/esm/index.js',
// import: createImport('init', 'browserTracingIntegration', 'spanStreamingIntegration'),
// gzip: true,
// limit: '44 KB',
// },
{
name: '@sentry/browser (incl. Tracing, Replay)',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration'),
gzip: true,
limit: '80 KB',
limit: '82 KB',
},
{
name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags',
Expand Down Expand Up @@ -82,14 +89,14 @@ module.exports = [
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'replayCanvasIntegration'),
gzip: true,
limit: '85 KB',
limit: '86 KB',
},
{
name: '@sentry/browser (incl. Tracing, Replay, Feedback)',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'feedbackIntegration'),
gzip: true,
limit: '97 KB',
limit: '98 KB',
},
{
name: '@sentry/browser (incl. Feedback)',
Expand All @@ -103,7 +110,7 @@ module.exports = [
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'sendFeedback'),
gzip: true,
limit: '30 KB',
limit: '31 KB',
},
{
name: '@sentry/browser (incl. FeedbackAsync)',
Expand All @@ -127,15 +134,15 @@ module.exports = [
import: createImport('init', 'ErrorBoundary', 'reactRouterV6BrowserTracingIntegration'),
ignore: ['react/jsx-runtime'],
gzip: true,
limit: '44 KB',
limit: '45 KB',
},
// Vue SDK (ESM)
{
name: '@sentry/vue',
path: 'packages/vue/build/esm/index.js',
import: createImport('init'),
gzip: true,
limit: '30 KB',
limit: '31 KB',
},
{
name: '@sentry/vue (incl. Tracing)',
Expand Down Expand Up @@ -163,7 +170,7 @@ module.exports = [
name: 'CDN Bundle (incl. Tracing)',
path: createCDNPath('bundle.tracing.min.js'),
gzip: true,
limit: '42.5 KB',
limit: '43 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay)',
Expand Down Expand Up @@ -213,7 +220,7 @@ module.exports = [
import: createImport('init'),
ignore: ['next/router', 'next/constants'],
gzip: true,
limit: '46 KB',
limit: '47 KB',
},
// SvelteKit SDK (ESM)
{
Expand All @@ -222,7 +229,7 @@ module.exports = [
import: createImport('init'),
ignore: ['$app/stores'],
gzip: true,
limit: '42 KB',
limit: '43 KB',
},
// Node-Core SDK (ESM)
{
Expand All @@ -240,7 +247,7 @@ module.exports = [
import: createImport('init'),
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
gzip: true,
limit: '160 KB',
limit: '161 KB',
},
{
name: '@sentry/node - without tracing',
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@

- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott

## 10.28.0-alpha.0

This release is a preview release for sending spans in browser via spanV2 instead of transaction event envelopes. All of this is experimental and subject to change. Use at your own risk. [More Details.](https://github.com/getsentry/sentry-javascript/pull/17852)

Changes from 10.24-alpha.1:

- add `sentry.segment.id` common span attribute
- rename attribute `user.username` to `user.name`
- remove `is_remote`, add `is_segment` span attributes
- remove `span.kind` field

## 10.27.0

### Important Changes
Expand Down Expand Up @@ -353,6 +364,16 @@ Work in this release was contributed by @hanseo0507. Thank you for your contribu

Work in this release was contributed by @0xbad0c0d3. Thank you for your contribution!

## 10.21.0-alpha.1

This release is a preview release for sending spans in browser via spanV2 instead of transaction event envelopes. All of this is experimental and subject to change. Use at your own risk. [More Details.](https://github.com/getsentry/sentry-javascript/pull/17852)

- export withStreamSpan from `@sentry/browser`

## 10.21.0-alpha.0

This release is a preview release for sending spans in browser via spanV2 instead of transaction event envelopes. All of this is experimental and subject to change. Use at your own risk. [More Details.](https://github.com/getsentry/sentry-javascript/pull/17852)

## 10.20.0

### Important Changes
Expand Down
4 changes: 2 additions & 2 deletions dev-packages/browser-integration-tests/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sentry-internal/browser-integration-tests",
"version": "10.27.0",
"version": "10.28.0-alpha.0",
"main": "index.js",
"license": "MIT",
"engines": {
Expand Down Expand Up @@ -43,7 +43,7 @@
"@babel/preset-typescript": "^7.16.7",
"@playwright/test": "~1.53.2",
"@sentry-internal/rrweb": "2.34.0",
"@sentry/browser": "10.27.0",
"@sentry/browser": "10.28.0-alpha.0",
"@supabase/supabase-js": "2.49.3",
"axios": "^1.12.2",
"babel-loader": "^8.2.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
traceLifecycle: 'stream',
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],
tracePropagationTargets: ['http://sentry-test-site.example'],
tracesSampleRate: 1,
sendDefaultPii: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
document.getElementById('go-background').addEventListener('click', () => {
setTimeout(() => {
Object.defineProperty(document, 'hidden', { value: true, writable: true });
const ev = document.createEvent('Event');
ev.initEvent('visibilitychange');
document.dispatchEvent(ev);
}, 250);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<button id="go-background">New Tab</button>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';
import { shouldSkipTracingTest } from '../../../utils/helpers';
import { getSpanOp, waitForV2Spans } from '../../../utils/spanFirstUtils';

sentryTest('ends pageload span when the page goes to background', async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}
const url = await getLocalTestUrl({ testDir: __dirname });

const spanPromise = waitForV2Spans(page, spans => !!spans.find(span => getSpanOp(span) === 'pageload'));

await page.goto(url);
await page.locator('#go-background').click();

const pageloadSpan = (await spanPromise).find(span => getSpanOp(span) === 'pageload');

expect(pageloadSpan?.status).toBe('error'); // a cancelled span previously mapped to status error with message cancelled.
expect(pageloadSpan?.attributes?.['sentry.op']?.value).toBe('pageload');
expect(pageloadSpan?.attributes?.['sentry.cancellation_reason']?.value).toBe('document.hidden');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
traceLifecycle: 'stream',
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],
tracePropagationTargets: ['http://sentry-test-site.example'],
tracesSampleRate: 1,
sendDefaultPii: true,
debug: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../utils/fixtures';
import {
envelopeRequestParser,
runScriptInSandbox,
shouldSkipTracingTest,
waitForErrorRequest,
} from '../../../utils/helpers';
import { getSpanOp, waitForV2Spans } from '../../../utils/spanFirstUtils';

sentryTest(
'puts the pageload span name onto an error event caught during pageload',
async ({ getLocalTestUrl, page, browserName }) => {
if (browserName === 'webkit') {
// This test fails on Webkit as errors thrown from `runScriptInSandbox` are Script Errors and skipped by Sentry
sentryTest.skip();
}

if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestUrl({ testDir: __dirname });

const errorEventPromise = waitForErrorRequest(page);
const spanPromise = waitForV2Spans(page, spans => !!spans.find(span => getSpanOp(span) === 'pageload'));

await page.goto(url);

await runScriptInSandbox(page, {
content: `
throw new Error('Error during pageload');
`,
});

const errorEvent = envelopeRequestParser<Event>(await errorEventPromise);
const pageloadSpan = (await spanPromise).find(span => getSpanOp(span) === 'pageload');

expect(pageloadSpan?.attributes?.['sentry.op']?.value).toEqual('pageload');
expect(errorEvent.exception?.values?.[0]).toBeDefined();

expect(pageloadSpan?.name).toEqual('/index.html');

expect(pageloadSpan?.status).toBe('error');
expect(pageloadSpan?.attributes?.['sentry.idle_span_finish_reason']?.value).toBe('idleTimeout');

expect(errorEvent.transaction).toEqual(pageloadSpan?.name);
},
);
12 changes: 12 additions & 0 deletions dev-packages/browser-integration-tests/suites/span-first/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
traceLifecycle: 'stream',
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],
tracePropagationTargets: ['http://sentry-test-site.example'],
tracesSampleRate: 1,
sendDefaultPii: true,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { expect } from '@playwright/test';
import { SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE } from '@sentry/core';
import { sentryTest } from '../../../utils/fixtures';
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../utils/helpers';
import { getSpanOp, waitForV2Spans } from '../../../utils/spanFirstUtils';

sentryTest("navigation spans link back to previous trace's root span", async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestUrl({ testDir: __dirname });

const pageloadSpan = await sentryTest.step('Initial pageload', async () => {
const pageloadSpanPromise = waitForV2Spans(page, spans => !!spans.find(span => getSpanOp(span) === 'pageload'));
await page.goto(url);
return (await pageloadSpanPromise).find(span => getSpanOp(span) === 'pageload');
});

const navigation1Span = await sentryTest.step('First navigation', async () => {
const navigation1SpanPromise = waitForV2Spans(
page,
spans => !!spans.find(span => getSpanOp(span) === 'navigation'),
);
await page.goto(`${url}#foo`);
return (await navigation1SpanPromise).find(span => getSpanOp(span) === 'navigation');
});

const navigation2Span = await sentryTest.step('Second navigation', async () => {
const navigation2SpanPromise = waitForV2Spans(
page,
spans => !!spans.find(span => getSpanOp(span) === 'navigation'),
);
await page.goto(`${url}#bar`);
return (await navigation2SpanPromise).find(span => getSpanOp(span) === 'navigation');
});

const pageloadTraceId = pageloadSpan?.trace_id;
const navigation1TraceId = navigation1Span?.trace_id;
const navigation2TraceId = navigation2Span?.trace_id;

expect(pageloadSpan?.links).toBeUndefined();

expect(navigation1Span?.links).toEqual([
{
trace_id: pageloadTraceId,
span_id: pageloadSpan?.span_id,
sampled: true,
attributes: {
[SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE]: { value: 'previous_trace', type: 'string' },
},
},
]);

expect(navigation1Span?.attributes).toMatchObject({
'sentry.previous_trace': { type: 'string', value: `${pageloadTraceId}-${pageloadSpan?.span_id}-1` },
});

expect(navigation2Span?.links).toEqual([
{
trace_id: navigation1TraceId,
span_id: navigation1Span?.span_id,
sampled: true,
attributes: {
[SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE]: { value: 'previous_trace', type: 'string' },
},
},
]);

expect(navigation2Span?.attributes).toMatchObject({
'sentry.previous_trace': { type: 'string', value: `${navigation1TraceId}-${navigation1Span?.span_id}-1` },
});

expect(pageloadTraceId).not.toEqual(navigation1TraceId);
expect(navigation1TraceId).not.toEqual(navigation2TraceId);
expect(pageloadTraceId).not.toEqual(navigation2TraceId);
});

sentryTest("doesn't link between hard page reloads by default", async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestUrl({ testDir: __dirname });

await sentryTest.step('First pageload', async () => {
const pageloadRequestPromise = waitForV2Spans(page, spans => !!spans.find(span => getSpanOp(span) === 'pageload'));
await page.goto(url);
return (await pageloadRequestPromise).find(span => getSpanOp(span) === 'pageload');
});

await sentryTest.step('Second pageload', async () => {
const pageload2RequestPromise = waitForV2Spans(page, spans => !!spans.find(span => getSpanOp(span) === 'pageload'));
await page.reload();
const pageload2Span = (await pageload2RequestPromise).find(span => getSpanOp(span) === 'pageload');

expect(pageload2Span?.trace_id).toBeDefined();
expect(pageload2Span?.links).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
traceLifecycle: 'stream',
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],
tracePropagationTargets: ['http://sentry-test-site.example'],
tracesSampleRate: 1,
sendDefaultPii: true,
});
Loading