1- /* eslint-disable @typescript-eslint/no-explicit-any */
21import { Integration , Transaction } from '@sentry/types' ;
32import { logger } from '@sentry/utils' ;
43
5- // Have to manually set types because we are using package-alias
64type Method =
75 | 'all'
86 | 'get'
@@ -27,17 +25,14 @@ type Method =
2725 | 'subscribe'
2826 | 'trace'
2927 | 'unlock'
30- | 'unsubscribe' ;
28+ | 'unsubscribe'
29+ | 'use' ;
3130
32- type Application = {
33- [ method in Method | 'use' ] : ( ...args : any ) => any ;
31+ type Router = {
32+ [ method in Method ] : ( ...args : unknown [ ] ) => unknown ;
3433} ;
3534
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 {
4136 once ( name : string , callback : ( ) => void ) : void ;
4237}
4338
@@ -52,8 +47,7 @@ interface SentryTracingResponse {
5247/**
5348 * Express integration
5449 *
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
5751 */
5852export class Express implements Integration {
5953 /**
@@ -69,27 +63,26 @@ export class Express implements Integration {
6963 /**
7064 * Express App instance
7165 */
72- private readonly _app ?: Application ;
66+ private readonly _router ?: Router ;
7367 private readonly _methods ?: Method [ ] ;
7468
7569 /**
7670 * @inheritDoc
7771 */
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' ) ;
8175 }
8276
8377 /**
8478 * @inheritDoc
8579 */
8680 public setupOnce ( ) : void {
87- if ( ! this . _app ) {
81+ if ( ! this . _router ) {
8882 logger . error ( 'ExpressIntegration is missing an Express instance' ) ;
8983 return ;
9084 }
91- instrumentMiddlewares ( this . _app ) ;
92- routeMiddlewares ( this . _app , this . _methods ) ;
85+ instrumentMiddlewares ( this . _router , this . _methods ) ;
9386 }
9487}
9588
@@ -104,75 +97,63 @@ export class Express implements Integration {
10497 * app.use(function (req, res, next) { ... })
10598 * // error handler
10699 * app.use(function (err, req, res, next) { ... })
100+ *
101+ * They all internally delegate to the `router[method]` of the given application instance.
107102 */
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 {
110105 const arity = fn . length ;
111106
112107 switch ( arity ) {
113108 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 {
115110 const transaction = res . __sentry_transaction ;
116- addExpressReqToTransaction ( transaction , req ) ;
117111 if ( transaction ) {
118112 const span = transaction . startChild ( {
119113 description : fn . name ,
120- op : ' middleware' ,
114+ op : ` middleware. ${ method } ` ,
121115 } ) ;
122116 res . once ( 'finish' , ( ) => {
123117 span . finish ( ) ;
124118 } ) ;
125119 }
126- // eslint-disable-next-line prefer-rest-params
127- return fn . apply ( this , arguments ) ;
120+ return fn . call ( this , req , res ) ;
128121 } ;
129122 }
130123 case 3 : {
131124 return function (
132125 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 {
137130 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 ) ;
151138 } ) ;
152139 } ;
153140 }
154141 case 4 : {
155142 return function (
156143 this : NodeJS . Global ,
157- err : any ,
144+ err : Error ,
158145 req : Request ,
159146 res : Response & SentryTracingResponse ,
160- next : NextFunction ,
161- ) : any {
147+ next : ( ) => void ,
148+ ) : void {
162149 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 ) ;
176157 } ) ;
177158 } ;
178159 }
@@ -183,24 +164,7 @@ function wrap(fn: Function): RequestHandler | ErrorRequestHandler {
183164}
184165
185166/**
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`
204168 * and wraps every function, as well as array of functions with a call to our `wrap` method.
205169 * We have to take care of the arrays as well as iterate over all of the arguments,
206170 * as `app.use` can accept middlewares in few various forms.
@@ -209,16 +173,16 @@ function addExpressReqToTransaction(transaction: Transaction | undefined, req: a
209173 * app.use([<path>], <fn>, ...<fn>)
210174 * app.use([<path>], ...<fn>[])
211175 */
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 ) => {
214178 if ( typeof arg === 'function' ) {
215- return wrap ( arg ) ;
179+ return wrap ( arg , method ) ;
216180 }
217181
218182 if ( Array . isArray ( arg ) ) {
219183 return arg . map ( ( a : unknown ) => {
220184 if ( typeof a === 'function' ) {
221- return wrap ( a ) ;
185+ return wrap ( a , method ) ;
222186 }
223187 return a ;
224188 } ) ;
@@ -229,31 +193,21 @@ function wrapUseArgs(args: IArguments): unknown[] {
229193}
230194
231195/**
232- * Patches original App to utilize our tracing functionality
196+ * Patches original router to utilize our tracing functionality
233197 */
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 ] ;
236200
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 ) ) ;
240203 } ;
241204
242- return app ;
205+ return router ;
243206}
244207
245208/**
246- * Patches original app.use
209+ * Patches original router methods
247210 */
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 ) ) ;
259213}
0 commit comments