Skip to content

Commit fbe0085

Browse files
committed
better support v3.21.0 and test it
1 parent e280414 commit fbe0085

3 files changed

Lines changed: 47 additions & 17 deletions

File tree

dev-packages/e2e-tests/test-applications/node-fastify-3/tests/transactions.test.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,19 @@ test('Sends an API route transaction', async ({ baseURL }) => {
6565

6666
const spans = transactionEvent.spans || [];
6767

68+
/* TODO(v11): Uncomment this. This only works on fastify v3.21.0, not worth it to test this here */
69+
/*
6870
expect(spans).toContainEqual({
6971
data: {
70-
'plugin.name': 'sentry-fastify-error-handler',
71-
'fastify.type': 'request_handler',
72-
'http.route': '/test-transaction',
7372
'sentry.origin': 'auto.http.otel.fastify',
7473
'sentry.op': 'request_handler.fastify',
74+
'fastify.root': '@sentry/instrumentation-fastify',
75+
'http.request.method': 'GET',
76+
'url.path': '/test-transaction',
77+
'http.route': '/test-transaction',
78+
'http.response.status_code': 200,
7579
},
76-
description: 'sentry-fastify-error-handler',
80+
description: 'GET /test-transaction',
7781
op: 'request_handler.fastify',
7882
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
7983
span_id: expect.stringMatching(/[a-f0-9]{16}/),
@@ -83,16 +87,17 @@ test('Sends an API route transaction', async ({ baseURL }) => {
8387
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
8488
origin: 'auto.http.otel.fastify',
8589
});
90+
*/
8691

8792
expect(spans).toContainEqual({
88-
data: {
89-
'plugin.name': 'sentry-fastify-error-handler',
90-
'fastify.type': 'request_handler',
91-
'http.route': '/test-transaction',
92-
'sentry.op': 'request_handler.fastify',
93+
data: expect.objectContaining({
9394
'sentry.origin': 'auto.http.otel.fastify',
94-
},
95-
description: 'sentry-fastify-error-handler',
95+
'sentry.op': 'request_handler.fastify',
96+
// format is slightly different in v3.20.0 and v3.21.0
97+
'fastify.type': expect.stringMatching(/request[-_]handler/),
98+
'http.route': '/test-transaction',
99+
}),
100+
description: expect.stringContaining('sentry-fastify-error-handler'),
96101
op: 'request_handler.fastify',
97102
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
98103
span_id: expect.stringMatching(/[a-f0-9]{16}/),

packages/node/src/integrations/tracing/fastify/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111

1212
interface FastifyIntegration extends Integration {
1313
getShouldHandleError: () => (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean;
14-
// This will be removed in the next major version.
14+
// todo(v11): Remove this
1515
setShouldHandleError: (
1616
shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean,
1717
) => void;

packages/server-utils/src/integrations/tracing-channel/fastify/instrumentation.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,31 @@ const kSetNotFoundOriginal = Symbol('sentry fastify setNotFoundHandler original'
6666

6767
type 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

148173
function 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

289314
function 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

341366
function 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

Comments
 (0)