@fastify/otel

OpenTelemetry auto-instrumentation library.
Install
npm i @fastify/otel
Usage
@fastify/otel works as a metric creator as well as application performance monitor for your Fastify application.
It must be configured before defining routes and other plugins in order to cover the most of your Fastify server.
- It automatically wraps the main request handler
- Instruments all route hooks (defined at instance and route definition level)
onRequest
preParsing
preValidation
preHandler
preSerialization
onSend
onResponse
onError
- Instruments automatically custom 404 Not Found handler
Example:
const FastifyOtelInstrumentation = require('@fastify/otel');
const fastifyOtelInstrumentation = new FastifyOtelInstrumentation();
fastifyOtelInstrumentation.setTracerProvider(provider)
module.exports = { fastifyOtelInstrumentation }
const { fastifyOtelInstrumentation } = require('./otel.js');
const Fastify = require('fastify');
const app = fastify();
await app.register(fastifyOtelInstrumentation.plugin());
app.get('/', () => 'hello world')
app.addHook('onError', () => )
app.get('/healthcheck', { config: { otel: false } }, () => 'Up!')
app.register((instance, opts, done) => {
instance.register(fastifyOtelInstrumentation.plugin());
app.get('/', () => 'hello world')
done()
}, { prefix: '/nested' })
Registration using OpenTelemetry Node SDK
The plugin can be automatically registered using Node SDK, with registerOnInitialization option set to true.
import { NodeSDK } from '@opentelemetry/sdk-node';
import FastifyOtelInstrumentation from "@fastify/otel";
const sdk = new NodeSDK({
resource: ...,
traceExporter: ...,
instrumentations: [
...others,
new FastifyOtelInstrumentation({ registerOnInitialization: true })
],
});
sdk.start();
Notes:
- This instrumentation requires
@opentelemetry/instrumentation-http to be able to propagate the traces all the way back to upstream
- The HTTP instrumentation might cover all your routes although
@fastify/otel just covers a subset of your application
For more information about OpenTelemetry, please refer to the OpenTelemetry JavaScript documentation.
APIs
FastifyOtelRequestContext
The FastifyOtelRequestContext is a wrapper around the OpenTelemetry Context and Tracer APIs. It also provides a way to manage the context of a request and its associated spans as well as some utilities to extract and inject further traces from and to the trace carrier.
FastifyOtelRequestContext#context: Context
The OpenTelemetry context object.
FastifyOtelRequestContext#tracer: Tracer
The OpenTelemetry tracer object.
FastifyOtelRequestContext#span: Span
The OpenTelemetry span object.
The span is created for each request and is automatically ended when the request is completed.
FastifyOtelRequestContext#inject: function
The OpenTelemetry inject function. It is used to inject the current context into a carrier object.
The carrier object can be any object that can hold key-value pairs, such as an HTTP request or response headers.
The OpenTelemetry extract function. It is used to extract a parent context from a carrier object.
The carrier object can be any object that can hold key-value pairs, such as an HTTP request or response headers.
The extracted context can be used as a parent span for a new span.
const { fastifyOtelInstrumentation } = require('./otel.js');
const Fastify = require('fastify');
const app = fastify();
await app.register(fastifyOtelInstrumentation.plugin());
app.get('/', (req, reply) => {
const { context, tracer, span, inject, extract } = req.opentelemetry();
const parentCxt = extract(req.headers);
const newSpan = tracer.startSpan('my-new-span', {
parent: parentCxt,
});
newSpan.end();
const carrier = {};
inject(carrier);
reply.headers(carrier);
return 'hello world';
});
Interfaces
FastifyOtelInstrumentationOptions
The options for the FastifyOtelInstrumentation class.
FastifyOtelInstrumentationOptions#registerOnInitialization: boolean
Whether to register the plugin on initialization. If set to true, the plugin will be registered automatically when the Fastify instance is created.
This is useful for applications that want to ensure that all routes are instrumented without having to manually register the plugin.
FastifyOtelInstrumentationOptions#ignorePaths: string | function
String or function to ignore paths from being instrumented.
If a string is provided, it will be used as a glob match pattern.
If a function is provided, it will be called with the request options and should return true if the path should be ignored.
FastifyOtelInstrumentationOptions#requestHook: function
A synchronous callback that runs immediately after the root request span is created.
- span – the newly-created
Span
- info.request – the current
FastifyRequest
Use it to add custom attributes, events, or rename the span.
If the function throws, the error is caught and logged so the request flow is never interrupted.
Examples
import { FastifyOtelInstrumentation } from '@fastify/otel';
const fastifyOtelInstrumentation = new FastifyOtelInstrumentation({
registerOnInitialization: true,
ignorePaths: (opts) => {
return opts.url.startsWith('/ignore');
},
});
const otel = new FastifyOtelInstrumentation({
requestHook: (span, request) => {
span.setAttribute('user.id', request.headers['x-user-id'] ?? 'anonymous')
span.updateName(`${request.method} ${request.routeOptions.url}`)
}
})
FastifyOtelInstrumentationOptions#lifecycleHook: function
A synchronous callback that runs whenever a span is created for a Fastify lifecycle hook (route hooks, instance hooks, not-found handlers, and route handlers).
- span – the hook span that was just created
- info.hookName – Fastify lifecycle stage (e.g.,
onRequest, preHandler, handler)
- info.handler – the resolved handler or plugin name when available
- info.request – the current
FastifyRequest
Use it to rename hook spans or annotate them with framework-specific metadata (for example, tRPC procedure names) without registering additional Fastify hooks.
const otel = new FastifyOtelInstrumentation({
lifecycleHook: (span, info) => {
if (info.hookName === 'handler' && info.request.headers['x-trpc-op'] != null) {
span.updateName(`tRPC handler - ${info.request.headers['x-trpc-op']}`)
}
span.setAttribute('hook.handler.name', info.handler ?? 'anonymous')
}
})
FastifyOtelInstrumentationOptions#recordExceptions: boolean
Control whether the instrumentation automatically calls span.recordException when a handler or hook throws.
Defaults to true, recording every exception. Set it to false if you prefer to record only the
exceptions that you consider actionable (for example to avoid noisy 4xx entries in Datadog Error Tracking).
License
Licensed under MIT.