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' ;
3
11
import type { HttpError , ServerLoad } from '@sveltejs/kit' ;
12
+ import * as domain from 'domain' ;
4
13
5
14
function isHttpError ( err : unknown ) : err is HttpError {
6
15
return typeof err === 'object' && err !== null && 'status' in err && 'body' in err ;
@@ -36,6 +45,10 @@ function sendErrorToSentry(e: unknown): unknown {
36
45
return objectifiedErr ;
37
46
}
38
47
48
+ function setSpan ( span : Span | undefined ) : void {
49
+ getCurrentHub ( ) . getScope ( ) ?. setSpan ( span ) ;
50
+ }
51
+
39
52
/**
40
53
* Wrap load function with Sentry
41
54
*
@@ -44,21 +57,74 @@ function sendErrorToSentry(e: unknown): unknown {
44
57
export function wrapLoadWithSentry ( origLoad : ServerLoad ) : ServerLoad {
45
58
return new Proxy ( origLoad , {
46
59
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 ) ;
48
101
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
+ }
54
110
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
+ }
60
125
61
- return maybePromiseResult ;
126
+ return maybePromiseResult ;
127
+ } ) ( ) ;
62
128
} ,
63
129
} ) ;
64
130
}
0 commit comments