Skip to content

Commit 87008e3

Browse files
committed
feat(browser): Trace continuation from server-timing headers
1 parent ba7f90a commit 87008e3

File tree

4 files changed

+82
-2
lines changed

4 files changed

+82
-2
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
// in browser TwP means not setting tracesSampleRate but adding browserTracingIntegration,
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [Sentry.browserTracingIntegration()],
9+
tracePropagationTargets: ['http://sentry-test-site.example'],
10+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<button id="errorBtn">Throw Error</button>
8+
<button id="fetchBtn">Fetch Request</button>
9+
<button id="xhrBtn">XHR Request</button>
10+
</body>
11+
</html>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../../utils/fixtures';
3+
import type { EventAndTraceHeader } from '../../../../utils/helpers';
4+
import {
5+
eventAndTraceHeaderRequestParser,
6+
getFirstSentryEnvelopeRequest,
7+
shouldSkipTracingTest,
8+
} from '../../../../utils/helpers';
9+
10+
const META_TAG_TRACE_ID = '12345678901234567890123456789012';
11+
const META_TAG_PARENT_SPAN_ID = '1234567890123456';
12+
const META_TAG_BAGGAGE =
13+
'sentry-trace_id=12345678901234567890123456789012,sentry-public_key=public,sentry-release=1.0.0,sentry-environment=prod,sentry-sample_rand=0.42';
14+
15+
sentryTest('error on initial page has traceId from server timing headers', async ({ getLocalTestUrl, page }) => {
16+
if (shouldSkipTracingTest()) {
17+
sentryTest.skip();
18+
}
19+
20+
const url = await getLocalTestUrl({
21+
testDir: __dirname,
22+
responseHeaders: {
23+
'Server-Timing': `sentry-trace;desc=${META_TAG_TRACE_ID}-${META_TAG_PARENT_SPAN_ID}, baggage;desc="${META_TAG_BAGGAGE}"`,
24+
},
25+
});
26+
await page.goto(url);
27+
28+
const errorEventPromise = getFirstSentryEnvelopeRequest<EventAndTraceHeader>(
29+
page,
30+
undefined,
31+
eventAndTraceHeaderRequestParser,
32+
);
33+
34+
await page.locator('#errorBtn').click();
35+
const [errorEvent, errorTraceHeader] = await errorEventPromise;
36+
37+
expect(errorEvent.type).toEqual(undefined);
38+
expect(errorEvent.contexts?.trace).toEqual({
39+
trace_id: META_TAG_TRACE_ID,
40+
parent_span_id: META_TAG_PARENT_SPAN_ID,
41+
span_id: expect.stringMatching(/^[\da-f]{16}$/),
42+
});
43+
44+
expect(errorTraceHeader).toEqual({
45+
environment: 'prod',
46+
public_key: 'public',
47+
release: '1.0.0',
48+
trace_id: META_TAG_TRACE_ID,
49+
sample_rand: '0.42',
50+
});
51+
});

packages/browser/src/tracing/browserTracingIntegration.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,9 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
594594
}
595595
maybeEndActiveSpan();
596596

597-
const sentryTrace = traceOptions.sentryTrace || getMetaContent('sentry-trace');
598-
const baggage = traceOptions.baggage || getMetaContent('baggage');
597+
const sentryTrace =
598+
traceOptions.sentryTrace || getMetaContent('sentry-trace') || getServerTiming('sentry-trace');
599+
const baggage = traceOptions.baggage || getMetaContent('baggage') || getServerTiming('baggage');
599600

600601
const propagationContext = propagationContextFromHeaders(sentryTrace, baggage);
601602

@@ -778,6 +779,13 @@ export function getMetaContent(metaName: string): string | undefined {
778779
return metaTag?.getAttribute('content') || undefined;
779780
}
780781

782+
/** Returns the description of a server timing entry */
783+
export function getServerTiming(name: string): string | undefined {
784+
const navigation = WINDOW.performance?.getEntriesByType?.('navigation')[0] as PerformanceNavigationTiming | undefined;
785+
const entry = navigation?.serverTiming.find(entry => entry.name === name);
786+
return entry?.description;
787+
}
788+
781789
/** Start listener for interaction transactions */
782790
function registerInteractionListener(
783791
client: Client,

0 commit comments

Comments
 (0)