diff --git a/ddtrace/tracer/log.go b/ddtrace/tracer/log.go index 5b6e056f28..d55bd8ce33 100644 --- a/ddtrace/tracer/log.go +++ b/ddtrace/tracer/log.go @@ -19,6 +19,8 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/osinfo" "gopkg.in/DataDog/dd-trace-go.v1/internal/version" + + "github.com/darccio/knobs" ) // startupInfo contains various information about the status of the tracer on startup. @@ -141,8 +143,8 @@ func logStartup(t *tracer) { AgentFeatures: t.config.agent, Integrations: t.config.integrations, AppSec: appsec.Enabled(), - PartialFlushEnabled: t.config.partialFlushEnabled, - PartialFlushMinSpans: t.config.partialFlushMinSpans, + PartialFlushEnabled: knobs.GetScope(t.config.Scope, partialFlushEnabled), + PartialFlushMinSpans: knobs.GetScope(t.config.Scope, partialFlushMinSpans), Orchestrion: t.config.orchestrionCfg, FeatureFlags: featureFlags, PropagationStyleInject: injectorNames, diff --git a/ddtrace/tracer/option.go b/ddtrace/tracer/option.go index dbffa546df..63e93eeaaa 100644 --- a/ddtrace/tracer/option.go +++ b/ddtrace/tracer/option.go @@ -37,6 +37,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/internal/version" "github.com/DataDog/datadog-go/v5/statsd" + "github.com/darccio/knobs" ) var contribIntegrations = map[string]struct { @@ -115,6 +116,8 @@ var ( // config holds the tracer configuration. type config struct { + *knobs.Scope + // debug, when true, writes details to logs. debug bool @@ -252,15 +255,6 @@ type config struct { // misconfiguration spanTimeout time.Duration - // partialFlushMinSpans is the number of finished spans in a single trace to trigger a - // partial flush, or 0 if partial flushing is disabled. - // Value from DD_TRACE_PARTIAL_FLUSH_MIN_SPANS, default 1000. - partialFlushMinSpans int - - // partialFlushEnabled specifices whether the tracer should enable partial flushing. Value - // from DD_TRACE_PARTIAL_FLUSH_ENABLED, default false. - partialFlushEnabled bool - // statsComputationEnabled enables client-side stats computation (aka trace metrics). statsComputationEnabled bool @@ -297,6 +291,39 @@ type config struct { logDirectory string } +var ( + // partialFlushEnabled specifices whether the tracer should enable partial flushing. Value + // from DD_TRACE_PARTIAL_FLUSH_ENABLED, default false. + partialFlushEnabled = knobs.Register(&knobs.Definition[bool]{ + Default: false, + EnvVars: []knobs.EnvVar{{Key: "DD_TRACE_PARTIAL_FLUSH_ENABLED"}}, + Parse: knobs.ToBool, + }) + + // partialFlushMinSpans is the number of finished spans in a single trace to trigger a + // partial flush, or 0 if partial flushing is disabled. + // Value from DD_TRACE_PARTIAL_FLUSH_MIN_SPANS, default 1000. + partialFlushMinSpans = knobs.Register(&knobs.Definition[int]{ + // TODO(partialFlush): consider logging a warning if DD_TRACE_PARTIAL_FLUSH_MIN_SPANS + // is set, but DD_TRACE_PARTIAL_FLUSH_ENABLED is not true. Or just assume it should be enabled + // if it's explicitly set, and don't require both variables to be configured. + Default: 1000, + EnvVars: []knobs.EnvVar{{Key: "DD_TRACE_PARTIAL_FLUSH_MIN_SPANS"}}, + Requires: []any{partialFlushEnabled}, + Parse: func(v string) (int, error) { + i, _ := strconv.Atoi(v) + if i <= 0 { + log.Warn("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS=%d is not a valid value, setting to default %d", i, 1000) + return 0, knobs.ErrInvalidValue + } else if i >= traceMaxSize { + log.Warn("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS=%d is above the max number of spans that can be kept in memory for a single trace (%d spans), so partial flushing will never trigger, setting to default %d", i, traceMaxSize, 1000) + return 0, knobs.ErrInvalidValue + } + return i, nil + }, + }) +) + // orchestrionConfig contains Orchestrion configuration. type orchestrionConfig struct { // Enabled indicates whether this tracer was instanciated via Orchestrion. @@ -318,13 +345,11 @@ type StartOption func(*config) // maxPropagatedTagsLength limits the size of DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH to prevent HTTP 413 responses. const maxPropagatedTagsLength = 512 -// partialFlushMinSpansDefault is the default number of spans for partial flushing, if enabled. -const partialFlushMinSpansDefault = 1000 - // newConfig renders the tracer configuration based on defaults, environment variables // and passed user opts. func newConfig(opts ...StartOption) *config { c := new(config) + c.Scope = knobs.NewScope() c.sampler = NewAllSampler() sampleRate := math.NaN() if r := getDDorOtelConfig("sampleRate"); r != "" { @@ -419,19 +444,6 @@ func newConfig(opts ...StartOption) *config { } c.statsComputationEnabled = internal.BoolEnv("DD_TRACE_STATS_COMPUTATION_ENABLED", false) c.dataStreamsMonitoringEnabled = internal.BoolEnv("DD_DATA_STREAMS_ENABLED", false) - c.partialFlushEnabled = internal.BoolEnv("DD_TRACE_PARTIAL_FLUSH_ENABLED", false) - c.partialFlushMinSpans = internal.IntEnv("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS", partialFlushMinSpansDefault) - if c.partialFlushMinSpans <= 0 { - log.Warn("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS=%d is not a valid value, setting to default %d", c.partialFlushMinSpans, partialFlushMinSpansDefault) - c.partialFlushMinSpans = partialFlushMinSpansDefault - } else if c.partialFlushMinSpans >= traceMaxSize { - log.Warn("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS=%d is above the max number of spans that can be kept in memory for a single trace (%d spans), so partial flushing will never trigger, setting to default %d", c.partialFlushMinSpans, traceMaxSize, partialFlushMinSpansDefault) - c.partialFlushMinSpans = partialFlushMinSpansDefault - } - // TODO(partialFlush): consider logging a warning if DD_TRACE_PARTIAL_FLUSH_MIN_SPANS - // is set, but DD_TRACE_PARTIAL_FLUSH_ENABLED is not true. Or just assume it should be enabled - // if it's explicitly set, and don't require both variables to be configured. - c.dynamicInstrumentationEnabled = internal.BoolEnv("DD_DYNAMIC_INSTRUMENTATION_ENABLED", false) schemaVersionStr := os.Getenv("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA") @@ -1192,8 +1204,8 @@ func WithDebugSpansMode(timeout time.Duration) StartOption { // is disabled by default. func WithPartialFlushing(numSpans int) StartOption { return func(c *config) { - c.partialFlushEnabled = true - c.partialFlushMinSpans = numSpans + knobs.SetScope(c.Scope, partialFlushEnabled, knobs.Code, true) + knobs.SetScope(c.Scope, partialFlushMinSpans, knobs.Code, numSpans) } } diff --git a/ddtrace/tracer/option_test.go b/ddtrace/tracer/option_test.go index d915cb45b7..35a150d058 100644 --- a/ddtrace/tracer/option_test.go +++ b/ddtrace/tracer/option_test.go @@ -34,6 +34,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof" "gopkg.in/DataDog/dd-trace-go.v1/internal/version" + "github.com/darccio/knobs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -1506,46 +1507,46 @@ func TestHostnameDisabled(t *testing.T) { func TestPartialFlushing(t *testing.T) { t.Run("None", func(t *testing.T) { c := newConfig() - assert.False(t, c.partialFlushEnabled) - assert.Equal(t, partialFlushMinSpansDefault, c.partialFlushMinSpans) + assert.False(t, knobs.GetScope(c.Scope, partialFlushEnabled)) + assert.Equal(t, 1000, knobs.GetScope(c.Scope, partialFlushMinSpans)) }) t.Run("Disabled-DefaultMinSpans", func(t *testing.T) { t.Setenv("DD_TRACE_PARTIAL_FLUSH_ENABLED", "false") c := newConfig() - assert.False(t, c.partialFlushEnabled) - assert.Equal(t, partialFlushMinSpansDefault, c.partialFlushMinSpans) + assert.False(t, knobs.GetScope(c.Scope, partialFlushEnabled)) + assert.Equal(t, 1000, knobs.GetScope(c.Scope, partialFlushMinSpans)) }) t.Run("Default-SetMinSpans", func(t *testing.T) { t.Setenv("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS", "10") c := newConfig() - assert.False(t, c.partialFlushEnabled) - assert.Equal(t, 10, c.partialFlushMinSpans) + assert.False(t, knobs.GetScope(c.Scope, partialFlushEnabled)) + assert.Equal(t, 10, knobs.GetScope(c.Scope, partialFlushMinSpans)) }) t.Run("Enabled-DefaultMinSpans", func(t *testing.T) { t.Setenv("DD_TRACE_PARTIAL_FLUSH_ENABLED", "true") c := newConfig() - assert.True(t, c.partialFlushEnabled) - assert.Equal(t, partialFlushMinSpansDefault, c.partialFlushMinSpans) + assert.True(t, knobs.GetScope(c.Scope, partialFlushEnabled)) + assert.Equal(t, 1000, knobs.GetScope(c.Scope, partialFlushMinSpans)) }) t.Run("Enabled-SetMinSpans", func(t *testing.T) { t.Setenv("DD_TRACE_PARTIAL_FLUSH_ENABLED", "true") t.Setenv("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS", "10") c := newConfig() - assert.True(t, c.partialFlushEnabled) - assert.Equal(t, 10, c.partialFlushMinSpans) + assert.True(t, knobs.GetScope(c.Scope, partialFlushEnabled)) + assert.Equal(t, 10, knobs.GetScope(c.Scope, partialFlushMinSpans)) }) t.Run("Enabled-SetMinSpansNegative", func(t *testing.T) { t.Setenv("DD_TRACE_PARTIAL_FLUSH_ENABLED", "true") t.Setenv("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS", "-1") c := newConfig() - assert.True(t, c.partialFlushEnabled) - assert.Equal(t, partialFlushMinSpansDefault, c.partialFlushMinSpans) + assert.True(t, knobs.GetScope(c.Scope, partialFlushEnabled)) + assert.Equal(t, 1000, knobs.GetScope(c.Scope, partialFlushMinSpans)) }) t.Run("WithPartialFlushOption", func(t *testing.T) { c := newConfig() WithPartialFlushing(20)(c) - assert.True(t, c.partialFlushEnabled) - assert.Equal(t, 20, c.partialFlushMinSpans) + assert.True(t, knobs.GetScope(c.Scope, partialFlushEnabled)) + assert.Equal(t, 20, knobs.GetScope(c.Scope, partialFlushMinSpans)) }) } diff --git a/ddtrace/tracer/spancontext.go b/ddtrace/tracer/spancontext.go index 30a791ee8e..9b0b68d991 100644 --- a/ddtrace/tracer/spancontext.go +++ b/ddtrace/tracer/spancontext.go @@ -23,6 +23,8 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames" "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" + + "github.com/darccio/knobs" ) var _ ddtrace.SpanContext = (*spanContext)(nil) @@ -499,7 +501,7 @@ func (t *trace) finishedOne(s *span) { return } - doPartialFlush := tr.config.partialFlushEnabled && t.finished >= tr.config.partialFlushMinSpans + doPartialFlush := knobs.GetScope(tr.config.Scope, partialFlushEnabled) && t.finished >= knobs.GetScope(tr.config.Scope, partialFlushMinSpans) if !doPartialFlush { return // The trace hasn't completed and partial flushing will not occur }