Skip to content

Commit 4a4f9e9

Browse files
committed
feat(sveltekit): Add performance monitoring for server load
1 parent 4434d60 commit 4a4f9e9

File tree

1 file changed

+80
-14
lines changed
  • packages/sveltekit/src/server

1 file changed

+80
-14
lines changed

packages/sveltekit/src/server/load.ts

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
import { captureException } from '@sentry/node';
2-
import { addExceptionMechanism, isThenable, objectify } from '@sentry/utils';
1+
/* eslint-disable @sentry-internal/sdk/no-optional-chaining */
2+
import type { Span } from '@sentry/core';
3+
import { captureException, getCurrentHub } from '@sentry/node';
4+
import {
5+
addExceptionMechanism,
6+
baggageHeaderToDynamicSamplingContext,
7+
extractTraceparentData,
8+
isThenable,
9+
objectify,
10+
} from '@sentry/utils';
311
import type { HttpError, ServerLoad } from '@sveltejs/kit';
12+
import * as domain from 'domain';
413

514
function isHttpError(err: unknown): err is HttpError {
615
return typeof err === 'object' && err !== null && 'status' in err && 'body' in err;
@@ -36,6 +45,10 @@ function sendErrorToSentry(e: unknown): unknown {
3645
return objectifiedErr;
3746
}
3847

48+
function setSpan(span: Span | undefined): void {
49+
getCurrentHub().getScope()?.setSpan(span);
50+
}
51+
3952
/**
4053
* Wrap load function with Sentry
4154
*
@@ -44,21 +57,74 @@ function sendErrorToSentry(e: unknown): unknown {
4457
export function wrapLoadWithSentry(origLoad: ServerLoad): ServerLoad {
4558
return new Proxy(origLoad, {
4659
apply: (wrappingTarget, thisArg, args: Parameters<ServerLoad>) => {
47-
let maybePromiseResult;
60+
return domain.create().bind(() => {
61+
let maybePromiseResult;
62+
63+
const [event] = args;
64+
const hub = getCurrentHub();
65+
const scope = hub.getScope();
66+
67+
const parentSpan = scope?.getSpan();
68+
69+
let activeSpan: Span | undefined = undefined;
70+
71+
function finishActiveSpan(): void {
72+
activeSpan?.finish();
73+
setSpan(parentSpan);
74+
}
75+
76+
if (parentSpan) {
77+
activeSpan = parentSpan.startChild({
78+
op: 'function.sveltekit.load',
79+
description: event.route.id || 'load',
80+
status: 'ok',
81+
});
82+
} else {
83+
const sentryTraceHeader = event.request.headers.get('sentry-trace');
84+
const baggageHeader = event.request.headers.get('baggage');
85+
const traceparentData = sentryTraceHeader ? extractTraceparentData(sentryTraceHeader) : undefined;
86+
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader);
87+
88+
activeSpan = hub.startTransaction({
89+
op: 'function.sveltekit.load',
90+
name: event.route.id || 'load',
91+
status: 'ok',
92+
...traceparentData,
93+
metadata: {
94+
source: 'route',
95+
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
96+
},
97+
});
98+
}
99+
100+
setSpan(activeSpan);
48101

49-
try {
50-
maybePromiseResult = wrappingTarget.apply(thisArg, args);
51-
} catch (e) {
52-
throw sendErrorToSentry(e);
53-
}
102+
try {
103+
maybePromiseResult = wrappingTarget.apply(thisArg, args);
104+
} catch (e) {
105+
activeSpan?.setStatus('internal_error');
106+
const sentryError = sendErrorToSentry(e);
107+
finishActiveSpan();
108+
throw sentryError;
109+
}
54110

55-
if (isThenable(maybePromiseResult)) {
56-
Promise.resolve(maybePromiseResult).then(null, e => {
57-
sendErrorToSentry(e);
58-
});
59-
}
111+
if (isThenable(maybePromiseResult)) {
112+
Promise.resolve(maybePromiseResult).then(
113+
() => {
114+
finishActiveSpan();
115+
},
116+
e => {
117+
activeSpan?.setStatus('internal_error');
118+
sendErrorToSentry(e);
119+
finishActiveSpan();
120+
},
121+
);
122+
} else {
123+
finishActiveSpan();
124+
}
60125

61-
return maybePromiseResult;
126+
return maybePromiseResult;
127+
})();
62128
},
63129
});
64130
}

0 commit comments

Comments
 (0)