Skip to content

Commit f916be1

Browse files
authored
feat(node): Use diagnostics channel for Fastify v5 error handling (#16715)
Resolves: #15936
1 parent 40f04bc commit f916be1

File tree

2 files changed

+47
-8
lines changed
  • dev-packages/e2e-tests/test-applications/node-fastify-5/src
  • packages/node/src/integrations/tracing/fastify

2 files changed

+47
-8
lines changed

dev-packages/e2e-tests/test-applications/node-fastify-5/src/app.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ const app = fastify();
3434
const port = 3030;
3535
const port2 = 3040;
3636

37-
Sentry.setupFastifyErrorHandler(app);
38-
3937
app.get('/test-success', function (_req, res) {
4038
res.send({ version: 'v1' });
4139
});

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,42 @@ const INTEGRATION_NAME_V3 = 'Fastify-V3';
5757

5858
export const instrumentFastifyV3 = generateInstrumentOnce(INTEGRATION_NAME_V3, () => new FastifyInstrumentationV3());
5959

60+
function handleFastifyError(
61+
this: {
62+
diagnosticsChannelExists?: boolean;
63+
},
64+
error: Error,
65+
request: FastifyRequest & { opentelemetry?: () => { span?: Span } },
66+
reply: FastifyReply,
67+
shouldHandleError: (error: Error, request: FastifyRequest, reply: FastifyReply) => boolean,
68+
handlerOrigin: 'diagnostics-channel' | 'onError-hook',
69+
): void {
70+
// Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered
71+
if (handlerOrigin === 'diagnostics-channel') {
72+
this.diagnosticsChannelExists = true;
73+
}
74+
75+
if (this.diagnosticsChannelExists && handlerOrigin === 'onError-hook') {
76+
DEBUG_BUILD &&
77+
logger.warn(
78+
'Fastify error handler was already registered via diagnostics channel.',
79+
'You can safely remove `setupFastifyErrorHandler` call.',
80+
);
81+
82+
// If the diagnostics channel already exists, we don't need to handle the error again
83+
return;
84+
}
85+
86+
if (shouldHandleError(error, request, reply)) {
87+
captureException(error);
88+
}
89+
}
90+
6091
export const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () => {
6192
const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation();
6293
const plugin = fastifyOtelInstrumentationInstance.plugin();
94+
const options = fastifyOtelInstrumentationInstance.getConfig();
95+
const shouldHandleError = (options as FastifyHandlerOptions)?.shouldHandleError || defaultShouldHandleError;
6396

6497
// This message handler works for Fastify versions 3, 4 and 5
6598
diagnosticsChannel.subscribe('fastify.initialization', message => {
@@ -78,8 +111,20 @@ export const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () =>
78111
});
79112
});
80113

114+
// This diagnostics channel only works on Fastify version 5
115+
// For versions 3 and 4, we use `setupFastifyErrorHandler` instead
116+
diagnosticsChannel.subscribe('tracing:fastify.request.handler:error', message => {
117+
const { error, request, reply } = message as {
118+
error: Error;
119+
request: FastifyRequest & { opentelemetry?: () => { span?: Span } };
120+
reply: FastifyReply;
121+
};
122+
123+
handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'diagnostics-channel');
124+
});
125+
81126
// Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation
82-
return fastifyOtelInstrumentationInstance as Instrumentation<InstrumentationConfig>;
127+
return fastifyOtelInstrumentationInstance as Instrumentation<InstrumentationConfig & FastifyHandlerOptions>;
83128
});
84129

85130
const _fastifyIntegration = (() => {
@@ -143,15 +188,11 @@ function defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply
143188
*/
144189
export function setupFastifyErrorHandler(fastify: FastifyInstance, options?: Partial<FastifyHandlerOptions>): void {
145190
const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError;
146-
147191
const plugin = Object.assign(
148192
function (fastify: FastifyInstance, _options: unknown, done: () => void): void {
149193
fastify.addHook('onError', async (request, reply, error) => {
150-
if (shouldHandleError(error, request, reply)) {
151-
captureException(error);
152-
}
194+
handleFastifyError.call(handleFastifyError, error, request, reply, shouldHandleError, 'onError-hook');
153195
});
154-
155196
done();
156197
},
157198
{

0 commit comments

Comments
 (0)