Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4b6cb4a

Browse files
authored
feat(browser): Send standalone fetch and XHR spans if there's no active parent span (#11783)
Enable sending standalone `http.client` spans for outgoing `fetch` and XHR requests if there's no active parent span. These spans will belong to the same trace id as a potentially previously started pageload or navigation span. Adjusted integration tests to test the newly sent spans and their trace lifetime.
1 parent 45a05c5 commit 4b6cb4a

File tree

16 files changed

+395
-204
lines changed

16 files changed

+395
-204
lines changed
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import * as Sentry from '@sentry/browser';
22

33
window.Sentry = Sentry;
44

5-
window._sentryTransactionsCount = 0;
6-
75
Sentry.init({
86
dsn: 'https://public@dsn.ingest.sentry.io/1337',
97
// disable auto span creation
@@ -16,8 +14,4 @@ Sentry.init({
1614
tracePropagationTargets: ['http://example.com'],
1715
tracesSampleRate: 1,
1816
autoSessionTracking: false,
19-
beforeSendTransaction() {
20-
window._sentryTransactionsCount++;
21-
return null;
22-
},
2317
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fetch('http://example.com/0');
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type { SpanEnvelope } from '@sentry/types';
2+
import { sentryTest } from '../../../../utils/fixtures';
3+
import {
4+
getFirstSentryEnvelopeRequest,
5+
properFullEnvelopeRequestParser,
6+
shouldSkipTracingTest,
7+
} from '../../../../utils/helpers';
8+
9+
import { expect } from '@playwright/test';
10+
11+
sentryTest(
12+
"should create standalone span for fetch requests if there's no active span and should attach tracing headers",
13+
async ({ getLocalTestUrl, page }) => {
14+
if (shouldSkipTracingTest()) {
15+
sentryTest.skip();
16+
}
17+
18+
let sentryTraceHeader = '';
19+
let baggageHeader = '';
20+
21+
await page.route('http://example.com/**', route => {
22+
sentryTraceHeader = route.request().headers()['sentry-trace'];
23+
baggageHeader = route.request().headers()['baggage'];
24+
return route.fulfill({
25+
status: 200,
26+
contentType: 'application/json',
27+
body: JSON.stringify({}),
28+
});
29+
});
30+
31+
const url = await getLocalTestUrl({ testDir: __dirname });
32+
33+
const spanEnvelopePromise = getFirstSentryEnvelopeRequest<SpanEnvelope>(
34+
page,
35+
undefined,
36+
properFullEnvelopeRequestParser,
37+
);
38+
39+
await page.goto(url);
40+
41+
const spanEnvelope = await spanEnvelopePromise;
42+
43+
const spanEnvelopeHeaders = spanEnvelope[0];
44+
const spanEnvelopeItem = spanEnvelope[1][0][1];
45+
46+
const traceId = spanEnvelopeHeaders.trace!.trace_id;
47+
const spanId = spanEnvelopeItem.span_id;
48+
49+
expect(traceId).toMatch(/[a-f0-9]{32}/);
50+
expect(spanId).toMatch(/[a-f0-9]{16}/);
51+
52+
expect(spanEnvelopeHeaders).toEqual({
53+
sent_at: expect.any(String),
54+
trace: {
55+
environment: 'production',
56+
public_key: 'public',
57+
sample_rate: '1',
58+
sampled: 'true',
59+
trace_id: traceId,
60+
transaction: 'GET http://example.com/0',
61+
},
62+
});
63+
64+
expect(spanEnvelopeItem).toEqual({
65+
data: expect.objectContaining({
66+
'http.method': 'GET',
67+
'http.response.status_code': 200,
68+
'http.response_content_length': expect.any(Number),
69+
'http.url': 'http://example.com/0',
70+
'sentry.op': 'http.client',
71+
'sentry.origin': 'auto.http.browser',
72+
'sentry.sample_rate': 1,
73+
'sentry.source': 'custom',
74+
'server.address': 'example.com',
75+
type: 'fetch',
76+
url: 'http://example.com/0',
77+
}),
78+
description: 'GET http://example.com/0',
79+
op: 'http.client',
80+
origin: 'auto.http.browser',
81+
status: 'ok',
82+
trace_id: traceId,
83+
span_id: spanId,
84+
segment_id: spanId,
85+
is_segment: true,
86+
start_timestamp: expect.any(Number),
87+
timestamp: expect.any(Number),
88+
});
89+
90+
// the standalone span was sampled, so we propagate the positive sampling decision
91+
expect(sentryTraceHeader).toBe(`${traceId}-${spanId}-1`);
92+
expect(baggageHeader).toBe(
93+
`sentry-environment=production,sentry-public_key=public,sentry-trace_id=${traceId},sentry-sample_rate=1,sentry-transaction=GET%20http%3A%2F%2Fexample.com%2F0,sentry-sampled=true`,
94+
);
95+
},
96+
);

‎dev-packages/browser-integration-tests/suites/tracing/request/fetch-with-no-active-span/subject.js‎

Lines changed: 0 additions & 1 deletion
This file was deleted.

‎dev-packages/browser-integration-tests/suites/tracing/request/fetch-with-no-active-span/test.ts‎

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import * as Sentry from '@sentry/browser';
22

33
window.Sentry = Sentry;
44

5-
window._sentryTransactionsCount = 0;
6-
75
Sentry.init({
86
dsn: 'https://public@dsn.ingest.sentry.io/1337',
97
// disable auto span creation
@@ -16,8 +14,4 @@ Sentry.init({
1614
tracePropagationTargets: ['http://example.com'],
1715
tracesSampleRate: 1,
1816
autoSessionTracking: false,
19-
beforeSendTransaction() {
20-
window._sentryTransactionsCount++;
21-
return null;
22-
},
2317
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const xhr_1 = new XMLHttpRequest();
2+
xhr_1.open('GET', 'http://example.com/0');
3+
xhr_1.send();
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { expect } from '@playwright/test';
2+
3+
import type { SpanEnvelope } from '@sentry/types';
4+
import { sentryTest } from '../../../../utils/fixtures';
5+
import {
6+
getFirstSentryEnvelopeRequest,
7+
properFullEnvelopeRequestParser,
8+
shouldSkipTracingTest,
9+
} from '../../../../utils/helpers';
10+
11+
sentryTest(
12+
"should create standalone span for XHR requests if there's no active span and should attach tracing headers",
13+
async ({ getLocalTestUrl, page }) => {
14+
if (shouldSkipTracingTest()) {
15+
sentryTest.skip();
16+
}
17+
18+
let sentryTraceHeader = '';
19+
let baggageHeader = '';
20+
21+
await page.route('http://example.com/**', route => {
22+
sentryTraceHeader = route.request().headers()['sentry-trace'];
23+
baggageHeader = route.request().headers()['baggage'];
24+
return route.fulfill({
25+
status: 200,
26+
contentType: 'application/json',
27+
body: JSON.stringify({}),
28+
});
29+
});
30+
31+
const url = await getLocalTestUrl({ testDir: __dirname });
32+
33+
const spanEnvelopePromise = getFirstSentryEnvelopeRequest<SpanEnvelope>(
34+
page,
35+
undefined,
36+
properFullEnvelopeRequestParser,
37+
);
38+
39+
await page.goto(url);
40+
41+
const spanEnvelope = await spanEnvelopePromise;
42+
43+
const spanEnvelopeHeaders = spanEnvelope[0];
44+
const spanEnvelopeItem = spanEnvelope[1][0][1];
45+
46+
const traceId = spanEnvelopeHeaders.trace!.trace_id;
47+
const spanId = spanEnvelopeItem.span_id;
48+
49+
expect(traceId).toMatch(/[a-f0-9]{32}/);
50+
expect(spanId).toMatch(/[a-f0-9]{16}/);
51+
52+
expect(spanEnvelopeHeaders).toEqual({
53+
sent_at: expect.any(String),
54+
trace: {
55+
environment: 'production',
56+
public_key: 'public',
57+
sample_rate: '1',
58+
sampled: 'true',
59+
trace_id: traceId,
60+
transaction: 'GET http://example.com/0',
61+
},
62+
});
63+
64+
expect(spanEnvelopeItem).toEqual({
65+
data: {
66+
'http.method': 'GET',
67+
'http.response.status_code': 200,
68+
'http.url': 'http://example.com/0',
69+
'sentry.op': 'http.client',
70+
'sentry.origin': 'auto.http.browser',
71+
'sentry.sample_rate': 1,
72+
'sentry.source': 'custom',
73+
'server.address': 'example.com',
74+
type: 'xhr',
75+
url: 'http://example.com/0',
76+
},
77+
description: 'GET http://example.com/0',
78+
op: 'http.client',
79+
origin: 'auto.http.browser',
80+
status: 'ok',
81+
trace_id: traceId,
82+
span_id: spanId,
83+
segment_id: spanId,
84+
is_segment: true,
85+
start_timestamp: expect.any(Number),
86+
timestamp: expect.any(Number),
87+
});
88+
89+
// the standalone span was sampled, so we propagate the positive sampling decision
90+
expect(sentryTraceHeader).toBe(`${traceId}-${spanId}-1`);
91+
expect(baggageHeader).toBe(
92+
`sentry-environment=production,sentry-public_key=public,sentry-trace_id=${traceId},sentry-sample_rate=1,sentry-transaction=GET%20http%3A%2F%2Fexample.com%2F0,sentry-sampled=true`,
93+
);
94+
},
95+
);

‎dev-packages/browser-integration-tests/suites/tracing/request/xhr-with-no-active-span/subject.js‎

Lines changed: 0 additions & 11 deletions
This file was deleted.

‎dev-packages/browser-integration-tests/suites/tracing/request/xhr-with-no-active-span/test.ts‎

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /