Skip to content

Commit 5655505

Browse files
committed
fix(ddtrace/tracer): wire version on aggregation key when adding a span to client-side stats
1 parent 2b2b2c2 commit 5655505

2 files changed

Lines changed: 85 additions & 3 deletions

File tree

ddtrace/tracer/stats.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type concentrator struct {
6767
type tracerStatSpan struct {
6868
statSpan *stats.StatSpan
6969
origin string
70+
version string // per-span version tag; "" means use global aggKey version
7071
}
7172

7273
// newConcentrator creates a new concentrator using the given tracer
@@ -197,10 +198,10 @@ func (c *concentrator) newTracerStatSpan(s *Span, obfuscator *obfuscate.Obfuscat
197198
if !ok {
198199
return nil, false
199200
}
200-
origin := s.meta[keyOrigin]
201201
return &tracerStatSpan{
202202
statSpan: statSpan,
203-
origin: origin,
203+
origin: s.meta[keyOrigin],
204+
version: s.meta[ext.Version],
204205
}, true
205206
}
206207

@@ -212,7 +213,11 @@ func (c *concentrator) shouldObfuscate() bool {
212213

213214
// add s into the concentrator's internal stats buckets.
214215
func (c *concentrator) add(s *tracerStatSpan) {
215-
c.spanConcentrator.AddSpan(s.statSpan, c.aggregationKey, "", nil, s.origin)
216+
aggKey := c.aggregationKey
217+
if s.version != "" {
218+
aggKey.Version = s.version
219+
}
220+
c.spanConcentrator.AddSpan(s.statSpan, aggKey, "", nil, s.origin)
216221
}
217222

218223
// Stop stops the concentrator and blocks until the operation completes.

ddtrace/tracer/stats_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,83 @@ func TestConcentratorDefaultEnv(t *testing.T) {
315315
})
316316
}
317317

318+
func TestPerSpanVersionInStats(t *testing.T) {
319+
bucketSize := int64(500_000)
320+
makeSpan := func(version string) *Span {
321+
s := &Span{
322+
name: "http.request",
323+
start: time.Now().UnixNano() + 3*bucketSize,
324+
duration: 1,
325+
metrics: map[string]float64{keyMeasured: 1},
326+
}
327+
if version != "" {
328+
s.meta = map[string]string{ext.Version: version}
329+
}
330+
return s
331+
}
332+
333+
t.Run("per-span version propagates to stats payload", func(t *testing.T) {
334+
spanVersion := "synthtracer-20250501120000"
335+
transport := newDummyTransport()
336+
c := newConcentrator(newTestConfigWithTransport(t, transport), bucketSize, &statsd.NoOpClientDirect{})
337+
338+
s := makeSpan(spanVersion)
339+
ss, ok := c.newTracerStatSpan(s, nil)
340+
require.True(t, ok)
341+
c.Start()
342+
c.In <- ss
343+
c.Stop()
344+
345+
got := transport.Stats()
346+
require.Len(t, got, 1)
347+
assert.Equal(t, spanVersion, got[0].Version,
348+
"per-span version tag must be used when no global version is configured")
349+
})
350+
351+
t.Run("falls back to global config version when span has no version tag", func(t *testing.T) {
352+
transport := newDummyTransport()
353+
cfg, err := newTestConfig(withNoopInfoHTTPClient(), func(c *config) {
354+
c.ddTransport = transport
355+
c.internalConfig.SetVersion("global-v1.2.3", internalconfig.OriginCode)
356+
})
357+
require.NoError(t, err)
358+
c := newConcentrator(cfg, bucketSize, &statsd.NoOpClientDirect{})
359+
360+
s := makeSpan("")
361+
ss, ok := c.newTracerStatSpan(s, nil)
362+
require.True(t, ok)
363+
c.Start()
364+
c.In <- ss
365+
c.Stop()
366+
367+
got := transport.Stats()
368+
require.Len(t, got, 1)
369+
assert.Equal(t, "global-v1.2.3", got[0].Version)
370+
})
371+
372+
t.Run("two spans with different versions produce separate payloads", func(t *testing.T) {
373+
transport := newDummyTransport()
374+
c := newConcentrator(newTestConfigWithTransport(t, transport), bucketSize, &statsd.NoOpClientDirect{})
375+
376+
s1 := makeSpan("v-timestamp-1")
377+
s2 := makeSpan("v-timestamp-2")
378+
ss1, ok := c.newTracerStatSpan(s1, nil)
379+
require.True(t, ok)
380+
ss2, ok := c.newTracerStatSpan(s2, nil)
381+
require.True(t, ok)
382+
c.Start()
383+
c.In <- ss1
384+
c.In <- ss2
385+
c.Stop()
386+
387+
got := transport.Stats()
388+
require.Len(t, got, 2)
389+
versions := map[string]struct{}{got[0].Version: {}, got[1].Version: {}}
390+
assert.Contains(t, versions, "v-timestamp-1")
391+
assert.Contains(t, versions, "v-timestamp-2")
392+
})
393+
}
394+
318395
func TestStatsIncludeHTTPMethodAndEndpoint(t *testing.T) {
319396
uniqueMethod := "POST"
320397
uniqueEndpoint := "/__unique_endpoint__"

0 commit comments

Comments
 (0)