1+ import { DEBUG_BUILD } from '../../debug-build' ;
12import { SEMANTIC_ATTRIBUTE_SENTRY_OP , SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes' ;
23import { SPAN_STATUS_ERROR , startSpanManual , withActiveSpan } from '../../tracing' ;
4+ import { debug } from '../../utils/debug-logger' ;
35import type { SpanAttributes } from '../../types-hoist/span' ;
46import { getActiveSpan } from '../../utils/spanUtils' ;
57import { getStoredLayers , storeLayer } from './request-layer-store' ;
68import {
79 type ExpressRequest ,
810 type ExpressResponse ,
9- kLayerPatched ,
1011 type ExpressIntegrationOptions ,
1112 type ExpressLayer ,
1213 ATTR_HTTP_ROUTE ,
1314 ATTR_EXPRESS_TYPE ,
1415 ATTR_EXPRESS_NAME ,
15- type ExpressLayerType ,
1616 ExpressLayerType_MIDDLEWARE ,
1717 ExpressLayerType_ROUTER ,
1818} from './types' ;
@@ -21,27 +21,31 @@ import {
2121 getActualMatchedRoute ,
2222 getConstructedRoute ,
2323 getLayerMetadata ,
24- getSpanName ,
2524 isLayerIgnored ,
2625} from './utils' ;
26+ import { getIsolationScope } from '../../currentScopes' ;
27+ import { getDefaultIsolationScope } from '../../defaultScopes' ;
28+ import { getOriginalFunction , markFunctionWrapped } from '../../utils/object' ;
2729
2830export type ExpressPatchLayerOptions = Pick <
2931 ExpressIntegrationOptions ,
3032 'onRouteResolved' | 'ignoreLayers' | 'ignoreLayersType'
3133> ;
3234
3335export function patchLayer ( options : ExpressPatchLayerOptions , maybeLayer ?: ExpressLayer , layerPath ?: string ) : void {
34- if ( ! maybeLayer ) {
36+ if ( ! maybeLayer ?. handle ) {
3537 return ;
3638 }
3739 const layer = maybeLayer ;
3840
41+ const layerHandleOriginal = layer . handle ;
42+
3943 // avoid patching multiple times the same layer
40- if ( layer [ kLayerPatched ] === true ) return ;
41- layer [ kLayerPatched ] = true ;
44+ if ( getOriginalFunction ( layerHandleOriginal ) ) {
45+ return ;
46+ }
4247
43- const originalHandle = layer . handle ;
44- if ( originalHandle . length === 4 ) {
48+ if ( layerHandleOriginal . length === 4 ) {
4549 // todo: instrument error handlers
4650 return ;
4751 }
@@ -57,10 +61,12 @@ export function patchLayer(options: ExpressPatchLayerOptions, maybeLayer?: Expre
5761 // Without a parent span, this request is being ignored, so skip it
5862 const parentSpan = getActiveSpan ( ) ;
5963 if ( ! parentSpan ) {
60- return originalHandle . apply ( this , [ req , res , ...otherArgs ] ) ;
64+ return layerHandleOriginal . apply ( this , [ req , res , ...otherArgs ] ) ;
6165 }
6266
63- if ( layerPath ) storeLayer ( req , layerPath ) ;
67+ if ( layerPath ) {
68+ storeLayer ( req , layerPath ) ;
69+ }
6470 const storedLayers = getStoredLayers ( req ) ;
6571 const isLayerPathStored = ! ! layerPath ;
6672
@@ -70,8 +76,8 @@ export function patchLayer(options: ExpressPatchLayerOptions, maybeLayer?: Expre
7076 options . onRouteResolved ?.( actualMatchedRoute ) ;
7177
7278 const metadata = getLayerMetadata ( constructedRoute , layer , layerPath ) ;
73- const name = metadata . attributes [ ATTR_EXPRESS_NAME ] as string ?? metadata . name ;
74- const type = metadata . attributes [ ATTR_EXPRESS_TYPE ] as ExpressLayerType ;
79+ const name = metadata . attributes [ ATTR_EXPRESS_NAME ] ;
80+ const type = metadata . attributes [ ATTR_EXPRESS_TYPE ] ;
7581 const attributes : SpanAttributes = Object . assign ( metadata . attributes , {
7682 [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.http.express' ,
7783 [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : `${ type } .express` ,
@@ -91,18 +97,21 @@ export function patchLayer(options: ExpressPatchLayerOptions, maybeLayer?: Expre
9197 if ( isLayerPathStored && type === ExpressLayerType_MIDDLEWARE ) {
9298 storedLayers . pop ( ) ;
9399 }
94- return originalHandle . apply ( this , [ req , res , ...otherArgs ] ) ;
100+ return layerHandleOriginal . apply ( this , [ req , res , ...otherArgs ] ) ;
95101 }
96102
97- const spanName = getSpanName (
98- {
99- request : req ,
100- layerType : type ,
101- route : constructedRoute ,
102- } ,
103- name ,
104- ) ;
105- return startSpanManual ( { name : spanName , attributes } , span => {
103+ const currentScope = getIsolationScope ( ) ;
104+ if ( currentScope !== getDefaultIsolationScope ( ) ) {
105+ if ( type === 'request_handler' ) {
106+ // type cast b/c Otel unfortunately types info.request as any :(
107+ const method = req . method ? req . method . toUpperCase ( ) : 'GET' ;
108+ currentScope . setTransactionName ( `${ method } ${ constructedRoute } ` ) ;
109+ }
110+ } else {
111+ DEBUG_BUILD && debug . warn ( 'Isolation scope is still default isolation scope - skipping setting transactionName' ) ;
112+ }
113+
114+ return startSpanManual ( { name, attributes } , span => {
106115 let spanHasEnded = false ;
107116 // TODO: Fix router spans (getRouterPath does not work properly) to
108117 // have useful names before removing this branch
@@ -121,7 +130,9 @@ export function patchLayer(options: ExpressPatchLayerOptions, maybeLayer?: Expre
121130 // verify we have a callback
122131 for ( let i = 0 ; i < otherArgs . length ; i ++ ) {
123132 const callback = otherArgs [ i ] as Function ;
124- if ( typeof callback !== 'function' ) continue ;
133+ if ( typeof callback !== 'function' ) {
134+ continue ;
135+ }
125136
126137 //oxlint-disable-next-line no-explicit-any
127138 otherArgs [ i ] = function ( ...args : any [ ] ) {
@@ -155,7 +166,7 @@ export function patchLayer(options: ExpressPatchLayerOptions, maybeLayer?: Expre
155166 }
156167
157168 try {
158- return originalHandle . apply ( this , [ req , res , ...otherArgs ] ) ;
169+ return layerHandleOriginal . apply ( this , [ req , res , ...otherArgs ] ) ;
159170 } catch ( anyError ) {
160171 const [ _ , message ] = asErrorAndMessage ( anyError ) ;
161172 // intentionally do not record the exception here, because
@@ -186,21 +197,23 @@ export function patchLayer(options: ExpressPatchLayerOptions, maybeLayer?: Expre
186197 // ref: https://github.com/open-telemetry/opentelemetry-js-contrib/issues/1950
187198 // ref: https://github.com/open-telemetry/opentelemetry-js-contrib/issues/2271
188199 // oxlint-disable-next-line guard-for-in
189- for ( const key in originalHandle as Function & Record < string , unknown > ) {
200+ for ( const key in layerHandleOriginal as Function & Record < string , unknown > ) {
190201 // skip standard function prototype fields that both have
191202 if ( key in layerHandlePatched ) {
192203 continue ;
193204 }
194205 Object . defineProperty ( layerHandlePatched , key , {
195206 get ( ) {
196- return originalHandle [ key ] ;
207+ return layerHandleOriginal [ key ] ;
197208 } ,
198209 set ( value ) {
199- originalHandle [ key ] = value ;
210+ layerHandleOriginal [ key ] = value ;
200211 } ,
201212 } ) ;
202213 }
203214
215+ markFunctionWrapped ( layerHandlePatched , layerHandleOriginal ) ;
216+
204217 Object . defineProperty ( layer , 'handle' , {
205218 enumerable : true ,
206219 configurable : true ,
0 commit comments