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' ;
311import type { HttpError , ServerLoad } from '@sveltejs/kit' ;
12+ import * as domain from 'domain' ;
413
514function 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 {
4457export 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