diff --git a/ddtrace/opentelemetry/tracer.go b/ddtrace/opentelemetry/tracer.go index e813fc7d93..9aeaf1ca0b 100644 --- a/ddtrace/opentelemetry/tracer.go +++ b/ddtrace/opentelemetry/tracer.go @@ -165,3 +165,7 @@ func (c *otelCtxToDDCtx) SpanID() uint64 { } func (c *otelCtxToDDCtx) ForeachBaggageItem(_ func(k, v string) bool) {} + +func (c *otelCtxToDDCtx) IsSampled() bool { + return c.oc.IsSampled() +} diff --git a/ddtrace/opentelemetry/tracer_test.go b/ddtrace/opentelemetry/tracer_test.go index 98de1eb5f2..cbf90b5cdf 100644 --- a/ddtrace/opentelemetry/tracer_test.go +++ b/ddtrace/opentelemetry/tracer_test.go @@ -398,3 +398,28 @@ func TestMergeOtelDDBaggage(t *testing.T) { assert.Equal("otelValue", value) }) } + +func TestSamplingDecision(t *testing.T) { + assert := assert.New(t) + tp := NewTracerProvider( + tracer.WithSamplingRules([]tracer.SamplingRule{ + {Rate: 0}, // This should be applied only when a brand new root span is started and should be ignored for a non-root span + }), + ) + defer tp.Shutdown() + otel.SetTracerProvider(tp) + tr := otel.Tracer("") + + parentSpanContext := oteltrace.NewSpanContext(oteltrace.SpanContextConfig{ + TraceID: oteltrace.TraceID{0xAA}, + SpanID: oteltrace.SpanID{0x01}, + TraceFlags: oteltrace.FlagsSampled, // the parent span is sampled, so its child spans should be sampled too + }) + ctx := oteltrace.ContextWithSpanContext(context.Background(), parentSpanContext) + _, span := tr.Start(ctx, "test") + span.End() + + childSpanContext := span.SpanContext() + assert.Equal(parentSpanContext.TraceID(), childSpanContext.TraceID()) + assert.True(childSpanContext.IsSampled(), "parent span is sampled, but child span is not sampled") +} diff --git a/ddtrace/tracer/spancontext.go b/ddtrace/tracer/spancontext.go index b9fe3d5b33..af075402f7 100644 --- a/ddtrace/tracer/spancontext.go +++ b/ddtrace/tracer/spancontext.go @@ -126,6 +126,10 @@ type spanContextV1Adapter interface { Tags() map[string]string } +type otelContextAdapter interface { + IsSampled() bool +} + // FromGenericCtx converts a ddtrace.SpanContext to a *SpanContext, which can be used // to start child spans. func FromGenericCtx(c ddtrace.SpanContext) *SpanContext { @@ -138,16 +142,21 @@ func FromGenericCtx(c ddtrace.SpanContext) *SpanContext { sc.baggage[k] = v return true }) - ctx, ok := c.(spanContextV1Adapter) - if !ok { - return &sc - } - sc.origin = ctx.Origin() - sc.trace = newTrace() - sc.trace.priority = ctx.Priority() - sc.trace.samplingDecision = samplingDecision(ctx.SamplingDecision()) - sc.trace.tags = ctx.Tags() - sc.trace.propagatingTags = ctx.PropagatingTags() + switch ctx := c.(type) { + case spanContextV1Adapter: + sc.origin = ctx.Origin() + sc.trace = newTrace() + sc.trace.priority = ctx.Priority() + sc.trace.samplingDecision = samplingDecision(ctx.SamplingDecision()) + sc.trace.tags = ctx.Tags() + sc.trace.propagatingTags = ctx.PropagatingTags() + case otelContextAdapter: + sc.trace = newTrace() + if ctx.IsSampled() { + sc.setSamplingPriority(1, samplernames.Default) + sc.trace.keep() + } + } return &sc }