Skip to content

Commit 8102ffc

Browse files
committed
fix(browser): Ensure pageload & navigation spans have correct data
1 parent efb86de commit 8102ffc

File tree

7 files changed

+76
-18
lines changed

7 files changed

+76
-18
lines changed

dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/init.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Sentry.init({
99
});
1010

1111
// Immediately navigate to a new page to abort the pageload
12-
window.location.href = '#foo';
12+
window.history.pushState({}, '', '/sub-page');

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

+15
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ sentryTest(
4040
expect(navigationTraceId).toBeDefined();
4141
expect(pageloadTraceId).not.toEqual(navigationTraceId);
4242

43+
expect(pageloadRequest.transaction).toEqual('/index.html');
44+
expect(navigationRequest.transaction).toEqual('/sub-page');
45+
4346
expect(pageloadRequest.contexts?.trace?.data).toMatchObject({
4447
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
4548
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
@@ -54,5 +57,17 @@ sentryTest(
5457
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
5558
['sentry.idle_span_finish_reason']: 'idleTimeout',
5659
});
60+
expect(pageloadRequest.request).toEqual({
61+
headers: {
62+
'User-Agent': expect.any(String),
63+
},
64+
url: 'http://sentry-test.io/index.html',
65+
});
66+
expect(navigationRequest.request).toEqual({
67+
headers: {
68+
'User-Agent': expect.any(String),
69+
},
70+
url: 'http://sentry-test.io/sub-page',
71+
});
5772
},
5873
);

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

+16
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ sentryTest('should create a navigation transaction on page navigation', async ({
3131
expect(navigationTraceId).toBeDefined();
3232
expect(pageloadTraceId).not.toEqual(navigationTraceId);
3333

34+
expect(pageloadRequest.transaction).toEqual('/index.html');
35+
// Fragment is not in transaction name
36+
expect(navigationRequest.transaction).toEqual('/index.html');
37+
3438
expect(pageloadRequest.contexts?.trace?.data).toMatchObject({
3539
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
3640
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
@@ -45,6 +49,18 @@ sentryTest('should create a navigation transaction on page navigation', async ({
4549
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
4650
['sentry.idle_span_finish_reason']: 'idleTimeout',
4751
});
52+
expect(pageloadRequest.request).toEqual({
53+
headers: {
54+
'User-Agent': expect.any(String),
55+
},
56+
url: 'http://sentry-test.io/index.html',
57+
});
58+
expect(navigationRequest.request).toEqual({
59+
headers: {
60+
'User-Agent': expect.any(String),
61+
},
62+
url: 'http://sentry-test.io/index.html#foo',
63+
});
4864

4965
const pageloadSpans = pageloadRequest.spans;
5066
const navigationSpans = navigationRequest.spans;

packages/browser/src/helpers.ts

+22
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
addExceptionTypeValue,
55
addNonEnumerableProperty,
66
captureException,
7+
getLocationHref,
78
getOriginalFunction,
89
GLOBAL_OBJ,
910
markFunctionWrapped,
@@ -175,3 +176,24 @@ export function wrap<T extends WrappableFunction, NonFunction>(
175176

176177
return sentryWrapped;
177178
}
179+
180+
/**
181+
* Get HTTP request data from the current page.
182+
*/
183+
export function getHttpRequestData(): { url: string; headers: Record<string, string> } {
184+
// grab as much info as exists and add it to the event
185+
const url = getLocationHref();
186+
const { referrer } = WINDOW.document || {};
187+
const { userAgent } = WINDOW.navigator || {};
188+
189+
const headers = {
190+
...(referrer && { Referer: referrer }),
191+
...(userAgent && { 'User-Agent': userAgent }),
192+
};
193+
const request = {
194+
url,
195+
headers,
196+
};
197+
198+
return request;
199+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { defineIntegration, getLocationHref } from '@sentry/core';
2-
import { WINDOW } from '../helpers';
1+
import { defineIntegration } from '@sentry/core';
2+
import { getHttpRequestData, WINDOW } from '../helpers';
33

44
/**
55
* Collects information about HTTP request headers and
@@ -14,23 +14,17 @@ export const httpContextIntegration = defineIntegration(() => {
1414
return;
1515
}
1616

17-
// grab as much info as exists and add it to the event
18-
const url = event.request?.url || getLocationHref();
19-
const { referrer } = WINDOW.document || {};
20-
const { userAgent } = WINDOW.navigator || {};
21-
17+
const reqData = getHttpRequestData();
2218
const headers = {
19+
...reqData.headers,
2320
...event.request?.headers,
24-
...(referrer && { Referer: referrer }),
25-
...(userAgent && { 'User-Agent': userAgent }),
2621
};
27-
const request = {
22+
23+
event.request = {
24+
...reqData,
2825
...event.request,
29-
...(url && { url }),
3026
headers,
3127
};
32-
33-
event.request = request;
3428
},
3529
};
3630
});

packages/browser/src/tracing/browserTracingIntegration.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
startTrackingWebVitals,
3434
} from '@sentry-internal/browser-utils';
3535
import { DEBUG_BUILD } from '../debug-build';
36-
import { WINDOW } from '../helpers';
36+
import { getHttpRequestData, WINDOW } from '../helpers';
3737
import { registerBackgroundTabDetection } from './backgroundtab';
3838
import { linkTraces } from './linkedTraces';
3939
import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './request';
@@ -360,6 +360,12 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
360360

361361
setActiveIdleSpan(client, idleSpan);
362362

363+
// We store the normalized request data on the scope, so we get the request data at time of span creation
364+
// otherwise, the URL etc. may already be of the following navigation, and we'd report the wrong URL
365+
getCurrentScope().setSDKProcessingMetadata({
366+
normalizedRequest: getHttpRequestData(),
367+
});
368+
363369
function emitFinish(): void {
364370
if (optionalWindowDocument && ['interactive', 'complete'].includes(optionalWindowDocument.readyState)) {
365371
client.emit('idleSpanEnableAutoFinish', idleSpan);
@@ -459,16 +465,18 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
459465
return;
460466
}
461467

462-
if (from !== to) {
463-
startingUrl = undefined;
468+
startingUrl = undefined;
469+
470+
// We wait a tick here to ensure that WINDOW.location.pathname is updated
471+
setTimeout(() => {
464472
startBrowserTracingNavigationSpan(client, {
465473
name: WINDOW.location.pathname,
466474
attributes: {
467475
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
468476
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.browser',
469477
},
470478
});
471-
}
479+
});
472480
});
473481
}
474482
}

packages/core/src/tracing/sentrySpan.ts

+3
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,8 @@ export class SentrySpan implements Span {
336336

337337
const { scope: capturedSpanScope, isolationScope: capturedSpanIsolationScope } = getCapturedScopesOnSpan(this);
338338

339+
const normalizedRequest = capturedSpanScope?.getScopeData().sdkProcessingMetadata?.normalizedRequest;
340+
339341
if (this._sampled !== true) {
340342
return undefined;
341343
}
@@ -374,6 +376,7 @@ export class SentrySpan implements Span {
374376
capturedSpanIsolationScope,
375377
dynamicSamplingContext: getDynamicSamplingContextFromSpan(this),
376378
},
379+
request: normalizedRequest,
377380
...(source && {
378381
transaction_info: {
379382
source,

0 commit comments

Comments
 (0)