diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index d9d7d882cb719..e2a466b22dae9 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -702,6 +702,15 @@ export default abstract class Server { ): Promise { await this.prepare() const method = req.method.toUpperCase() + + // Extract the w3c trace context headers and pass them to tracer + const traceParentHeader = req.headers.traceparent + const traceStateHeader = req.headers.tracestate + const traceParent = + typeof traceParentHeader === 'string' ? traceParentHeader : undefined + const traceState = + typeof traceStateHeader === 'string' ? traceStateHeader : undefined + return getTracer().trace( BaseServerSpan.handleRequest, { @@ -711,6 +720,7 @@ export default abstract class Server { 'http.method': method, 'http.target': req.url, }, + reqHeaderTraceContext: { traceParent, traceState }, }, async (span) => this.handleRequestImpl(req, res, parsedUrl).finally(() => { diff --git a/packages/next/src/server/lib/trace/tracer.ts b/packages/next/src/server/lib/trace/tracer.ts index 60632b318afee..d407e7aa4243c 100644 --- a/packages/next/src/server/lib/trace/tracer.ts +++ b/packages/next/src/server/lib/trace/tracer.ts @@ -28,7 +28,7 @@ if (process.env.NEXT_RUNTIME === 'edge') { } } -const { context, trace, SpanStatusCode, SpanKind } = api +const { context, trace, SpanStatusCode, SpanKind, propagation } = api const isPromise = (p: any): p is Promise => { return p !== null && typeof p === 'object' && typeof p.then === 'function' @@ -47,6 +47,10 @@ type TracerSpanOptions = Omit & { spanName?: string attributes?: Partial> hideSpan?: boolean + reqHeaderTraceContext?: { + traceParent?: string + traceState?: string + } } interface NextTracer { @@ -220,8 +224,20 @@ class NextTracerImpl implements NextTracer { let spanContext = this.getSpanContext( options?.parentSpan ?? this.getActiveScopeSpan() ) - let isRootSpan = false + // Use w3c trace context headers to create span context if they exist + if ( + !spanContext && + (options.reqHeaderTraceContext?.traceParent || + options.reqHeaderTraceContext?.traceState) + ) { + spanContext = propagation.extract(context.active(), { + traceparent: options.reqHeaderTraceContext?.traceParent, + tracestate: options.reqHeaderTraceContext?.traceState, + }) + } + + let isRootSpan = false if (!spanContext) { spanContext = api.ROOT_CONTEXT isRootSpan = true