Skip to content

Commit ecf23b8

Browse files
committed
poc(ddtrace/tracer): migrate globalSampleRate
1 parent 6b7054d commit ecf23b8

File tree

3 files changed

+75
-32
lines changed

3 files changed

+75
-32
lines changed

ddtrace/tracer/option.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,6 @@ type config struct {
278278
// Value from DD_DYNAMIC_INSTRUMENTATION_ENABLED, default false.
279279
dynamicInstrumentationEnabled bool
280280

281-
// globalSampleRate holds sample rate read from environment variables.
282-
globalSampleRate float64
283-
284281
// ciVisibilityEnabled controls if the tracer is loaded with CI Visibility mode. default false
285282
ciVisibilityEnabled bool
286283

@@ -292,6 +289,40 @@ type config struct {
292289
}
293290

294291
var (
292+
// globalSampleRate holds sample rate read from environment variables.
293+
globalSampleRate = knobs.Register(&knobs.Definition[float64]{
294+
Default: math.NaN(),
295+
// Out of scope: detecting if both DD_TRACE_SAMPLE_RATE and OTEL_TRACES_SAMPLER are set
296+
// and reporting warning log and telemetry "otel.env.hiding" if so.
297+
EnvVars: []knobs.EnvVar{
298+
{
299+
Key: "DD_TRACE_SAMPLE_RATE",
300+
},
301+
{
302+
Key: "OTEL_TRACES_SAMPLER",
303+
Transform: func(v string) string {
304+
v, err := mapSampleRate(v)
305+
if err != nil {
306+
log.Warn("ignoring OTEL_TRACES_SAMPLER, error: %v", err)
307+
return ""
308+
}
309+
return v
310+
},
311+
},
312+
},
313+
Parse: func(s string) (float64, error) {
314+
sampleRate, err := strconv.ParseFloat(s, 64)
315+
if err != nil {
316+
log.Warn("ignoring DD_TRACE_SAMPLE_RATE, error: %v", err)
317+
return 0.0, knobs.ErrInvalidValue
318+
} else if sampleRate < 0.0 || sampleRate > 1.0 {
319+
log.Warn("ignoring DD_TRACE_SAMPLE_RATE: out of range %f", sampleRate)
320+
return 0.0, knobs.ErrInvalidValue
321+
}
322+
return sampleRate, nil
323+
},
324+
})
325+
295326
// partialFlushEnabled specifices whether the tracer should enable partial flushing. Value
296327
// from DD_TRACE_PARTIAL_FLUSH_ENABLED, default false.
297328
partialFlushEnabled = knobs.Register(&knobs.Definition[bool]{
@@ -351,19 +382,6 @@ func newConfig(opts ...StartOption) *config {
351382
c := new(config)
352383
c.Scope = knobs.NewScope()
353384
c.sampler = NewAllSampler()
354-
sampleRate := math.NaN()
355-
if r := getDDorOtelConfig("sampleRate"); r != "" {
356-
var err error
357-
sampleRate, err = strconv.ParseFloat(r, 64)
358-
if err != nil {
359-
log.Warn("ignoring DD_TRACE_SAMPLE_RATE, error: %v", err)
360-
sampleRate = math.NaN()
361-
} else if sampleRate < 0.0 || sampleRate > 1.0 {
362-
log.Warn("ignoring DD_TRACE_SAMPLE_RATE: out of range %f", sampleRate)
363-
sampleRate = math.NaN()
364-
}
365-
}
366-
c.globalSampleRate = sampleRate
367385
c.httpClientTimeout = time.Second * 10 // 10 seconds
368386

369387
if v := os.Getenv("OTEL_LOGS_EXPORTER"); v != "" {

ddtrace/tracer/sampler_test.go

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/internal"
2121
"gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames"
2222

23+
"github.com/darccio/knobs"
2324
"github.com/stretchr/testify/assert"
2425
"golang.org/x/time/rate"
2526
)
@@ -201,7 +202,8 @@ func TestRuleEnvVars(t *testing.T) {
201202
{in: "1point0", out: math.NaN()}, // default if invalid value
202203
} {
203204
t.Setenv("DD_TRACE_SAMPLE_RATE", tt.in)
204-
res := newConfig().globalSampleRate
205+
c := newConfig()
206+
res := knobs.GetScope(c.Scope, globalSampleRate)
205207
if math.IsNaN(tt.out) {
206208
assert.True(math.IsNaN(res))
207209
} else {
@@ -226,7 +228,9 @@ func TestRuleEnvVars(t *testing.T) {
226228
assert := assert.New(t)
227229
t.Setenv("OTEL_TRACES_SAMPLER", tt.config)
228230
t.Setenv("OTEL_TRACES_SAMPLER_ARG", fmt.Sprintf("%f", tt.rate))
229-
assert.Equal(tt.rate, newConfig().globalSampleRate)
231+
c := newConfig()
232+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
233+
assert.Equal(tt.rate, sampleRate)
230234
})
231235
}
232236
})
@@ -477,7 +481,9 @@ func TestRulesSampler(t *testing.T) {
477481
}
478482
t.Run("no-rules", func(t *testing.T) {
479483
assert := assert.New(t)
480-
rs := newRulesSampler(nil, nil, newConfig().globalSampleRate)
484+
c := newConfig()
485+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
486+
rs := newRulesSampler(nil, nil, sampleRate)
481487

482488
span := makeSpan("http.request", "test-service")
483489
result := rs.SampleTrace(span)
@@ -542,7 +548,9 @@ func TestRulesSampler(t *testing.T) {
542548
assert.Nil(t, err)
543549

544550
assert := assert.New(t)
545-
rs := newRulesSampler(rules, nil, newConfig().globalSampleRate)
551+
c := newConfig()
552+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
553+
rs := newRulesSampler(rules, nil, sampleRate)
546554

547555
span := makeFinishedSpan(tt.spanName, tt.spanSrv, tt.spanRsc, tt.spanTags)
548556

@@ -566,7 +574,9 @@ func TestRulesSampler(t *testing.T) {
566574
for _, v := range traceRules {
567575
t.Run("", func(t *testing.T) {
568576
assert := assert.New(t)
569-
rs := newRulesSampler(v, nil, newConfig().globalSampleRate)
577+
c := newConfig()
578+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
579+
rs := newRulesSampler(v, nil, sampleRate)
570580

571581
span := makeSpan("http.request", "test-service")
572582
result := rs.SampleTrace(span)
@@ -592,7 +602,9 @@ func TestRulesSampler(t *testing.T) {
592602
for _, v := range traceRules {
593603
t.Run("", func(t *testing.T) {
594604
assert := assert.New(t)
595-
rs := newRulesSampler(v, nil, newConfig().globalSampleRate)
605+
c := newConfig()
606+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
607+
rs := newRulesSampler(v, nil, sampleRate)
596608

597609
span := makeSpan("http.request", "test-service")
598610
result := rs.SampleTrace(span)
@@ -638,7 +650,9 @@ func TestRulesSampler(t *testing.T) {
638650
_, rules, err := samplingRulesFromEnv()
639651
assert.Nil(t, err)
640652
assert := assert.New(t)
641-
rs := newRulesSampler(nil, rules, newConfig().globalSampleRate)
653+
c := newConfig()
654+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
655+
rs := newRulesSampler(nil, rules, sampleRate)
642656

643657
span := makeFinishedSpan(tt.spanName, tt.spanSrv, "res-10", map[string]interface{}{"hostname": "hn-30"})
644658

@@ -761,7 +775,8 @@ func TestRulesSampler(t *testing.T) {
761775
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
762776
assert := assert.New(t)
763777
c := newConfig(WithSamplingRules(tt.rules))
764-
rs := newRulesSampler(nil, c.spanRules, newConfig().globalSampleRate)
778+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
779+
rs := newRulesSampler(nil, c.spanRules, sampleRate)
765780

766781
span := makeFinishedSpan(tt.spanName, tt.spanSrv, "res-10", map[string]interface{}{"hostname": "hn-30",
767782
"tag": 20.1,
@@ -829,7 +844,8 @@ func TestRulesSampler(t *testing.T) {
829844
_, rules, _ := samplingRulesFromEnv()
830845

831846
assert := assert.New(t)
832-
sampleRate := newConfig().globalSampleRate
847+
c := newConfig()
848+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
833849
rs := newRulesSampler(nil, rules, sampleRate)
834850

835851
span := makeFinishedSpan(tt.spanName, tt.spanSrv, tt.resName, map[string]interface{}{"hostname": "hn-30"})
@@ -940,7 +956,8 @@ func TestRulesSampler(t *testing.T) {
940956
t.Run("", func(t *testing.T) {
941957
assert := assert.New(t)
942958
c := newConfig(WithSamplingRules(tt.rules))
943-
rs := newRulesSampler(nil, c.spanRules, newConfig().globalSampleRate)
959+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
960+
rs := newRulesSampler(nil, c.spanRules, sampleRate)
944961

945962
span := makeFinishedSpan(tt.spanName, tt.spanSrv, "res-10", map[string]interface{}{"hostname": "hn-30",
946963
"tag": 20.1,
@@ -969,7 +986,9 @@ func TestRulesSampler(t *testing.T) {
969986
t.Run("", func(t *testing.T) {
970987
assert := assert.New(t)
971988
t.Setenv("DD_TRACE_SAMPLE_RATE", fmt.Sprint(rate))
972-
rs := newRulesSampler(nil, rules, newConfig().globalSampleRate)
989+
c := newConfig()
990+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
991+
rs := newRulesSampler(nil, rules, sampleRate)
973992

974993
span := makeSpan("http.request", "test-service")
975994
result := rs.SampleTrace(span)
@@ -1250,7 +1269,9 @@ func TestRulesSamplerInternals(t *testing.T) {
12501269
t.Run("full-rate", func(t *testing.T) {
12511270
assert := assert.New(t)
12521271
now := time.Now()
1253-
rs := newRulesSampler(nil, nil, newConfig().globalSampleRate)
1272+
c := newConfig()
1273+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
1274+
rs := newRulesSampler(nil, nil, sampleRate)
12541275
// set samplingLimiter to specific state
12551276
rs.traces.limiter.prevTime = now.Add(-1 * time.Second)
12561277
rs.traces.limiter.allowed = 1
@@ -1265,7 +1286,9 @@ func TestRulesSamplerInternals(t *testing.T) {
12651286
t.Run("limited-rate", func(t *testing.T) {
12661287
assert := assert.New(t)
12671288
now := time.Now()
1268-
rs := newRulesSampler(nil, nil, newConfig().globalSampleRate)
1289+
c := newConfig()
1290+
sampleRate := knobs.GetScope(c.Scope, globalSampleRate)
1291+
rs := newRulesSampler(nil, nil, sampleRate)
12691292
// force sampling limiter to 1.0 spans/sec
12701293
rs.traces.limiter.limiter = rate.NewLimiter(rate.Limit(1.0), 1)
12711294
rs.traces.limiter.prevTime = now.Add(-1 * time.Second)

ddtrace/tracer/tracer.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434

3535
"github.com/DataDog/datadog-agent/pkg/obfuscate"
3636
"github.com/DataDog/go-runtime-metrics-internal/pkg/runtimemetrics"
37+
"github.com/darccio/knobs"
3738
)
3839

3940
var _ ddtrace.Tracer = (*tracer)(nil)
@@ -267,13 +268,14 @@ func newUnstartedTracer(opts ...StartOption) *tracer {
267268
if spans != nil {
268269
c.spanRules = spans
269270
}
270-
rulesSampler := newRulesSampler(c.traceRules, c.spanRules, c.globalSampleRate)
271-
c.traceSampleRate = newDynamicConfig("trace_sample_rate", c.globalSampleRate, rulesSampler.traces.setGlobalSampleRate, equal[float64])
271+
gsr := knobs.GetScope(c.Scope, globalSampleRate)
272+
rulesSampler := newRulesSampler(c.traceRules, c.spanRules, gsr)
273+
c.traceSampleRate = newDynamicConfig("trace_sample_rate", gsr, rulesSampler.traces.setGlobalSampleRate, equal[float64])
272274
// If globalSampleRate returns NaN, it means the environment variable was not set or valid.
273275
// We could always set the origin to "env_var" inconditionally, but then it wouldn't be possible
274276
// to distinguish between the case where the environment variable was not set and the case where
275277
// it default to NaN.
276-
if !math.IsNaN(c.globalSampleRate) {
278+
if !math.IsNaN(gsr) {
277279
c.traceSampleRate.cfgOrigin = telemetry.OriginEnvVar
278280
}
279281
c.traceSampleRules = newDynamicConfig("trace_sample_rules", c.traceRules,

0 commit comments

Comments
 (0)