diff --git a/pkg/infra/tracing/tracing.go b/pkg/infra/tracing/tracing.go index 4c6bd6545a3..5f028c6ce80 100644 --- a/pkg/infra/tracing/tracing.go +++ b/pkg/infra/tracing/tracing.go @@ -381,6 +381,15 @@ func TraceIDFromContext(ctx context.Context, requireSampled bool) string { return spanCtx.TraceID().String() } +func ServerTimingForSpan(span trace.Span) string { + spanCtx := span.SpanContext() + if !spanCtx.HasTraceID() || !spanCtx.IsValid() { + return "" + } + + return fmt.Sprintf("00-%s-%s-01", spanCtx.TraceID().String(), spanCtx.SpanID().String()) +} + // Error sets the status to error and record the error as an exception in the provided span. func Error(span trace.Span, err error) error { attr := []attribute.KeyValue{} diff --git a/pkg/middleware/request_tracing.go b/pkg/middleware/request_tracing.go index 2985fe0ad60..2f492a697f9 100644 --- a/pkg/middleware/request_tracing.go +++ b/pkg/middleware/request_tracing.go @@ -94,6 +94,14 @@ func RequestTracing(tracer tracing.Tracer) web.Middleware { ), trace.WithSpanKind(trace.SpanKindServer)) defer span.End() + // inject local root span context into the response via server-timing header + // we're doing it this early so that we can capture the root span context + // which is not available later-on. + serverTimingValue := tracing.ServerTimingForSpan(span) + if serverTimingValue != "" { + w.Header().Set("server-timing", fmt.Sprintf("traceparent;desc=\"%s\"", serverTimingValue)) + } + req = req.WithContext(ctx) // Ensure the response writer's status can be captured.