1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
import { Integration , Transaction } from '@sentry/types' ;
3
2
import { logger } from '@sentry/utils' ;
4
3
5
- // Have to manually set types because we are using package-alias
6
4
type Method =
7
5
| 'all'
8
6
| 'get'
@@ -27,17 +25,14 @@ type Method =
27
25
| 'subscribe'
28
26
| 'trace'
29
27
| 'unlock'
30
- | 'unsubscribe' ;
28
+ | 'unsubscribe'
29
+ | 'use' ;
31
30
32
- type Application = {
33
- [ method in Method | 'use' ] : ( ...args : any ) => any ;
31
+ type Router = {
32
+ [ method in Method ] : ( ...args : unknown [ ] ) => unknown ;
34
33
} ;
35
34
36
- type ErrorRequestHandler = ( ...args : any ) => any ;
37
- type RequestHandler = ( ...args : any ) => any ;
38
- type NextFunction = ( ...args : any ) => any ;
39
-
40
- interface Response {
35
+ interface ExpressResponse {
41
36
once ( name : string , callback : ( ) => void ) : void ;
42
37
}
43
38
@@ -52,8 +47,7 @@ interface SentryTracingResponse {
52
47
/**
53
48
* Express integration
54
49
*
55
- * Provides an request and error handler for Express framework
56
- * as well as tracing capabilities
50
+ * Provides an request and error handler for Express framework as well as tracing capabilities
57
51
*/
58
52
export class Express implements Integration {
59
53
/**
@@ -69,27 +63,26 @@ export class Express implements Integration {
69
63
/**
70
64
* Express App instance
71
65
*/
72
- private readonly _app ?: Application ;
66
+ private readonly _router ?: Router ;
73
67
private readonly _methods ?: Method [ ] ;
74
68
75
69
/**
76
70
* @inheritDoc
77
71
*/
78
- public constructor ( options : { app ?: Application ; methods ?: Method [ ] } = { } ) {
79
- this . _app = options . app ;
80
- this . _methods = options . methods ;
72
+ public constructor ( options : { app ?: Router ; router ?: Router ; methods ?: Method [ ] } = { } ) {
73
+ this . _router = options . router || options . app ;
74
+ this . _methods = ( Array . isArray ( options . methods ) ? options . methods : [ ] ) . concat ( 'use' ) ;
81
75
}
82
76
83
77
/**
84
78
* @inheritDoc
85
79
*/
86
80
public setupOnce ( ) : void {
87
- if ( ! this . _app ) {
81
+ if ( ! this . _router ) {
88
82
logger . error ( 'ExpressIntegration is missing an Express instance' ) ;
89
83
return ;
90
84
}
91
- instrumentMiddlewares ( this . _app ) ;
92
- routeMiddlewares ( this . _app , this . _methods ) ;
85
+ instrumentMiddlewares ( this . _router , this . _methods ) ;
93
86
}
94
87
}
95
88
@@ -104,75 +97,63 @@ export class Express implements Integration {
104
97
* app.use(function (req, res, next) { ... })
105
98
* // error handler
106
99
* app.use(function (err, req, res, next) { ... })
100
+ *
101
+ * They all internally delegate to the `router[method]` of the given application instance.
107
102
*/
108
- // eslint-disable-next-line @typescript-eslint/ban-types
109
- function wrap ( fn : Function ) : RequestHandler | ErrorRequestHandler {
103
+ // eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any
104
+ function wrap ( fn : Function , method : Method ) : ( ... args : any [ ] ) => void {
110
105
const arity = fn . length ;
111
106
112
107
switch ( arity ) {
113
108
case 2 : {
114
- return function ( this : NodeJS . Global , req : Request , res : Response & SentryTracingResponse ) : any {
109
+ return function ( this : NodeJS . Global , req : unknown , res : ExpressResponse & SentryTracingResponse ) : void {
115
110
const transaction = res . __sentry_transaction ;
116
- addExpressReqToTransaction ( transaction , req ) ;
117
111
if ( transaction ) {
118
112
const span = transaction . startChild ( {
119
113
description : fn . name ,
120
- op : ' middleware' ,
114
+ op : ` middleware. ${ method } ` ,
121
115
} ) ;
122
116
res . once ( 'finish' , ( ) => {
123
117
span . finish ( ) ;
124
118
} ) ;
125
119
}
126
- // eslint-disable-next-line prefer-rest-params
127
- return fn . apply ( this , arguments ) ;
120
+ return fn . call ( this , req , res ) ;
128
121
} ;
129
122
}
130
123
case 3 : {
131
124
return function (
132
125
this : NodeJS . Global ,
133
- req : Request ,
134
- res : Response & SentryTracingResponse ,
135
- next : NextFunction ,
136
- ) : any {
126
+ req : unknown ,
127
+ res : ExpressResponse & SentryTracingResponse ,
128
+ next : ( ) => void ,
129
+ ) : void {
137
130
const transaction = res . __sentry_transaction ;
138
- addExpressReqToTransaction ( transaction , req ) ;
139
- const span =
140
- transaction &&
141
- transaction . startChild ( {
142
- description : fn . name ,
143
- op : 'middleware' ,
144
- } ) ;
145
- fn . call ( this , req , res , function ( this : NodeJS . Global ) : any {
146
- if ( span ) {
147
- span . finish ( ) ;
148
- }
149
- // eslint-disable-next-line prefer-rest-params
150
- return next . apply ( this , arguments ) ;
131
+ const span = transaction ?. startChild ( {
132
+ description : fn . name ,
133
+ op : `middleware.${ method } ` ,
134
+ } ) ;
135
+ fn . call ( this , req , res , function ( this : NodeJS . Global , ...args : unknown [ ] ) : void {
136
+ span ?. finish ( ) ;
137
+ next . call ( this , ...args ) ;
151
138
} ) ;
152
139
} ;
153
140
}
154
141
case 4 : {
155
142
return function (
156
143
this : NodeJS . Global ,
157
- err : any ,
144
+ err : Error ,
158
145
req : Request ,
159
146
res : Response & SentryTracingResponse ,
160
- next : NextFunction ,
161
- ) : any {
147
+ next : ( ) => void ,
148
+ ) : void {
162
149
const transaction = res . __sentry_transaction ;
163
- addExpressReqToTransaction ( transaction , req ) ;
164
- const span =
165
- transaction &&
166
- transaction . startChild ( {
167
- description : fn . name ,
168
- op : 'middleware' ,
169
- } ) ;
170
- fn . call ( this , err , req , res , function ( this : NodeJS . Global ) : any {
171
- if ( span ) {
172
- span . finish ( ) ;
173
- }
174
- // eslint-disable-next-line prefer-rest-params
175
- return next . apply ( this , arguments ) ;
150
+ const span = transaction ?. startChild ( {
151
+ description : fn . name ,
152
+ op : `middleware.${ method } ` ,
153
+ } ) ;
154
+ fn . call ( this , err , req , res , function ( this : NodeJS . Global , ...args : unknown [ ] ) : void {
155
+ span ?. finish ( ) ;
156
+ next . call ( this , ...args ) ;
176
157
} ) ;
177
158
} ;
178
159
}
@@ -183,24 +164,7 @@ function wrap(fn: Function): RequestHandler | ErrorRequestHandler {
183
164
}
184
165
185
166
/**
186
- * Set parameterized as transaction name e.g.: `GET /users/:id`
187
- * Also adds more context data on the transaction from the request
188
- */
189
- function addExpressReqToTransaction ( transaction : Transaction | undefined , req : any ) : void {
190
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
191
- if ( transaction ) {
192
- if ( req . route && req . route . path ) {
193
- transaction . name = `${ req . method } ${ req . route . path } ` ;
194
- }
195
- transaction . setData ( 'url' , req . originalUrl ) ;
196
- transaction . setData ( 'baseUrl' , req . baseUrl ) ;
197
- transaction . setData ( 'query' , req . query ) ;
198
- }
199
- /* eslint-enable @typescript-eslint/no-unsafe-member-access */
200
- }
201
-
202
- /**
203
- * Takes all the function arguments passed to the original `app.use` call
167
+ * Takes all the function arguments passed to the original `app` or `router` method, eg. `app.use` or `router.use`
204
168
* and wraps every function, as well as array of functions with a call to our `wrap` method.
205
169
* We have to take care of the arrays as well as iterate over all of the arguments,
206
170
* as `app.use` can accept middlewares in few various forms.
@@ -209,16 +173,16 @@ function addExpressReqToTransaction(transaction: Transaction | undefined, req: a
209
173
* app.use([<path>], <fn>, ...<fn>)
210
174
* app.use([<path>], ...<fn>[])
211
175
*/
212
- function wrapUseArgs ( args : IArguments ) : unknown [ ] {
213
- return Array . from ( args ) . map ( ( arg : unknown ) => {
176
+ function wrapMiddlewareArgs ( args : unknown [ ] , method : Method ) : unknown [ ] {
177
+ return args . map ( ( arg : unknown ) => {
214
178
if ( typeof arg === 'function' ) {
215
- return wrap ( arg ) ;
179
+ return wrap ( arg , method ) ;
216
180
}
217
181
218
182
if ( Array . isArray ( arg ) ) {
219
183
return arg . map ( ( a : unknown ) => {
220
184
if ( typeof a === 'function' ) {
221
- return wrap ( a ) ;
185
+ return wrap ( a , method ) ;
222
186
}
223
187
return a ;
224
188
} ) ;
@@ -229,31 +193,21 @@ function wrapUseArgs(args: IArguments): unknown[] {
229
193
}
230
194
231
195
/**
232
- * Patches original App to utilize our tracing functionality
196
+ * Patches original router to utilize our tracing functionality
233
197
*/
234
- function patchMiddleware ( app : Application , method : Method | 'use' ) : Application {
235
- const originalAppCallback = app [ method ] ;
198
+ function patchMiddleware ( router : Router , method : Method ) : Router {
199
+ const originalCallback = router [ method ] ;
236
200
237
- app [ method ] = function ( ) : any {
238
- // eslint-disable-next-line prefer-rest-params
239
- return originalAppCallback . apply ( this , wrapUseArgs ( arguments ) ) ;
201
+ router [ method ] = function ( ...args : unknown [ ] ) : void {
202
+ return originalCallback . call ( this , ...wrapMiddlewareArgs ( args , method ) ) ;
240
203
} ;
241
204
242
- return app ;
205
+ return router ;
243
206
}
244
207
245
208
/**
246
- * Patches original app.use
209
+ * Patches original router methods
247
210
*/
248
- function instrumentMiddlewares ( app : Application ) : void {
249
- patchMiddleware ( app , 'use' ) ;
250
- }
251
-
252
- /**
253
- * Patches original app.METHOD
254
- */
255
- function routeMiddlewares ( app : Application , methods : Method [ ] = [ ] ) : void {
256
- methods . forEach ( function ( method : Method ) {
257
- patchMiddleware ( app , method ) ;
258
- } ) ;
211
+ function instrumentMiddlewares ( router : Router , methods : Method [ ] = [ ] ) : void {
212
+ methods . forEach ( ( method : Method ) => patchMiddleware ( router , method ) ) ;
259
213
}
0 commit comments