Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions ddtrace/opentelemetry/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,21 @@ func (c *otelCtxToDDCtx) SpanID() uint64 {
}

func (c *otelCtxToDDCtx) ForeachBaggageItem(_ func(k, v string) bool) {}

// SamplingDecision returns the sampling decision of the span context.
func (c *otelCtxToDDCtx) SamplingDecision() uint32 {
if c.oc.IsSampled() {
return 2 // decisionKeep
}
return 1 // decisionDrop
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://pkg.go.dev/go.opentelemetry.io/otel/trace#TraceFlags.IsSampled

IsSampled reports whether the sampling bit is set in the TraceFlags.

The logic is fuzzy on this one. I would interpret this to mean: if c.oc.IsSampled() is false, then "we don't know anything about the sampling decision," instead of "the sampling decision is to drop."

}

// Priority returns the sampling priority of the span context.
func (c *otelCtxToDDCtx) Priority() *float64 {
if c.oc.IsSampled() {
p := float64(ext.PriorityAutoKeep)
return &p
}
p := float64(ext.PriorityAutoReject)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above

return &p
}
21 changes: 21 additions & 0 deletions ddtrace/opentelemetry/tracer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,24 @@ func TestMergeOtelDDBaggage(t *testing.T) {
assert.Equal("otelValue", value)
})
}

func Test_DDOpenTelemetryTracer(t *testing.T) {
ddOTelTracer := 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
}),
).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 := ddOTelTracer.Start(ctx, "test")
span.End()

childSpanContext := span.SpanContext()
assert.Equal(t, parentSpanContext.TraceID(), childSpanContext.TraceID())
assert.True(t, childSpanContext.IsSampled(), "parent span is sampled, but child span is not sampled") // this test fails
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you want to delete the comment on this line 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be nice to have additional tests that assert the values of IsSampled have alignment with the values returned by new methods SamplingDecision() and Priority()

}
36 changes: 31 additions & 5 deletions ddtrace/tracer/spancontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,16 @@ type SpanContext struct {
baggageOnly bool // when true, indicates this context only propagates baggage items and should not be used for distributed tracing fields
}

// Private interface for span contexts that can propagate sampling decisions.
type spanContextWithSamplingDecision interface {
SamplingDecision() uint32
Priority() *float64
}

// Private interface for converting v1 span contexts to v2 ones.
type spanContextV1Adapter interface {
SamplingDecision() uint32
spanContextWithSamplingDecision
Origin() string
Priority() *float64
PropagatingTags() map[string]string
Tags() map[string]string
}
Expand All @@ -137,14 +142,35 @@ func FromGenericCtx(c ddtrace.SpanContext) *SpanContext {
sc.baggage[k] = v
return true
})

ctxSpl, ok := c.(spanContextWithSamplingDecision)
if !ok {
return &sc
}

// If the generic context has a sampling decision, set it on the trace
// along with the priority if it exists.
// After setting the sampling decision, lock the trace so the decision is
// respected and avoid re-sampling.
if sDecision := samplingDecision(ctxSpl.SamplingDecision()); sDecision != decisionNone {
sc.trace = newTrace()
sc.trace.samplingDecision = sDecision

if p := ctxSpl.Priority(); p != nil {
sc.setSamplingPriority(int(*p), samplernames.Unknown)
sc.trace.setLocked(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())
if sc.trace == nil {
sc.trace = newTrace()
}
sc.trace.tags = ctx.Tags()
sc.trace.propagatingTags = ctx.PropagatingTags()
return &sc
Expand Down
Loading