Skip to content
Merged
23 changes: 23 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,29 @@ Sentry.init({
- The experimental `_experiments.enableStandaloneClsSpans` and `_experiments.enableStandaloneLcpSpans` options were removed from both `browserTracingIntegration` and `webVitalsIntegration`. CLS and LCP are no longer configurable: they are recorded as measurements on the pageload span, unless span streaming is enabled (`traceLifecycle: 'stream'`), in which case they are sent as dedicated spans.
- INP is now always sent as a web vital span (streamed when span streaming is enabled, standalone otherwise) that carries its value as a `browser.web_vital.inp.value` attribute. Previously, with span streaming disabled, INP was sent as a standalone span that carried its value as a span measurement.

- `browserTracingIntegration` no longer captures spans created by `performance.mark()` and `performance.measure()` by default. Add `userTimingIntegration()` to continue capturing them. The `ignorePerformanceApiSpans` option moved to the new integration as `ignore`.

```js
// before
Sentry.init({
integrations: [
Sentry.browserTracingIntegration({
ignorePerformanceApiSpans: ['third-party-mark'],
}),
],
});

// after
Sentry.init({
integrations: [
Sentry.browserTracingIntegration(),
Sentry.userTimingIntegration({
ignore: ['third-party-mark'],
}),
],
});
```

### `@sentry/node` / Server-side SDKs

- `SentryContextManager` is no longer exported. It is no longer needed now that Sentry does not set up OpenTelemetry by default.
Expand Down

This file was deleted.

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',
integrations: [Sentry.browserTracingIntegration()],
tracesSampleRate: 1,
});

performance.mark('app-ready');
performance.measure('app-initialization');
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../../utils/fixtures';
import { shouldSkipTracingTest } from '../../../../utils/helpers';
import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils';

sentryTest('does not capture mark and measure spans by default', async ({ getLocalTestUrl, page }) => {
sentryTest.skip(shouldSkipTracingTest());

const url = await getLocalTestUrl({ testDir: __dirname });
const spansPromise = waitForStreamedSpans(page, spans => spans.some(span => getSpanOp(span) === 'pageload'));

await page.goto(url);

const spans = await spansPromise;
const userTimingSpans = spans.filter(span => ['mark', 'measure'].includes(getSpanOp(span) ?? ''));

expect(userTimingSpans).toHaveLength(0);
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: wdyt about pulling all the usertiming tests out of the /metrics sub directory? Given this is now an integration, it could just live under tracing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 I think the browser metrics is a bloated term right now

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/browser';

import { userTimingIntegration } from '@sentry/browser';
// Create measures BEFORE SDK initializes

// Create a measure with detail
Expand All @@ -24,7 +24,7 @@ window.Sentry = Sentry;
Sentry.init({
traceLifecycle: 'static',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration({})],
integrations: [Sentry.browserTracingIntegration(), userTimingIntegration()],
tracesSampleRate: 1,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ sentryTest(
expect(restrictedMeasure).toBeDefined();
expect(restrictedMeasure?.data).toMatchObject({
'sentry.op': 'measure',
'sentry.origin': 'auto.resource.browser.metrics',
'sentry.origin': 'auto.browser.user_timing.measure',
});

// Verify no detail attributes were added due to the permission error
Expand All @@ -39,15 +39,15 @@ sentryTest(
expect(normalMeasure?.data).toMatchObject({
'sentry.browser.measure.detail': 'this-should-work',
'sentry.op': 'measure',
'sentry.origin': 'auto.resource.browser.metrics',
'sentry.origin': 'auto.browser.user_timing.measure',
});

// Test 3: Verify the complex detail object is captured correctly
const complexMeasure = measureSpans?.find(span => span.description === 'complex-detail-measure');
expect(complexMeasure).toBeDefined();
expect(complexMeasure?.data).toMatchObject({
'sentry.op': 'measure',
'sentry.origin': 'auto.resource.browser.metrics',
'sentry.origin': 'auto.browser.user_timing.measure',
// The entire nested object is stringified as a single value
'sentry.browser.measure.detail.nested': JSON.stringify({
array: [1, 2, 3],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Add measure before SDK initializes
import * as Sentry from '@sentry/browser';
import { userTimingIntegration } from '@sentry/browser';

const end = performance.now();
performance.measure('Next.js-before-hydration', {
Expand All @@ -12,6 +13,6 @@ window.Sentry = Sentry;
Sentry.init({
traceLifecycle: 'static',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration()],
integrations: [Sentry.browserTracingIntegration(), userTimingIntegration()],
tracesSampleRate: 1,
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ sentryTest('should add browser-related spans to pageload transaction', async ({
'sentry.browser.measure_happened_before_request': true,
'sentry.browser.measure_start_time': expect.any(Number),
'sentry.op': 'measure',
'sentry.origin': 'auto.resource.browser.metrics',
'sentry.origin': 'auto.browser.user_timing.measure',
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import * as Sentry from '@sentry/browser';
import { userTimingIntegration } from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
traceLifecycle: 'static',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [
Sentry.browserTracingIntegration({
ignorePerformanceApiSpans: ['measure-ignore', /mark-i/],
}),
],
integrations: [Sentry.browserTracingIntegration(), userTimingIntegration({ ignore: ['measure-ignore', /mark-i/] })],
tracesSampleRate: 1,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../../utils/fixtures';
import { shouldSkipTracingTest } from '../../../../utils/helpers';
import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils';

sentryTest('captures non-ignored mark and measure spans', async ({ getLocalTestUrl, page }) => {
sentryTest.skip(shouldSkipTracingTest());

const url = await getLocalTestUrl({ testDir: __dirname });
const spansPromise = waitForStreamedSpans(page, spans => spans.some(span => getSpanOp(span) === 'pageload'));

await page.goto(url);

const spans = await spansPromise;
const userTimingSpans = spans
.filter(span => ['mark', 'measure'].includes(getSpanOp(span) ?? ''))
.map(span => ({
name: span.name,
op: getSpanOp(span),
origin: span.attributes['sentry.origin']?.value,
}))
.sort((a, b) => a.name.localeCompare(b.name));

expect(userTimingSpans).toEqual([
{ name: 'mark-pass', op: 'mark', origin: 'auto.browser.user_timing.mark' },
{ name: 'measure-pass', op: 'measure', origin: 'auto.browser.user_timing.measure' },
]);
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record<string, string> = {
contextLinesIntegration: 'contextlines',
extraErrorDataIntegration: 'extraerrordata',
reportingObserverIntegration: 'reportingobserver',
userTimingIntegration: 'usertiming',
feedbackIntegration: 'feedback',
moduleMetadataIntegration: 'modulemetadata',
graphqlClientIntegration: 'graphqlclient',
Expand Down
2 changes: 2 additions & 0 deletions packages/browser-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export {
// eslint-disable-next-line typescript/no-deprecated
export { elementTimingIntegration, startTrackingElementTiming } from './metrics/elementTiming';

export { userTimingIntegration } from './metrics/userTiming';

export { extractNetworkProtocol } from './metrics/utils';

export { trackClsAsSpan, trackInpAsSpan, trackLcpAsSpan } from './metrics/webVitalSpans';
Expand Down
Loading
Loading