Skip to content

Commit 3d34752

Browse files
authored
fix(browser): Ensure pageload trace remains active after pageload span finished (#11600)
Remove the now incorrect propagationContext resetting logic for pageload spans and add tests to cover this scenario.
1 parent d7d4359 commit 3d34752

File tree

8 files changed

+110
-36
lines changed

8 files changed

+110
-36
lines changed

dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,3 @@ sentryTest('should create a navigation transaction on page navigation', async ({
4949

5050
expect(pageloadSpanId).not.toEqual(navigationSpanId);
5151
});
52-
53-
sentryTest('should create a new trace for for multiple navigations', async ({ getLocalTestPath, page }) => {
54-
if (shouldSkipTracingTest()) {
55-
sentryTest.skip();
56-
}
57-
58-
const url = await getLocalTestPath({ testDir: __dirname });
59-
60-
await getFirstSentryEnvelopeRequest<Event>(page, url);
61-
const navigationEvent1 = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);
62-
const navigationEvent2 = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#bar`);
63-
64-
expect(navigationEvent1.contexts?.trace?.op).toBe('navigation');
65-
expect(navigationEvent2.contexts?.trace?.op).toBe('navigation');
66-
67-
const navigation1TraceId = navigationEvent1.contexts?.trace?.trace_id;
68-
const navigation2TraceId = navigationEvent2.contexts?.trace?.trace_id;
69-
70-
expect(navigation1TraceId).toBeDefined();
71-
expect(navigation2TraceId).toBeDefined();
72-
expect(navigation1TraceId).not.toEqual(navigation2TraceId);
73-
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
integrations: [Sentry.browserTracingIntegration()],
8+
tracesSampleRate: 1,
9+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/types';
3+
import { sentryTest } from '../../../../utils/fixtures';
4+
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
5+
6+
sentryTest('should create a new trace on each navigation', async ({ getLocalTestPath, page }) => {
7+
if (shouldSkipTracingTest()) {
8+
sentryTest.skip();
9+
}
10+
11+
const url = await getLocalTestPath({ testDir: __dirname });
12+
13+
await getFirstSentryEnvelopeRequest<Event>(page, url);
14+
const navigationEvent1 = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);
15+
const navigationEvent2 = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#bar`);
16+
17+
expect(navigationEvent1.contexts?.trace?.op).toBe('navigation');
18+
expect(navigationEvent2.contexts?.trace?.op).toBe('navigation');
19+
20+
const navigation1TraceId = navigationEvent1.contexts?.trace?.trace_id;
21+
const navigation2TraceId = navigationEvent2.contexts?.trace?.trace_id;
22+
23+
expect(navigation1TraceId).toMatch(/^[0-9a-f]{32}$/);
24+
expect(navigation2TraceId).toMatch(/^[0-9a-f]{32}$/);
25+
expect(navigation1TraceId).not.toEqual(navigation2TraceId);
26+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/types';
3+
import { sentryTest } from '../../../../utils/fixtures';
4+
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
5+
6+
sentryTest(
7+
'should create a new trace for a navigation after the initial pageload',
8+
async ({ getLocalTestPath, page }) => {
9+
if (shouldSkipTracingTest()) {
10+
sentryTest.skip();
11+
}
12+
13+
const url = await getLocalTestPath({ testDir: __dirname });
14+
15+
const pageloadEvent = await getFirstSentryEnvelopeRequest<Event>(page, url);
16+
const navigationEvent1 = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);
17+
18+
expect(pageloadEvent.contexts?.trace?.op).toBe('pageload');
19+
expect(navigationEvent1.contexts?.trace?.op).toBe('navigation');
20+
21+
const pageloadTraceId = pageloadEvent.contexts?.trace?.trace_id;
22+
const navigation1TraceId = navigationEvent1.contexts?.trace?.trace_id;
23+
24+
expect(pageloadTraceId).toMatch(/^[0-9a-f]{32}$/);
25+
expect(navigation1TraceId).toMatch(/^[0-9a-f]{32}$/);
26+
expect(pageloadTraceId).not.toEqual(navigation1TraceId);
27+
},
28+
);
29+
30+
sentryTest('error after pageload has pageload traceId', async ({ getLocalTestPath, page }) => {
31+
if (shouldSkipTracingTest()) {
32+
sentryTest.skip();
33+
}
34+
35+
const url = await getLocalTestPath({ testDir: __dirname });
36+
37+
const pageloadEvent = await getFirstSentryEnvelopeRequest<Event>(page, url);
38+
expect(pageloadEvent.contexts?.trace?.op).toBe('pageload');
39+
40+
const pageloadTraceId = pageloadEvent.contexts?.trace?.trace_id;
41+
expect(pageloadTraceId).toMatch(/^[0-9a-f]{32}$/);
42+
43+
const [, errorEvent] = await Promise.all([
44+
page.locator('#errorBtn').click(),
45+
getFirstSentryEnvelopeRequest<Event>(page),
46+
]);
47+
48+
const errorTraceId = errorEvent.contexts?.trace?.trace_id;
49+
expect(errorTraceId).toBe(pageloadTraceId);
50+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const errorBtn = document.getElementById('errorBtn');
2+
errorBtn.addEventListener('click', () => {
3+
throw new Error('Sentry Test Error');
4+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<button id="errorBtn">Throw Error</button>
8+
</body>
9+
</html>

packages/browser/src/tracing/browserTracingIntegration.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,11 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
265265
// This is a bit hacky, because we want to get the span to use both the correct scope _and_ the correct propagation context
266266
// but afterwards, we want to reset it to avoid this also applying to other spans
267267
const scope = getCurrentScope();
268-
const propagationContextBefore = scope.getPropagationContext();
269268

270269
activeSpan = continueTrace({ sentryTrace, baggage }, () => {
271-
// We update the outer current scope to have the correct propagation context...
270+
// We update the outer current scope to have the correct propagation context
271+
// this means, the scope active when the pageload span is created will continue to hold the
272+
// propagationContext from the incoming trace, even after the pageload span ended.
272273
scope.setPropagationContext(getCurrentScope().getPropagationContext());
273274

274275
// Ensure we are on the original current scope again, so the span is set as active on it
@@ -279,9 +280,6 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
279280
});
280281
});
281282
});
282-
283-
// Reset this back to what it was before
284-
scope.setPropagationContext(propagationContextBefore);
285283
});
286284

287285
if (options.instrumentPageLoad && WINDOW.location) {

packages/browser/test/unit/tracing/browserTracingIntegration.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -710,9 +710,9 @@ describe('browserTracingIntegration', () => {
710710
expect(dynamicSamplingContext).toBeDefined();
711711
expect(dynamicSamplingContext).toStrictEqual({ release: '2.1.14' });
712712

713-
// Propagation context is reset and does not contain the meta tag data
714-
expect(propagationContext.traceId).not.toEqual('12312012123120121231201212312012');
715-
expect(propagationContext.parentSpanId).not.toEqual('1121201211212012');
713+
// Propagation context keeps the meta tag trace data for later events on the same route to add them to the trace
714+
expect(propagationContext.traceId).toEqual('12312012123120121231201212312012');
715+
expect(propagationContext.parentSpanId).toEqual('1121201211212012');
716716
});
717717

718718
it('puts frozen Dynamic Sampling Context on pageload span if sentry-trace data and only 3rd party baggage is present', () => {
@@ -747,9 +747,9 @@ describe('browserTracingIntegration', () => {
747747
expect(dynamicSamplingContext).toBeDefined();
748748
expect(dynamicSamplingContext).toStrictEqual({});
749749

750-
// Propagation context is reset and does not contain the meta tag data
751-
expect(propagationContext.traceId).not.toEqual('12312012123120121231201212312012');
752-
expect(propagationContext.parentSpanId).not.toEqual('1121201211212012');
750+
// Propagation context keeps the meta tag trace data for later events on the same route to add them to the trace
751+
expect(propagationContext.traceId).toEqual('12312012123120121231201212312012');
752+
expect(propagationContext.parentSpanId).toEqual('1121201211212012');
753753
});
754754

755755
it('ignores the meta tag data for navigation spans', () => {
@@ -843,9 +843,9 @@ describe('browserTracingIntegration', () => {
843843
expect(dynamicSamplingContext).toBeDefined();
844844
expect(dynamicSamplingContext).toStrictEqual({ release: '2.2.14' });
845845

846-
// Propagation context is reset and does not contain the meta tag data
847-
expect(propagationContext.traceId).not.toEqual('12312012123120121231201212312012');
848-
expect(propagationContext.parentSpanId).not.toEqual('1121201211212012');
846+
// Propagation context keeps the custom trace data for later events on the same route to add them to the trace
847+
expect(propagationContext.traceId).toEqual('12312012123120121231201212312011');
848+
expect(propagationContext.parentSpanId).toEqual('1121201211212011');
849849
});
850850
});
851851

0 commit comments

Comments
 (0)