diff --git a/README.md b/README.md index 55a9387..b754f8b 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,8 @@ The plugin accepts the the following configuration properties: - The ignored routes will still have access to `request.openTelemetry`, but `activeSpan` will be `undefined`. +- **`propagateToReply` : `boolean`** - When `true`, the current span context will be injected into the reply headers using your registered propagators. Defaults to `false`. + ### Request Decorator This plugin decorates the request with an `openTelemetry` function that returns an object with the following properties: @@ -164,6 +166,8 @@ This plugin registers the following Fastify hooks: - `onRoute`: Added only if `wrapRoutes` is enabled. +- `onSend`: Added only if `propagateToReply` is enabled. + ### OpenTelemetry Compatibility As the [OpenTelemetry API](https://github.com/open-telemetry/opentelemetry-js-api#version-compatibility) uses a diff --git a/fastify-opentelemetry.d.ts b/fastify-opentelemetry.d.ts index 96ba0ea..e7066b6 100644 --- a/fastify-opentelemetry.d.ts +++ b/fastify-opentelemetry.d.ts @@ -35,6 +35,7 @@ declare namespace fastifyOpenTelemetry { }, wrapRoutes?: boolean | string[], ignoreRoutes?: string[] | ((path: string, method: string) => boolean), + propagateToReply?: boolean, } export const fastifyOpenTelemetry: FastifyPluginCallback diff --git a/index.js b/index.js index 4a5cabb..92cfd89 100644 --- a/index.js +++ b/index.js @@ -47,7 +47,8 @@ async function openTelemetryPlugin (fastify, opts = {}) { wrapRoutes, exposeApi = true, formatSpanName = defaultFormatSpanName, - ignoreRoutes = [] + ignoreRoutes = [], + propagateToReply = false } = opts const shouldIgnoreRoute = typeof ignoreRoutes === 'function' @@ -146,10 +147,19 @@ async function openTelemetryPlugin (fastify, opts = {}) { span.setAttributes(formatSpanAttributes.error(error)) } + async function onSend (request, reply, payload) { + const { inject } = request.openTelemetry() + const propagationHeaders = {} + inject(propagationHeaders) + reply.headers(propagationHeaders) + return payload + }; + fastify.addHook('onRequest', onRequest) if (wrapRoutes) fastify.addHook('onRequest', onRequestWrapRoutes) fastify.addHook('onResponse', onResponse) fastify.addHook('onError', onError) + if (propagateToReply) fastify.addHook('onSend', onSend) } module.exports = fp(openTelemetryPlugin, { diff --git a/test/fixtures/openTelemetryApi.js b/test/fixtures/openTelemetryApi.js index acb6306..69b8427 100644 --- a/test/fixtures/openTelemetryApi.js +++ b/test/fixtures/openTelemetryApi.js @@ -5,6 +5,17 @@ const { propagation, trace } = require('@opentelemetry/api') +const { + BasicTracerProvider, + ConsoleSpanExporter, + SimpleSpanProcessor +} = require('@opentelemetry/sdk-trace-base') +const { W3CTraceContextPropagator } = require('@opentelemetry/core') + +const provider = new BasicTracerProvider() +const exporter = new ConsoleSpanExporter() +provider.addSpanProcessor(new SimpleSpanProcessor(exporter)) +provider.register({ propagator: new W3CTraceContextPropagator() }) const { name: moduleName, version: moduleVersion } = require('../../package.json') diff --git a/test/index.test.js b/test/index.test.js index 4410d6b..dabd19d 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -463,3 +463,29 @@ test('should use request.routerPath if request.routeOptions does not exist', asy 'should not contain router path when no matching routes found' ) }) + +test('add propagation headers to reply when propagateToReply is true', async ({ same, teardown }) => { + const fastify = require('fastify')() + + await fastify.register(openTelemetryPlugin, { wrapRoutes: true, propagateToReply: true }) + + async function testHandler (request, reply) { + const { activeSpan } = request.openTelemetry() + reply.headers({ one: 'ok' }) + return { body: activeSpan.spanContext() } + } + + fastify.get('/test', testHandler) + + await fastify.ready() + + teardown(() => { + resetHistory() + fastify.close() + }) + + const res = await fastify.inject({ ...injectArgs, url: '/test' }) + const spanContext = res.json().body + same(res.statusCode, 200) + same(res.headers.traceparent, `00-${spanContext.traceId}-${spanContext.spanId}-0${spanContext.traceFlags}`) +}) diff --git a/test/types/fastify-opentelemetry.test-d.ts b/test/types/fastify-opentelemetry.test-d.ts index 38be05c..b5629d5 100644 --- a/test/types/fastify-opentelemetry.test-d.ts +++ b/test/types/fastify-opentelemetry.test-d.ts @@ -46,5 +46,6 @@ expectType(({ }, ignoreRoutes: (path: string, method: string) => method === 'OPTIONS', formatSpanName: (request: FastifyRequest) => `${request.method} constant-part`, - wrapRoutes: true + wrapRoutes: true, + propagateToReply: true }))