11import { captureException , flush , getCurrentHub , Handlers , startTransaction , withScope } from '@sentry/node' ;
22import { extractTraceparentData , getActiveTransaction , hasTracingEnabled } from '@sentry/tracing' ;
33import { addExceptionMechanism , isString , logger , stripUrlQueryAndFragment } from '@sentry/utils' ;
4+ import * as domain from 'domain' ;
45import { NextApiHandler } from 'next' ;
56
67import { addRequestDataToEvent , NextRequest } from './instrumentServer' ;
@@ -14,70 +15,77 @@ type WrappedNextApiHandler = NextApiHandler;
1415export const withSentry = ( handler : NextApiHandler ) : WrappedNextApiHandler => {
1516 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
1617 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 ) ;
1922
20- if ( currentScope ) {
21- currentScope . addEventProcessor ( event => addRequestDataToEvent ( event , req as NextRequest ) ) ;
23+ local . run ( async ( ) => {
24+ try {
25+ const currentScope = getCurrentHub ( ) . getScope ( ) ;
2226
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 ) ) ;
3029
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 } .` ) ;
4036 }
41- }
42- const reqMethod = `${ ( req . method || 'GET' ) . toUpperCase ( ) } ` ;
4337
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+ }
5462 }
55- }
5663
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 ) ;
6372 } ) ;
64- return parseRequest ( event , req ) ;
73+ captureException ( e ) ;
6574 } ) ;
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 ) ;
7380
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+ }
8088 }
81- }
89+ } ) ;
8290 } ;
8391} ;
0 commit comments