diff --git a/ddtrace/tracer/tracer.go b/ddtrace/tracer/tracer.go index 6b4437d7b0..cb29718e5b 100644 --- a/ddtrace/tracer/tracer.go +++ b/ddtrace/tracer/tracer.go @@ -334,25 +334,24 @@ func (t *tracer) sampleFinishedTrace(info *finishedTrace) { if info.decision == decisionKeep { return } - if !t.rulesSampling.HasSpanRules() { - info.spans = nil - return - } - // if trace sampling decision is drop, we still want to send single spans - // unless there are no single span sampling rules defined var kept []*span - for _, span := range info.spans { - if t.rulesSampling.SampleSpan(span) { - kept = append(kept, span) + if t.rulesSampling.HasSpanRules() { + // Apply sampling rules to individual spans in the trace. + for _, span := range info.spans { + if t.rulesSampling.SampleSpan(span) { + kept = append(kept, span) + } + } + if len(kept) > 0 && len(kept) < len(info.spans) { + // Some spans in the trace were kept, so a partial trace will be sent. + atomic.AddUint64(&t.partialTraces, 1) } } - atomic.AddUint64(&t.droppedP0Spans, uint64(len(info.spans)-len(kept))) - info.spans = kept if len(kept) == 0 { atomic.AddUint64(&t.droppedP0Traces, 1) - return // no spans matched the rules and were sampled } - atomic.AddUint64(&t.partialTraces, 1) + atomic.AddUint64(&t.droppedP0Spans, uint64(len(info.spans)-len(kept))) + info.spans = kept } func (t *tracer) pushTrace(trace *finishedTrace) { diff --git a/ddtrace/tracer/tracer_test.go b/ddtrace/tracer/tracer_test.go index 85e3b18e76..01e4cb3207 100644 --- a/ddtrace/tracer/tracer_test.go +++ b/ddtrace/tracer/tracer_test.go @@ -351,6 +351,11 @@ func TestSamplingDecision(t *testing.T) { t.Run("client_dropped", func(t *testing.T) { tracer, _, _, stop := startTestTracer(t) + defer func() { + // Must check these after tracer is stopped to avoid flakiness + assert.Equal(t, uint64(1), tracer.droppedP0Traces) + assert.Equal(t, uint64(2), tracer.droppedP0Spans) + }() defer stop() tracer.config.agent.DropP0s = true tracer.config.sampler = NewRateSampler(0)