Skip to content

Commit 3eb46ad

Browse files
committed
feat(sveltekit): Add custom browserTracingIntegration()
1 parent 9fcbb84 commit 3eb46ad

File tree

5 files changed

+165
-4
lines changed

5 files changed

+165
-4
lines changed

dev-packages/e2e-tests/test-applications/sveltekit/vite.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { version } from '$app/environment';
12
import { sentrySvelteKit } from '@sentry/sveltekit';
23
import { sveltekit } from '@sveltejs/kit/vite';
34
import { defineConfig } from 'vite';
@@ -6,6 +7,9 @@ export default defineConfig({
67
plugins: [
78
sentrySvelteKit({
89
autoUploadSourceMaps: false,
10+
sourceMapsUploadOptions: {
11+
release: version,
12+
},
913
}),
1014
sveltekit(),
1115
],
Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,165 @@
1-
import { BrowserTracing as OriginalBrowserTracing } from '@sentry/svelte';
1+
import { navigating, page } from '$app/stores';
2+
import {
3+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
4+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
5+
getActiveSpan,
6+
startInactiveSpan,
7+
} from '@sentry/core';
8+
import {
9+
BrowserTracing as OriginalBrowserTracing,
10+
WINDOW,
11+
browserTracingIntegration as originalBrowserTracingIntegration,
12+
startBrowserTracingNavigationSpan,
13+
startBrowserTracingPageLoadSpan,
14+
} from '@sentry/svelte';
15+
import type { Client, Integration, Span } from '@sentry/types';
216
import { svelteKitRoutingInstrumentation } from './router';
317

418
/**
519
* A custom BrowserTracing integration for Sveltekit.
20+
*
21+
* @deprecated use `browserTracingIntegration()` instead.
622
*/
723
export class BrowserTracing extends OriginalBrowserTracing {
824
public constructor(options?: ConstructorParameters<typeof OriginalBrowserTracing>[0]) {
925
super({
26+
// eslint-disable-next-line deprecation/deprecation
1027
routingInstrumentation: svelteKitRoutingInstrumentation,
1128
...options,
1229
});
1330
}
1431
}
32+
33+
/**
34+
* A custom `BrowserTracing` integration for SvelteKit.
35+
*
36+
* @param options
37+
*/
38+
export function browserTracingIntegration(
39+
options: Parameters<typeof originalBrowserTracingIntegration>[0] = {},
40+
): Integration {
41+
const integration = {
42+
...originalBrowserTracingIntegration({
43+
...options,
44+
instrumentNavigation: false,
45+
instrumentPageLoad: false,
46+
}),
47+
};
48+
49+
return {
50+
...integration,
51+
afterAllSetup: client => {
52+
integration.afterAllSetup(client);
53+
54+
if (options.instrumentPageLoad !== false) {
55+
_instrumentPageload(client);
56+
}
57+
58+
if (options.instrumentNavigation !== false) {
59+
_instrumentNavigations(client);
60+
}
61+
},
62+
};
63+
}
64+
65+
function _instrumentPageload(client: Client): void {
66+
const initialPath = WINDOW && WINDOW.location && WINDOW.location.pathname;
67+
68+
startBrowserTracingPageLoadSpan(client, {
69+
name: initialPath,
70+
op: 'pageload',
71+
origin: 'auto.pageload.sveltekit',
72+
description: initialPath,
73+
tags: {
74+
'routing.instrumentation': '@sentry/sveltekit',
75+
},
76+
attributes: {
77+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
78+
},
79+
});
80+
81+
const pageloadSpan = getActiveSpan();
82+
83+
page.subscribe(page => {
84+
if (!page) {
85+
return;
86+
}
87+
88+
const routeId = page.route && page.route.id;
89+
90+
if (pageloadSpan && routeId) {
91+
pageloadSpan.updateName(routeId);
92+
pageloadSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
93+
}
94+
});
95+
}
96+
97+
/**
98+
* Use the `navigating` store to start a transaction on navigations.
99+
*/
100+
function _instrumentNavigations(client: Client): void {
101+
let routingSpan: Span | undefined = undefined;
102+
let activeSpan: Span | undefined;
103+
104+
navigating.subscribe(navigation => {
105+
if (!navigation) {
106+
// `navigating` emits a 'null' value when the navigation is completed.
107+
// So in this case, we can finish the routing span. If the transaction was an IdleTransaction,
108+
// it will finish automatically and if it was user-created users also need to finish it.
109+
if (routingSpan) {
110+
routingSpan.end();
111+
routingSpan = undefined;
112+
}
113+
return;
114+
}
115+
116+
const from = navigation.from;
117+
const to = navigation.to;
118+
119+
// for the origin we can fall back to window.location.pathname because in this emission, it still is set to the origin path
120+
const rawRouteOrigin = (from && from.url.pathname) || (WINDOW && WINDOW.location && WINDOW.location.pathname);
121+
122+
const rawRouteDestination = to && to.url.pathname;
123+
124+
// We don't want to create transactions for navigations of same origin and destination.
125+
// We need to look at the raw URL here because parameterized routes can still differ in their raw parameters.
126+
if (rawRouteOrigin === rawRouteDestination) {
127+
return;
128+
}
129+
130+
const parameterizedRouteOrigin = from && from.route.id;
131+
const parameterizedRouteDestination = to && to.route.id;
132+
133+
activeSpan = getActiveSpan();
134+
135+
if (!activeSpan) {
136+
startBrowserTracingNavigationSpan(client, {
137+
name: parameterizedRouteDestination || rawRouteDestination || 'unknown',
138+
op: 'navigation',
139+
origin: 'auto.navigation.sveltekit',
140+
attributes: {
141+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: parameterizedRouteDestination ? 'route' : 'url',
142+
},
143+
tags: {
144+
'routing.instrumentation': '@sentry/sveltekit',
145+
},
146+
});
147+
activeSpan = getActiveSpan();
148+
}
149+
150+
if (activeSpan) {
151+
if (routingSpan) {
152+
// If a routing span is still open from a previous navigation, we finish it.
153+
routingSpan.end();
154+
}
155+
routingSpan = startInactiveSpan({
156+
op: 'ui.sveltekit.routing',
157+
name: 'SvelteKit Route Change',
158+
attributes: {
159+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.sveltekit',
160+
},
161+
});
162+
activeSpan.setAttribute('sentry.sveltekit.navigation.from', parameterizedRouteOrigin || undefined);
163+
}
164+
});
165+
}

packages/sveltekit/src/client/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from '@sentry/svelte';
33
export { init } from './sdk';
44
export { handleErrorWithSentry } from './handleError';
55
export { wrapLoadWithSentry } from './load';
6+
export { browserTracingIntegration } from './browserTracingIntegration';

packages/sveltekit/src/client/router.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const DEFAULT_TAGS = {
1717
* @param startTransactionFn the function used to start (idle) transactions
1818
* @param startTransactionOnPageLoad controls if pageload transactions should be created (defaults to `true`)
1919
* @param startTransactionOnLocationChange controls if navigation transactions should be created (defauls to `true`)
20+
*
21+
* @deprecated use `browserTracingIntegration()` instead which includes SvelteKit-specific routing instrumentation out of the box.
2022
*/
2123
export function svelteKitRoutingInstrumentation<T extends Transaction>(
2224
startTransactionFn: (context: TransactionContext) => T | undefined,

packages/sveltekit/src/client/sdk.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { applySdkMetadata, hasTracingEnabled } from '@sentry/core';
2-
import type { BrowserOptions, browserTracingIntegration } from '@sentry/svelte';
2+
import { BrowserOptions } from '@sentry/svelte';
33
import { getDefaultIntegrations as getDefaultSvelteIntegrations } from '@sentry/svelte';
44
import { WINDOW, getCurrentScope, init as initSvelteSdk } from '@sentry/svelte';
55
import type { Integration } from '@sentry/types';
66

7-
import { BrowserTracing } from './browserTracingIntegration';
7+
import {
8+
BrowserTracing,
9+
browserTracingIntegration as svelteKitBrowserTracingIntegration,
10+
} from './browserTracingIntegration';
811

912
type WindowWithSentryFetchProxy = typeof WINDOW & {
1013
_sentryFetchProxy?: typeof fetch;
@@ -97,7 +100,7 @@ function getDefaultIntegrations(options: BrowserOptions): Integration[] | undefi
97100
// will get treeshaken away
98101
if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) {
99102
if (hasTracingEnabled(options)) {
100-
return [...getDefaultSvelteIntegrations(options), new BrowserTracing()];
103+
return [...getDefaultSvelteIntegrations(options), svelteKitBrowserTracingIntegration()];
101104
}
102105
}
103106

0 commit comments

Comments
 (0)