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 } diff --git a/go.mod b/go.mod index 6696dcc4cc..83296dcc92 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20230611145640-acc696258285 github.com/confluentinc/confluent-kafka-go v1.9.2 github.com/confluentinc/confluent-kafka-go/v2 v2.2.0 + github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c github.com/denisenkom/go-mssqldb v0.11.0 github.com/dimfeld/httptreemux/v5 v5.5.0 github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 diff --git a/go.sum b/go.sum index b05429f344..d454e3af61 100644 --- a/go.sum +++ b/go.sum @@ -1032,6 +1032,8 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c h1:3h9o1zuYKNAyi5vpoZlFmmckCXsT+w/VB/PWDtz20EA= +github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c/go.mod h1:nJ3ntDIyIQgqzTWF2QiL7eRNTy6iZJbBot3WcB+SymI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= diff --git a/internal/apps/go.mod b/internal/apps/go.mod index 9ce5485a85..196f23c32a 100644 --- a/internal/apps/go.mod +++ b/internal/apps/go.mod @@ -18,6 +18,7 @@ require ( github.com/DataDog/go-sqllexer v0.0.14 // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.20.0 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect + github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect github.com/ebitengine/purego v0.6.0-alpha.5 // indirect diff --git a/internal/apps/go.sum b/internal/apps/go.sum index a591990f7b..e6fd4dcfaa 100644 --- a/internal/apps/go.sum +++ b/internal/apps/go.sum @@ -40,6 +40,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c h1:3h9o1zuYKNAyi5vpoZlFmmckCXsT+w/VB/PWDtz20EA= +github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c/go.mod h1:nJ3ntDIyIQgqzTWF2QiL7eRNTy6iZJbBot3WcB+SymI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= diff --git a/internal/exectracetest/go.mod b/internal/exectracetest/go.mod index 0bc1db3393..a8969ff06d 100644 --- a/internal/exectracetest/go.mod +++ b/internal/exectracetest/go.mod @@ -27,6 +27,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect + github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect github.com/ebitengine/purego v0.6.0-alpha.5 // indirect diff --git a/internal/exectracetest/go.sum b/internal/exectracetest/go.sum index 0d8bd345c4..1b1c0e0063 100644 --- a/internal/exectracetest/go.sum +++ b/internal/exectracetest/go.sum @@ -40,6 +40,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c h1:3h9o1zuYKNAyi5vpoZlFmmckCXsT+w/VB/PWDtz20EA= +github.com/darccio/knobs v0.0.0-20241211164423-8ba351cb0c0c/go.mod h1:nJ3ntDIyIQgqzTWF2QiL7eRNTy6iZJbBot3WcB+SymI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=