Skip to content

Commit f503cbe

Browse files
committed
fix(browser): Propagate trace data from active span if http.client span is not recording
1 parent dc87779 commit f503cbe

16 files changed

Lines changed: 292 additions & 4 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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(), Sentry.spanStreamingIntegration()],
8+
ignoreSpans: [/ignored-click-listener/],
9+
parentSpanIsAlwaysRootSpan: false,
10+
tracePropagationTargets: ['sentry-test-external.io'],
11+
tracesSampleRate: 0,
12+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const fetchButton = document.getElementById('fetch');
2+
3+
fetchButton.addEventListener('click', async () => {
4+
await Sentry.startSpan({ name: 'ignored-click-listener', op: 'ui.interaction.click' }, async () => {
5+
await fetch('http://sentry-test-external.io');
6+
});
7+
});
8+
9+
fetchButton.click();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="sentry-trace" content="12345678901234567890123456789012-1234567890123456-1" />
6+
<meta
7+
name="baggage"
8+
content="sentry-trace_id=12345678901234567890123456789012,sentry-sample_rate=1,sentry-sampled=true,sentry-public_key=public,sentry-sample_rand=0.5"
9+
/>
10+
</head>
11+
<body>
12+
<button id="fetch">Fetch</button>
13+
</body>
14+
</html>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { expect } from '@playwright/test';
2+
import type { ClientReport } from '@sentry/core';
3+
import { sentryTest } from '../../../../utils/fixtures';
4+
import {
5+
envelopeRequestParser,
6+
hidePage,
7+
shouldSkipTracingTest,
8+
waitForClientReportRequest,
9+
waitForTracingHeadersOnUrl,
10+
} from '../../../../utils/helpers';
11+
import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils';
12+
13+
sentryTest(
14+
'ignoring a child span preserves the positive sampling decision of a continued trace when propagating it',
15+
async ({ getLocalTestUrl, page }) => {
16+
sentryTest.skip(shouldSkipTracingTest());
17+
18+
const url = await getLocalTestUrl({ testDir: __dirname });
19+
20+
const clientReportPromise = waitForClientReportRequest(page);
21+
const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io');
22+
const spansPromise = waitForStreamedSpans(
23+
page,
24+
spans =>
25+
spans.some(span => getSpanOp(span) === 'pageload') && spans.some(span => getSpanOp(span) === 'http.client'),
26+
);
27+
28+
await page.goto(url);
29+
30+
const [{ baggage, sentryTrace }, spans] = await Promise.all([tracingHeadersPromise, spansPromise]);
31+
const pageloadSpan = spans.find(span => getSpanOp(span) === 'pageload');
32+
const httpClientSpan = spans.find(span => getSpanOp(span) === 'http.client');
33+
34+
expect(pageloadSpan?.is_segment).toBe(true);
35+
expect(pageloadSpan?.trace_id).toBe('12345678901234567890123456789012');
36+
expect(httpClientSpan?.parent_span_id).toBe(pageloadSpan?.span_id);
37+
expect(sentryTrace).toBe(`${pageloadSpan?.trace_id}-${httpClientSpan?.span_id}-1`);
38+
expect(baggage).toEqual(
39+
'sentry-trace_id=12345678901234567890123456789012,sentry-sample_rate=1,sentry-sampled=true,sentry-public_key=public,sentry-sample_rand=0.5',
40+
);
41+
42+
await hidePage(page);
43+
const clientReport = envelopeRequestParser<ClientReport>(await clientReportPromise);
44+
expect(clientReport.discarded_events).toEqual([{ category: 'span', quantity: 1, reason: 'ignored' }]);
45+
},
46+
);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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(), Sentry.spanStreamingIntegration()],
8+
ignoreSpans: [{ op: 'http.client' }],
9+
tracePropagationTargets: ['sentry-test-external.io'],
10+
tracesSampleRate: 0,
11+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
setTimeout(() => fetch('http://sentry-test-external.io'));
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="sentry-trace" content="12345678901234567890123456789012-1234567890123456-1" />
6+
<meta
7+
name="baggage"
8+
content="sentry-trace_id=12345678901234567890123456789012,sentry-sample_rate=1,sentry-sampled=true,sentry-public_key=public,sentry-sample_rand=0.5"
9+
/>
10+
</head>
11+
<body></body>
12+
</html>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { expect } from '@playwright/test';
2+
import type { ClientReport } from '@sentry/core';
3+
import { sentryTest } from '../../../../utils/fixtures';
4+
import {
5+
envelopeRequestParser,
6+
hidePage,
7+
shouldSkipTracingTest,
8+
waitForClientReportRequest,
9+
waitForTracingHeadersOnUrl,
10+
} from '../../../../utils/helpers';
11+
import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils';
12+
13+
sentryTest(
14+
'ignoring an outgoing HTTP span preserves the positive sampling decision of a continued trace',
15+
async ({ getLocalTestUrl, page }) => {
16+
sentryTest.skip(shouldSkipTracingTest());
17+
18+
const url = await getLocalTestUrl({ testDir: __dirname });
19+
const clientReportPromise = waitForClientReportRequest(page);
20+
const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io');
21+
const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload');
22+
23+
await page.goto(url);
24+
25+
const [{ baggage, sentryTrace }, pageloadSpan] = await Promise.all([tracingHeadersPromise, pageloadSpanPromise]);
26+
expect(pageloadSpan.is_segment).toBe(true);
27+
expect(pageloadSpan.trace_id).toBe('12345678901234567890123456789012');
28+
expect(sentryTrace).toBe(`12345678901234567890123456789012-${pageloadSpan.span_id}-1`);
29+
expect(baggage).toEqual(
30+
'sentry-trace_id=12345678901234567890123456789012,sentry-sample_rate=1,sentry-sampled=true,sentry-public_key=public,sentry-sample_rand=0.5',
31+
);
32+
33+
await hidePage(page);
34+
const clientReport = envelopeRequestParser<ClientReport>(await clientReportPromise);
35+
expect(clientReport.discarded_events).toEqual([{ category: 'span', quantity: 1, reason: 'ignored' }]);
36+
},
37+
);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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(), Sentry.spanStreamingIntegration()],
8+
ignoreSpans: [{ op: 'pageload' }],
9+
tracePropagationTargets: ['sentry-test-external.io'],
10+
tracesSampleRate: 0,
11+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
document.getElementById('fetch').addEventListener('click', () => {
2+
fetch('http://sentry-test-external.io');
3+
});

0 commit comments

Comments
 (0)