1
1
import { captureException , flush , getCurrentHub , Handlers , startTransaction , withScope } from '@sentry/node' ;
2
2
import { extractTraceparentData , getActiveTransaction , hasTracingEnabled } from '@sentry/tracing' ;
3
3
import { addExceptionMechanism , isString , logger , stripUrlQueryAndFragment } from '@sentry/utils' ;
4
+ import * as domain from 'domain' ;
4
5
import { NextApiHandler } from 'next' ;
5
6
6
7
import { addRequestDataToEvent , NextRequest } from './instrumentServer' ;
@@ -14,70 +15,77 @@ type WrappedNextApiHandler = NextApiHandler;
14
15
export const withSentry = ( handler : NextApiHandler ) : WrappedNextApiHandler => {
15
16
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
16
17
return async ( req , res ) => {
17
- try {
18
- const currentScope = getCurrentHub ( ) . getScope ( ) ;
18
+ // wrap everything in a domain in order to prevent scope bleed between requests
19
+ const local = domain . create ( ) ;
20
+ local . add ( req ) ;
21
+ local . add ( res ) ;
19
22
20
- if ( currentScope ) {
21
- currentScope . addEventProcessor ( event => addRequestDataToEvent ( event , req as NextRequest ) ) ;
23
+ local . run ( async ( ) => {
24
+ try {
25
+ const currentScope = getCurrentHub ( ) . getScope ( ) ;
22
26
23
- if ( hasTracingEnabled ( ) ) {
24
- // If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
25
- let traceparentData ;
26
- if ( req . headers && isString ( req . headers [ 'sentry-trace' ] ) ) {
27
- traceparentData = extractTraceparentData ( req . headers [ 'sentry-trace' ] as string ) ;
28
- logger . log ( `[Tracing] Continuing trace ${ traceparentData ?. traceId } .` ) ;
29
- }
27
+ if ( currentScope ) {
28
+ currentScope . addEventProcessor ( event => addRequestDataToEvent ( event , req as NextRequest ) ) ;
30
29
31
- const url = `${ req . url } ` ;
32
- // pull off query string, if any
33
- let reqPath = stripUrlQueryAndFragment ( url ) ;
34
- // Replace with placeholder
35
- if ( req . query ) {
36
- // TODO get this from next if possible, to avoid accidentally replacing non-dynamic parts of the path if
37
- // they match dynamic parts
38
- for ( const [ key , value ] of Object . entries ( req . query ) ) {
39
- reqPath = reqPath . replace ( `${ value } ` , `[${ key } ]` ) ;
30
+ if ( hasTracingEnabled ( ) ) {
31
+ // If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
32
+ let traceparentData ;
33
+ if ( req . headers && isString ( req . headers [ 'sentry-trace' ] ) ) {
34
+ traceparentData = extractTraceparentData ( req . headers [ 'sentry-trace' ] as string ) ;
35
+ logger . log ( `[Tracing] Continuing trace ${ traceparentData ?. traceId } .` ) ;
40
36
}
41
- }
42
- const reqMethod = `${ ( req . method || 'GET' ) . toUpperCase ( ) } ` ;
43
37
44
- const transaction = startTransaction (
45
- {
46
- name : `${ reqMethod } ${ reqPath } ` ,
47
- op : 'http.server' ,
48
- ...traceparentData ,
49
- } ,
50
- // extra context passed to the `tracesSampler`
51
- { request : req } ,
52
- ) ;
53
- currentScope . setSpan ( transaction ) ;
38
+ const url = `${ req . url } ` ;
39
+ // pull off query string, if any
40
+ let reqPath = stripUrlQueryAndFragment ( url ) ;
41
+ // Replace with placeholder
42
+ if ( req . query ) {
43
+ // TODO get this from next if possible, to avoid accidentally replacing non-dynamic parts of the path if
44
+ // they match dynamic parts
45
+ for ( const [ key , value ] of Object . entries ( req . query ) ) {
46
+ reqPath = reqPath . replace ( `${ value } ` , `[${ key } ]` ) ;
47
+ }
48
+ }
49
+ const reqMethod = `${ ( req . method || 'GET' ) . toUpperCase ( ) } ` ;
50
+
51
+ const transaction = startTransaction (
52
+ {
53
+ name : `${ reqMethod } ${ reqPath } ` ,
54
+ op : 'http.server' ,
55
+ ...traceparentData ,
56
+ } ,
57
+ // extra context passed to the `tracesSampler`
58
+ { request : req } ,
59
+ ) ;
60
+ currentScope . setSpan ( transaction ) ;
61
+ }
54
62
}
55
- }
56
63
57
- return await handler ( req , res ) ; // Call original handler
58
- } catch ( e ) {
59
- withScope ( scope => {
60
- scope . addEventProcessor ( event => {
61
- addExceptionMechanism ( event , {
62
- handled : false ,
64
+ return await handler ( req , res ) ; // Call original handler
65
+ } catch ( e ) {
66
+ withScope ( scope => {
67
+ scope . addEventProcessor ( event => {
68
+ addExceptionMechanism ( event , {
69
+ handled : false ,
70
+ } ) ;
71
+ return parseRequest ( event , req ) ;
63
72
} ) ;
64
- return parseRequest ( event , req ) ;
73
+ captureException ( e ) ;
65
74
} ) ;
66
- captureException ( e ) ;
67
- } ) ;
68
- throw e ;
69
- } finally {
70
- const transaction = getActiveTransaction ( ) ;
71
- if ( transaction ) {
72
- transaction . setHttpStatus ( res . statusCode ) ;
75
+ throw e ;
76
+ } finally {
77
+ const transaction = getActiveTransaction ( ) ;
78
+ if ( transaction ) {
79
+ transaction . setHttpStatus ( res . statusCode ) ;
73
80
74
- transaction . finish ( ) ;
75
- }
76
- try {
77
- await flush ( 2000 ) ;
78
- } catch ( e ) {
79
- // no-empty
81
+ transaction . finish ( ) ;
82
+ }
83
+ try {
84
+ await flush ( 2000 ) ;
85
+ } catch ( e ) {
86
+ // no-empty
87
+ }
80
88
}
81
- }
89
+ } ) ;
82
90
} ;
83
91
} ;
0 commit comments