From 831161b785a318b5e133b40f776dd6f76818ce0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dario=20Casta=C3=B1=C3=A9?= Date: Wed, 13 Aug 2025 11:15:25 +0200 Subject: [PATCH] feat(ddtrace/tracer): introduce ExtractCtx to support propagated context --- ddtrace/tracer/context.go | 20 ++++++++++++++++++++ ddtrace/tracer/tracer.go | 13 +++++++++++++ internal/active_span_key.go | 3 +++ 3 files changed, 36 insertions(+) diff --git a/ddtrace/tracer/context.go b/ddtrace/tracer/context.go index 75fff356a5..8919c8917d 100644 --- a/ddtrace/tracer/context.go +++ b/ddtrace/tracer/context.go @@ -18,6 +18,11 @@ func ContextWithSpan(ctx context.Context, s *Span) context.Context { return orchestrion.CtxWithValue(ctx, internal.ActiveSpanKey, s) } +// contextWithSpanContext returns a copy of the given context which includes the span context sctx. +func contextWithSpanContext(ctx context.Context, sctx *SpanContext) context.Context { + return orchestrion.CtxWithValue(ctx, internal.ActiveSpanContextKey, sctx) +} + // SpanFromContext returns the span contained in the given context. A second return // value indicates if a span was found in the context. If no span is found, a no-op // span is returned. @@ -35,6 +40,19 @@ func SpanFromContext(ctx context.Context) (*Span, bool) { return nil, false } +// spanContextFromContext returns the span context contained in the given context. A second return +// value indicates if a span context was found in the context. If no span context is found, a nil is returned. +func spanContextFromContext(ctx context.Context) (*SpanContext, bool) { + if ctx == nil { + return nil, false + } + v := orchestrion.WrapContext(ctx).Value(internal.ActiveSpanContextKey) + if sctx, ok := v.(*SpanContext); ok { + return sctx, true + } + return nil, false +} + // StartSpanFromContext returns a new span with the given operation name and options. If a span // is found in the context, it will be used as the parent of the resulting span. If the ChildOf // option is passed, it will only be used as the parent if there is no span found in `ctx`. @@ -47,6 +65,8 @@ func StartSpanFromContext(ctx context.Context, operationName string, opts ...Sta ctx = context.Background() } else if s, ok := SpanFromContext(ctx); ok { optsLocal = append(optsLocal, ChildOf(s.Context())) + } else if sctx, ok := spanContextFromContext(ctx); ok { + optsLocal = append(optsLocal, ChildOf(sctx)) } optsLocal = append(optsLocal, withContext(ctx)) s := StartSpan(operationName, optsLocal...) diff --git a/ddtrace/tracer/tracer.go b/ddtrace/tracer/tracer.go index a95e7419e0..104a2847a3 100644 --- a/ddtrace/tracer/tracer.go +++ b/ddtrace/tracer/tracer.go @@ -312,6 +312,19 @@ func Extract(carrier interface{}) (*SpanContext, error) { return getGlobalTracer().Extract(carrier) } +// ExtractCtx extracts a span context from the carrier and returns a copy of +// the given context which includes the span context. +func ExtractCtx(ctx gocontext.Context, carrier interface{}) (gocontext.Context, error) { + sctx, err := Extract(carrier) + if err != nil { + return nil, err + } + if sctx == nil { + return ctx, nil + } + return contextWithSpanContext(ctx, sctx), nil +} + // Inject injects the given SpanContext into the carrier. The carrier is // expected to implement TextMapWriter, otherwise an error is returned. // If the tracer is not started, calling this function is a no-op. diff --git a/internal/active_span_key.go b/internal/active_span_key.go index 090150a587..68396d009c 100644 --- a/internal/active_span_key.go +++ b/internal/active_span_key.go @@ -9,3 +9,6 @@ type contextKey struct{} // ActiveSpanKey is used to set tracer context on a context.Context objects with a unique key var ActiveSpanKey = contextKey{} + +// ActiveSpanContextKey is used to set span context on a context.Context objects with a unique key +var ActiveSpanContextKey = contextKey{}