1- import { getClient , getCurrentHub , getCurrentScope , hasTracingEnabled } from '@sentry/core' ;
1+ import { getClient , getCurrentHub , getCurrentScope , hasTracingEnabled , runWithAsyncContext } from '@sentry/core' ;
22import { flush } from '@sentry/node' ;
33import type { Transaction } from '@sentry/types' ;
4- import { extractRequestData , isString , logger } from '@sentry/utils' ;
4+ import { extractRequestData , fill , isString , logger } from '@sentry/utils' ;
55import { cwd } from 'process' ;
66
77import { DEBUG_BUILD } from '../debug-build' ;
8- import {
9- createRoutes ,
10- getTransactionName ,
11- instrumentBuild ,
12- isRequestHandlerWrapped ,
13- startRequestHandlerTransaction ,
14- } from '../instrumentServer' ;
8+ import { createRoutes , getTransactionName , instrumentBuild , startRequestHandlerTransaction } from '../instrumentServer' ;
159import type {
10+ AppLoadContext ,
1611 ExpressCreateRequestHandler ,
1712 ExpressCreateRequestHandlerOptions ,
1813 ExpressNextFunction ,
1914 ExpressRequest ,
2015 ExpressRequestHandler ,
2116 ExpressResponse ,
17+ GetLoadContextFunction ,
2218 ReactRouterDomPkg ,
2319 ServerBuild ,
2420} from '../vendor/types' ;
@@ -31,11 +27,6 @@ function wrapExpressRequestHandler(
3127) : ExpressRequestHandler {
3228 const routes = createRoutes ( build . routes ) ;
3329
34- // If the core request handler is already wrapped, don't wrap Express handler which uses it.
35- if ( isRequestHandlerWrapped ) {
36- return origRequestHandler ;
37- }
38-
3930 return async function (
4031 this : unknown ,
4132 req : ExpressRequest ,
@@ -54,33 +45,46 @@ function wrapExpressRequestHandler(
5445 }
5546 }
5647
57- // eslint-disable-next-line @typescript-eslint/unbound-method
58- res . end = wrapEndMethod ( res . end ) ;
48+ await runWithAsyncContext ( async ( ) => {
49+ // eslint-disable-next-line @typescript-eslint/unbound-method
50+ res . end = wrapEndMethod ( res . end ) ;
5951
60- const request = extractRequestData ( req ) ;
61- const hub = getCurrentHub ( ) ;
62- const options = getClient ( ) ?. getOptions ( ) ;
63- const scope = getCurrentScope ( ) ;
52+ const request = extractRequestData ( req ) ;
53+ const hub = getCurrentHub ( ) ;
54+ const options = getClient ( ) ?. getOptions ( ) ;
55+ const scope = getCurrentScope ( ) ;
6456
65- scope . setSDKProcessingMetadata ( { request } ) ;
57+ scope . setSDKProcessingMetadata ( { request } ) ;
6658
67- if ( ! options || ! hasTracingEnabled ( options ) || ! request . url || ! request . method ) {
68- return origRequestHandler . call ( this , req , res , next ) ;
69- }
59+ if ( ! options || ! hasTracingEnabled ( options ) || ! request . url || ! request . method ) {
60+ return origRequestHandler . call ( this , req , res , next ) ;
61+ }
7062
71- const url = new URL ( request . url ) ;
72- const [ name , source ] = getTransactionName ( routes , url , pkg ) ;
73- const transaction = startRequestHandlerTransaction ( hub , name , source , {
74- headers : {
75- 'sentry-trace' : ( req . headers && isString ( req . headers [ 'sentry-trace' ] ) && req . headers [ 'sentry-trace' ] ) || '' ,
76- baggage : ( req . headers && isString ( req . headers . baggage ) && req . headers . baggage ) || '' ,
77- } ,
78- method : request . method ,
63+ const url = new URL ( request . url ) ;
64+
65+ const [ name , source ] = getTransactionName ( routes , url , pkg ) ;
66+ const transaction = startRequestHandlerTransaction ( hub , name , source , {
67+ headers : {
68+ 'sentry-trace' : ( req . headers && isString ( req . headers [ 'sentry-trace' ] ) && req . headers [ 'sentry-trace' ] ) || '' ,
69+ baggage : ( req . headers && isString ( req . headers . baggage ) && req . headers . baggage ) || '' ,
70+ } ,
71+ method : request . method ,
72+ } ) ;
73+ // save a link to the transaction on the response, so that even if there's an error (landing us outside of
74+ // the domain), we can still finish it (albeit possibly missing some scope data)
75+ ( res as AugmentedExpressResponse ) . __sentryTransaction = transaction ;
76+ return origRequestHandler . call ( this , req , res , next ) ;
7977 } ) ;
80- // save a link to the transaction on the response, so that even if there's an error (landing us outside of
81- // the domain), we can still finish it (albeit possibly missing some scope data)
82- ( res as AugmentedExpressResponse ) . __sentryTransaction = transaction ;
83- return origRequestHandler . call ( this , req , res , next ) ;
78+ } ;
79+ }
80+
81+ function wrapGetLoadContext ( origGetLoadContext : ( ) => AppLoadContext ) : GetLoadContextFunction {
82+ return function ( this : unknown , req : ExpressRequest , res : ExpressResponse ) : AppLoadContext {
83+ const loadContext = ( origGetLoadContext . call ( this , req , res ) || { } ) as AppLoadContext ;
84+
85+ loadContext [ '__sentry_express_wrapped__' ] = true ;
86+
87+ return loadContext ;
8488 } ;
8589}
8690
@@ -92,9 +96,18 @@ export function wrapExpressCreateRequestHandler(
9296 // eslint-disable-next-line @typescript-eslint/no-explicit-any
9397) : ( options : any ) => ExpressRequestHandler {
9498 // eslint-disable-next-line @typescript-eslint/no-explicit-any
95- return function ( this : unknown , options : any ) : ExpressRequestHandler {
96- const newBuild = instrumentBuild ( ( options as ExpressCreateRequestHandlerOptions ) . build ) ;
97- const requestHandler = origCreateRequestHandler . call ( this , { ...options , build : newBuild } ) ;
99+ return function ( this : unknown , options : ExpressCreateRequestHandlerOptions ) : ExpressRequestHandler {
100+ if ( ! ( 'getLoadContext' in options ) ) {
101+ options [ 'getLoadContext' ] = ( ) => ( { } ) ;
102+ }
103+
104+ fill ( options , 'getLoadContext' , wrapGetLoadContext ) ;
105+
106+ const newBuild = instrumentBuild ( options . build , true ) ;
107+ const requestHandler = origCreateRequestHandler . call ( this , {
108+ ...options ,
109+ build : newBuild ,
110+ } ) ;
98111
99112 return wrapExpressRequestHandler ( requestHandler , newBuild ) ;
100113 } ;
0 commit comments