@@ -66,6 +66,31 @@ const kSetNotFoundOriginal = Symbol('sentry fastify setNotFoundHandler original'
6666
6767type AnyFn = ( ...args : any [ ] ) => any ;
6868
69+ /**
70+ * Read the matched route URL off a request. Fastify >=4 exposes it on `request.routeOptions.url`,
71+ * while v3 only has the (since-removed-in-v5) `request.routerPath`.
72+ */
73+ function getRequestRouteUrl ( request : any ) : string | undefined {
74+ return request . routeOptions ?. url ?? request . routerPath ;
75+ }
76+
77+ /**
78+ * Read the per-route config off a request. Fastify >=4 exposes it on `request.routeOptions.config`,
79+ * while v3 uses `request.routeConfig`. Used to honor the `{ config: { otel: false } }` opt-out.
80+ */
81+ function getRequestRouteConfig ( request : any ) : { otel ?: boolean } | undefined {
82+ return request . routeOptions ?. config ?? request . routeConfig ;
83+ }
84+
85+ /**
86+ * Detect whether one of a wrapped handler's arguments is the Fastify request. We can't rely on a
87+ * single property since the route metadata moved from `routerPath` (v3) to `routeOptions` (>=4),
88+ * so we accept either shape.
89+ */
90+ function isFastifyRequest ( arg : any ) : boolean {
91+ return ! ! arg && typeof arg === 'object' && ! ! arg . method && ! ! arg . url && ( ! ! arg . routeOptions || 'routerPath' in arg ) ;
92+ }
93+
6994/**
7095 * The Fastify plugin that wires up the request/hook/handler spans. It is registered on every Fastify
7196 * instance via the `fastify.initialization` diagnostics channel.
@@ -146,7 +171,7 @@ function appendRouteHook(existing: AnyFn | AnyFn[] | undefined, hook: AnyFn): An
146171}
147172
148173function startRequestSpanHook ( this : any , request : any , _reply : any , hookDone : ( ) => void ) : void {
149- if ( request . routeOptions . config ?. otel === false ) {
174+ if ( getRequestRouteConfig ( request ) ?. otel === false ) {
150175 return hookDone ( ) ;
151176 }
152177
@@ -157,7 +182,7 @@ function startRequestSpanHook(this: any, request: any, _reply: any, hookDone: ()
157182 [ URL_PATH ] : request . url ,
158183 } ;
159184
160- const route = request . routeOptions . url as string | undefined ;
185+ const route = getRequestRouteUrl ( request ) ;
161186 if ( route != null ) {
162187 attributes [ HTTP_ROUTE ] = route ;
163188
@@ -288,7 +313,7 @@ function setNotFoundHandlerPatched(this: any, hooks: any, handler?: any): void {
288313
289314function getRequestFromArgs ( args : any [ ] ) : any | null {
290315 for ( const arg of args ) {
291- if ( arg ?. routeOptions && arg . url && arg . method ) {
316+ if ( isFastifyRequest ( arg ) ) {
292317 return arg ;
293318 }
294319 }
@@ -299,7 +324,7 @@ function handlerWrapper(handler: AnyFn, hookName: string, spanAttributes: Record
299324 return function handlerWrapped ( this : any , ...args : any [ ] ) {
300325 const request = getRequestFromArgs ( args ) ;
301326
302- if ( request === null || request . routeOptions . config ?. otel === false ) {
327+ if ( request === null || getRequestRouteConfig ( request ) ?. otel === false ) {
303328 return handler . call ( this , ...args ) ;
304329 }
305330
@@ -340,7 +365,7 @@ function stripFastifyPrefix(hookName = ''): string {
340365
341366function instrumentOnRequest ( fastify : FastifyInstance ) : void {
342367 fastify . addHook ( 'onRequest' , async ( request : FastifyRequest , _reply ) => {
343- const routeName = request . routeOptions ?. url ;
368+ const routeName = getRequestRouteUrl ( request ) ;
344369 const method = request . method || 'GET' ;
345370
346371 getIsolationScope ( ) . setTransactionName ( `${ method } ${ routeName } ` ) ;
0 commit comments