Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
79896b0
wip
Lms24 Sep 26, 2025
e3e024b
types, serialization, integration WIP
Lms24 Oct 1, 2025
6221ead
create span v2 envelope
Lms24 Oct 1, 2025
544964e
exports
Lms24 Oct 2, 2025
11345f4
apply ignorespans, improve beforesendspan, handle segment span being …
Lms24 Oct 2, 2025
a202f81
apply common attributes
Lms24 Oct 3, 2025
ff190f3
linter really doesn't like me and I can't blame him
Lms24 Oct 3, 2025
8eb992d
apply scope contexts, extras, request data attributes
Lms24 Oct 3, 2025
717b443
cleanup
Lms24 Oct 14, 2025
577e2b1
changelog entry
Lms24 Oct 14, 2025
43a6c30
size-limit bumps
Lms24 Oct 14, 2025
5816c31
fix lint, circular deps, size limit
Lms24 Oct 14, 2025
e748a42
bump preview version
Lms24 Oct 15, 2025
1c874ca
s/makeV2Callback/withStreamSpan
Lms24 Oct 15, 2025
5e32795
add todos for event processors and integration hooks
Lms24 Oct 15, 2025
be4aab2
changelog
Lms24 Oct 16, 2025
2484e39
export withStreamSpan from browser
Lms24 Oct 16, 2025
fa08262
changelog
Lms24 Oct 16, 2025
d2d0cdb
fix some attribute mishaps
Lms24 Oct 31, 2025
97eef15
remove is_remote, add is_segment
Lms24 Nov 10, 2025
f51b865
add `sentry.segment.id` common span attribute
Lms24 Nov 10, 2025
f41837d
rip span kind
Lms24 Nov 12, 2025
d1a2704
restart ci
Lms24 Nov 12, 2025
0c3a73e
does this fix size limit?
Lms24 Nov 21, 2025
9bbb80e
size limit once more
Lms24 Nov 21, 2025
3037ce8
s/user.username/user.name
Lms24 Nov 25, 2025
bc7d717
one more limit bump
Lms24 Nov 25, 2025
b07f572
rewrite to `captureSpan`
Lms24 Nov 28, 2025
31023c0
capturespan
Lms24 Nov 28, 2025
d9f7336
add integration test for pageload span
Lms24 Dec 1, 2025
23151b3
more integration tests
Lms24 Dec 1, 2025
12c50ee
span links test
Lms24 Dec 1, 2025
7e9782b
set web vitals as attributes in span-first
Lms24 Dec 2, 2025
bf9a4cf
rewrite httpContext integration to use processSpan client hook
Lms24 Dec 2, 2025
e63bbb0
minor lint stuff
Lms24 Dec 4, 2025
58fed40
initial StreamingSpanExporter implementation for otel
Lms24 Dec 4, 2025
7d7ca23
rewrite pipeline to just always use spanJSonV2 because thanks OTel
Lms24 Dec 5, 2025
b4c0ef4
wip
Lms24 Dec 10, 2025
443e72c
add exporter
Lms24 Dec 10, 2025
b0cef89
more contexts
Lms24 Dec 10, 2025
02d2710
add unit tests for captureSpan pipeline and utils
Lms24 Dec 11, 2025
4c33e5a
add another test for scope attribute precedence over contexts
Lms24 Dec 12, 2025
33ebe22
Potential fix for code scanning alert no. 424: Missing regular expres…
Lms24 Dec 12, 2025
a1086e2
fix lint errors
Lms24 Dec 12, 2025
077e83d
skip integration tests for cdn bundles
Lms24 Dec 12, 2025
25dad24
fix missing exports of `withStreamSpan`
Lms24 Dec 12, 2025
ed5c8a5
apply request data to segment span
Lms24 Dec 15, 2025
bf3294b
register a bunch of known contexts
Lms24 Dec 15, 2025
a0a915e
use new attribute serialization for spanv2
Lms24 Dec 15, 2025
9b96157
remove unused code
Lms24 Dec 15, 2025
4da7d09
flush buffer when `Sentry.flush()` is called
Lms24 Dec 16, 2025
ce3d935
extract span buffer to class, add serverSpanStreamingIntegration, use…
Lms24 Dec 16, 2025
b85a4e3
raise size limits
Lms24 Dec 17, 2025
0d0abe3
save some bytes in httpContext integration when tree-shaking tracing
Lms24 Dec 17, 2025
7d62127
fix test flakiness
Lms24 Dec 18, 2025
c61b3b2
fix regex
Lms24 Dec 18, 2025
d3e6af9
set `sentry.span.source` attribute
Lms24 Jan 9, 2026
1113876
update with improved span serialization logic from scope attribute PRs
Lms24 Jan 9, 2026
571c19a
remove redundant attribute serilaization functions
Lms24 Jan 9, 2026
8f56324
spanStreamingIntegration everywhere
Lms24 Jan 9, 2026
2b9a55a
use `SpanBuffer` in browser
Lms24 Jan 9, 2026
6a87239
fixups and failing node integration tests :)
Lms24 Jan 14, 2026
455c1c5
add web vital support
Lms24 Jan 19, 2026
91f9aa8
feat(test-utils): Add a way to wait for single spans for Span streami…
JPeer264 Jan 28, 2026
5fe3b0f
web vitals wip
Lms24 Jan 30, 2026
1e6ca47
add plan for step-by-step migration
Lms24 Jan 30, 2026
3f7c90a
docs: Mark PR 1 as completed in span streaming plan
Lms24 Jan 30, 2026
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
703 changes: 703 additions & 0 deletions .cursor/plans/span_streaming_prs_43411dc5.plan.md

Large diffs are not rendered by default.

52 changes: 26 additions & 26 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ module.exports = [
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init'),
gzip: true,
limit: '25.5 KB',
limit: '26KB',
},
{
name: '@sentry/browser - with treeshaking flags',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init'),
gzip: true,
limit: '24.1 KB',
limit: '25 KB',
modifyWebpackConfig: function (config) {
const webpack = require('webpack');

Expand Down Expand Up @@ -47,6 +47,13 @@ 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',
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.55 KB',
limit: '87 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: '98 KB',
limit: '99 KB',
},
{
name: '@sentry/browser (incl. Feedback)',
Expand Down Expand Up @@ -148,43 +155,43 @@ module.exports = [
import: createImport('init', 'ErrorBoundary', 'reactRouterV6BrowserTracingIntegration'),
ignore: ['react/jsx-runtime'],
gzip: true,
limit: '44.5 KB',
limit: '46 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)',
path: 'packages/vue/build/esm/index.js',
import: createImport('init', 'browserTracingIntegration'),
gzip: true,
limit: '44.1 KB',
limit: '45 KB',
},
// Svelte SDK (ESM)
{
name: '@sentry/svelte',
path: 'packages/svelte/build/esm/index.js',
import: createImport('init'),
gzip: true,
limit: '25.5 KB',
limit: '26 KB',
},
// Browser CDN bundles
{
name: 'CDN Bundle',
path: createCDNPath('bundle.min.js'),
gzip: true,
limit: '28 KB',
limit: '28.5 KB',
},
{
name: 'CDN Bundle (incl. Tracing)',
path: createCDNPath('bundle.tracing.min.js'),
gzip: true,
limit: '43 KB',
limit: '44 KB',
},
{
name: 'CDN Bundle (incl. Logs, Metrics)',
Expand All @@ -208,7 +215,7 @@ module.exports = [
name: 'CDN Bundle (incl. Tracing, Replay)',
path: createCDNPath('bundle.tracing.replay.min.js'),
gzip: true,
limit: '80 KB',
limit: '81 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Logs, Metrics)',
Expand All @@ -234,21 +241,14 @@ module.exports = [
path: createCDNPath('bundle.min.js'),
gzip: false,
brotli: false,
limit: '82 KB',
limit: '83 KB',
},
{
name: 'CDN Bundle (incl. Tracing) - uncompressed',
path: createCDNPath('bundle.tracing.min.js'),
gzip: false,
brotli: false,
limit: '128 KB',
},
{
name: 'CDN Bundle (incl. Logs, Metrics) - uncompressed',
path: createCDNPath('bundle.logs.metrics.min.js'),
gzip: false,
brotli: false,
limit: '86 KB',
limit: '129 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed',
Expand All @@ -269,7 +269,7 @@ module.exports = [
path: createCDNPath('bundle.tracing.replay.min.js'),
gzip: false,
brotli: false,
limit: '245 KB',
limit: '247 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed',
Expand Down Expand Up @@ -299,7 +299,7 @@ module.exports = [
import: createImport('init'),
ignore: ['next/router', 'next/constants'],
gzip: true,
limit: '47 KB',
limit: '48 KB',
},
// SvelteKit SDK (ESM)
{
Expand All @@ -308,7 +308,7 @@ module.exports = [
import: createImport('init'),
ignore: ['$app/stores'],
gzip: true,
limit: '43 KB',
limit: '44 KB',
},
// Node-Core SDK (ESM)
{
Expand All @@ -326,14 +326,14 @@ module.exports = [
import: createImport('init'),
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
gzip: true,
limit: '167 KB',
limit: '166 KB',
},
{
name: '@sentry/node - without tracing',
path: 'packages/node/build/esm/index.js',
import: createImport('initWithoutDefaultIntegrations', 'getDefaultIntegrationsWithoutPerformance'),
gzip: true,
limit: '95 KB',
limit: '97 KB',
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
modifyWebpackConfig: function (config) {
const webpack = require('webpack');
Expand All @@ -356,7 +356,7 @@ module.exports = [
import: createImport('init'),
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
gzip: true,
limit: '111 KB',
limit: '112 KB',
},
];

Expand Down
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
},
"angular.enable-strict-mode-prompt": false
}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,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
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://[email protected]/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,24 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';
import { shouldSkipTracingTest, testingCdnBundle } from '../../../utils/helpers';
import { getSpanOp, waitForV2Spans } from '../../../utils/spanFirstUtils';

sentryTest('ends pageload span when the page goes to background', async ({ getLocalTestUrl, page }) => {
// for now, spanStreamingIntegration is only exported in the NPM package, so we skip the test for bundles.
if (shouldSkipTracingTest() || testingCdnBundle()) {
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://[email protected]/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,48 @@
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../utils/fixtures';
import {
envelopeRequestParser,
runScriptInSandbox,
shouldSkipTracingTest,
testingCdnBundle,
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 }) => {
// for now, spanStreamingIntegration is only exported in the NPM package, so we skip the test for bundles.
// This test fails on Webkit as errors thrown from `runScriptInSandbox` are Script Errors and skipped by Sentry
if (shouldSkipTracingTest() || testingCdnBundle() || browserName === 'webkit') {
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://[email protected]/1337',
traceLifecycle: 'stream',
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],
tracePropagationTargets: ['http://sentry-test-site.example'],
tracesSampleRate: 1,
sendDefaultPii: true,
});
Loading
Loading