Skip to content

Commit 24c4eba

Browse files
authored
ref(sveltekit): Update trace propagation & span options (#10838)
This WIP updates sveltekit to avoid deprecated options to `startSpan`, which also includes changing how we propagate traces to use `continueTrace`. I fixed the tests to ensure this still works, but would appreciate a review from @Lms24 if that all seems correct 😅
1 parent a08d370 commit 24c4eba

File tree

5 files changed

+250
-197
lines changed

5 files changed

+250
-197
lines changed

packages/sveltekit/src/server/handle.ts

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import {
22
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
33
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
4+
continueTrace,
45
getActiveSpan,
5-
getCurrentScope,
66
getDynamicSamplingContextFromSpan,
7+
getRootSpan,
78
setHttpStatus,
89
spanToTraceHeader,
910
withIsolationScope,
1011
} from '@sentry/core';
11-
import { getActiveTransaction, startSpan } from '@sentry/core';
12+
import { startSpan } from '@sentry/core';
1213
import { captureException } from '@sentry/node-experimental';
13-
/* eslint-disable @sentry-internal/sdk/no-optional-chaining */
14+
import type { Span } from '@sentry/types';
1415
import { dynamicSamplingContextToSentryBaggageHeader, objectify } from '@sentry/utils';
1516
import type { Handle, ResolveOptions } from '@sveltejs/kit';
1617

@@ -103,12 +104,12 @@ export function addSentryCodeToPage(options: SentryHandleOptions): NonNullable<R
103104
const nonce = fetchProxyScriptNonce ? `nonce="${fetchProxyScriptNonce}"` : '';
104105

105106
return ({ html }) => {
106-
// eslint-disable-next-line deprecation/deprecation
107-
const transaction = getActiveTransaction();
108-
if (transaction) {
109-
const traceparentData = spanToTraceHeader(transaction);
107+
const activeSpan = getActiveSpan();
108+
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
109+
if (rootSpan) {
110+
const traceparentData = spanToTraceHeader(rootSpan);
110111
const dynamicSamplingContext = dynamicSamplingContextToSentryBaggageHeader(
111-
getDynamicSamplingContextFromSpan(transaction),
112+
getDynamicSamplingContextFromSpan(rootSpan),
112113
);
113114
const contentMeta = `<head>
114115
<meta name="sentry-trace" content="${traceparentData}"/>
@@ -170,36 +171,35 @@ async function instrumentHandle(
170171
return resolve(event);
171172
}
172173

173-
const { dynamicSamplingContext, traceparentData, propagationContext } = getTracePropagationData(event);
174-
getCurrentScope().setPropagationContext(propagationContext);
175-
176-
try {
177-
const resolveResult = await startSpan(
178-
{
179-
op: 'http.server',
180-
attributes: {
181-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.sveltekit',
182-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: event.route?.id ? 'route' : 'url',
174+
const { sentryTrace, baggage } = getTracePropagationData(event);
175+
176+
return continueTrace({ sentryTrace, baggage }, async () => {
177+
try {
178+
const resolveResult = await startSpan(
179+
{
180+
op: 'http.server',
181+
attributes: {
182+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.sveltekit',
183+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: event.route?.id ? 'route' : 'url',
184+
},
185+
name: `${event.request.method} ${event.route?.id || event.url.pathname}`,
183186
},
184-
name: `${event.request.method} ${event.route?.id || event.url.pathname}`,
185-
...traceparentData,
186-
metadata: {
187-
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
187+
async (span?: Span) => {
188+
const res = await resolve(event, {
189+
transformPageChunk: addSentryCodeToPage(options),
190+
});
191+
if (span) {
192+
setHttpStatus(span, res.status);
193+
}
194+
return res;
188195
},
189-
},
190-
async span => {
191-
const res = await resolve(event, {
192-
transformPageChunk: addSentryCodeToPage(options),
193-
});
194-
setHttpStatus(span, res.status);
195-
return res;
196-
},
197-
);
198-
return resolveResult;
199-
} catch (e: unknown) {
200-
sendErrorToSentry(e);
201-
throw e;
202-
} finally {
203-
await flushIfServerless();
204-
}
196+
);
197+
return resolveResult;
198+
} catch (e: unknown) {
199+
sendErrorToSentry(e);
200+
throw e;
201+
} finally {
202+
await flushIfServerless();
203+
}
204+
});
205205
}

packages/sveltekit/src/server/load.ts

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
/* eslint-disable @sentry-internal/sdk/no-optional-chaining */
21
import {
32
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
43
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
5-
getCurrentScope,
4+
continueTrace,
65
startSpan,
76
} from '@sentry/core';
87
import { captureException } from '@sentry/node-experimental';
98
import { addNonEnumerableProperty, objectify } from '@sentry/utils';
109
import type { LoadEvent, ServerLoadEvent } from '@sveltejs/kit';
1110

12-
import type { TransactionContext } from '@sentry/types';
1311
import type { SentryWrappedFlag } from '../common/utils';
1412
import { isHttpError, isRedirect } from '../common/utils';
1513
import { flushIfServerless, getTracePropagationData } from './utils';
@@ -70,18 +68,19 @@ export function wrapLoadWithSentry<T extends (...args: any) => any>(origLoad: T)
7068

7169
const routeId = event.route && event.route.id;
7270

73-
const traceLoadContext: TransactionContext = {
74-
op: 'function.sveltekit.load',
75-
attributes: {
76-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit',
77-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeId ? 'route' : 'url',
78-
},
79-
name: routeId ? routeId : event.url.pathname,
80-
};
81-
8271
try {
8372
// We need to await before returning, otherwise we won't catch any errors thrown by the load function
84-
return await startSpan(traceLoadContext, () => wrappingTarget.apply(thisArg, args));
73+
return await startSpan(
74+
{
75+
op: 'function.sveltekit.load',
76+
attributes: {
77+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit',
78+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeId ? 'route' : 'url',
79+
},
80+
name: routeId ? routeId : event.url.pathname,
81+
},
82+
() => wrappingTarget.apply(thisArg, args),
83+
);
8584
} catch (e) {
8685
sendErrorToSentry(e);
8786
throw e;
@@ -133,34 +132,30 @@ export function wrapServerLoadWithSentry<T extends (...args: any) => any>(origSe
133132
// https://github.com/sveltejs/kit/blob/e133aba479fa9ba0e7f9e71512f5f937f0247e2c/packages/kit/src/runtime/server/page/load_data.js#L111C3-L124
134133
const routeId = event.route && (Object.getOwnPropertyDescriptor(event.route, 'id')?.value as string | undefined);
135134

136-
const { dynamicSamplingContext, traceparentData, propagationContext } = getTracePropagationData(event);
137-
getCurrentScope().setPropagationContext(propagationContext);
138-
139-
const traceLoadContext: TransactionContext = {
140-
op: 'function.sveltekit.server.load',
141-
attributes: {
142-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit',
143-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeId ? 'route' : 'url',
144-
},
145-
name: routeId ? routeId : event.url.pathname,
146-
metadata: {
147-
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
148-
},
149-
data: {
150-
'http.method': event.request.method,
151-
},
152-
...traceparentData,
153-
};
154-
155-
try {
156-
// We need to await before returning, otherwise we won't catch any errors thrown by the load function
157-
return await startSpan(traceLoadContext, () => wrappingTarget.apply(thisArg, args));
158-
} catch (e: unknown) {
159-
sendErrorToSentry(e);
160-
throw e;
161-
} finally {
162-
await flushIfServerless();
163-
}
135+
const { sentryTrace, baggage } = getTracePropagationData(event);
136+
137+
return continueTrace({ sentryTrace, baggage }, async () => {
138+
try {
139+
// We need to await before returning, otherwise we won't catch any errors thrown by the load function
140+
return await startSpan(
141+
{
142+
op: 'function.sveltekit.server.load',
143+
attributes: {
144+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.sveltekit',
145+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: routeId ? 'route' : 'url',
146+
'http.method': event.request.method,
147+
},
148+
name: routeId ? routeId : event.url.pathname,
149+
},
150+
() => wrappingTarget.apply(thisArg, args),
151+
);
152+
} catch (e: unknown) {
153+
sendErrorToSentry(e);
154+
throw e;
155+
} finally {
156+
await flushIfServerless();
157+
}
158+
});
164159
},
165160
});
166161
}

packages/sveltekit/src/server/utils.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { flush } from '@sentry/node-experimental';
2-
import { logger, tracingContextFromHeaders } from '@sentry/utils';
2+
import { logger } from '@sentry/utils';
33
import type { RequestEvent } from '@sveltejs/kit';
44

55
import { DEBUG_BUILD } from '../common/debug-build';
@@ -10,12 +10,11 @@ import { DEBUG_BUILD } from '../common/debug-build';
1010
*
1111
* Sets propagation context as a side effect.
1212
*/
13-
// eslint-disable-next-line deprecation/deprecation
14-
export function getTracePropagationData(event: RequestEvent): ReturnType<typeof tracingContextFromHeaders> {
15-
const sentryTraceHeader = event.request.headers.get('sentry-trace') || '';
16-
const baggageHeader = event.request.headers.get('baggage');
17-
// eslint-disable-next-line deprecation/deprecation
18-
return tracingContextFromHeaders(sentryTraceHeader, baggageHeader);
13+
export function getTracePropagationData(event: RequestEvent): { sentryTrace: string; baggage: string | null } {
14+
const sentryTrace = event.request.headers.get('sentry-trace') || '';
15+
const baggage = event.request.headers.get('baggage');
16+
17+
return { sentryTrace, baggage };
1918
}
2019

2120
/** Flush the event queue to ensure that events get sent to Sentry before the response is finished and the lambda ends */

0 commit comments

Comments
 (0)