Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ddtrace/tracer/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func logStartup(t *tracer) {
Env: t.config.env,
Service: t.config.serviceName,
AgentURL: agentURL,
Debug: t.config.debug,
Debug: t.config.internalConfig.Debug(),
AnalyticsEnabled: !math.IsNaN(globalconfig.AnalyticsRate()),
SampleRate: fmt.Sprintf("%f", t.rulesSampling.traces.globalRate),
SampleRateLimit: "disabled",
Expand All @@ -155,7 +155,7 @@ func logStartup(t *tracer) {
FeatureFlags: featureFlags,
PropagationStyleInject: injectorNames,
PropagationStyleExtract: extractorNames,
TracingAsTransport: t.config.tracingAsTransport,
TracingAsTransport: t.config.internalConfig.TracingAsTransport(),
DogstatsdAddr: t.config.dogstatsdAddr,
DataStreamsEnabled: t.config.dataStreamsMonitoringEnabled,
}
Expand Down
17 changes: 6 additions & 11 deletions ddtrace/tracer/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ const (

// config holds the tracer configuration.
type config struct {
// debug, when true, writes details to logs.
debug bool
// internalConfig holds a reference to the global configuration singleton.
internalConfig *internalconfig.Config

// appsecStartOptions controls the options used when starting appsec features.
appsecStartOptions []appsecconfig.StartOption
Expand Down Expand Up @@ -328,9 +328,6 @@ type config struct {
// logDirectory is directory for tracer logs specified by user-setting DD_TRACE_LOG_DIRECTORY. default empty/unused
logDirectory string

// tracingAsTransport specifies whether the tracer is running in transport-only mode, where traces are only sent when other products request it.
tracingAsTransport bool

// traceRateLimitPerSecond specifies the rate limit for traces.
traceRateLimitPerSecond float64

Expand Down Expand Up @@ -379,7 +376,7 @@ const partialFlushMinSpansDefault = 1000
// and passed user opts.
func newConfig(opts ...StartOption) (*config, error) {
c := new(config)
internalConfig := internalconfig.Get()
c.internalConfig = internalconfig.Get()

// If this was built with a recent-enough version of Orchestrion, force the orchestrion config to
// the baked-in values. We do this early so that opts can be used to override the baked-in values,
Expand Down Expand Up @@ -482,7 +479,6 @@ func newConfig(opts ...StartOption) (*config, error) {
c.logStartup = internal.BoolEnv("DD_TRACE_STARTUP_LOGS", true)
c.runtimeMetrics = internal.BoolVal(getDDorOtelConfig("metrics"), false)
c.runtimeMetricsV2 = internal.BoolEnv("DD_RUNTIME_METRICS_V2_ENABLED", true)
c.debug = internalConfig.Debug()
c.logDirectory = env.Get("DD_TRACE_LOG_DIRECTORY")
c.enabled = newDynamicConfig("tracing_enabled", internal.BoolVal(getDDorOtelConfig("enabled"), true), func(_ bool) bool { return true }, equal[bool])
if _, ok := env.Lookup("DD_TRACE_ENABLED"); ok {
Expand Down Expand Up @@ -613,7 +609,7 @@ func newConfig(opts ...StartOption) (*config, error) {
if c.logger != nil {
log.UseLogger(c.logger)
}
if c.debug {
if c.internalConfig.Debug() {
log.SetLevel(log.LevelDebug)
}
// Check if CI Visibility mode is enabled
Expand Down Expand Up @@ -692,7 +688,7 @@ func apmTracingDisabled(c *config) {
// to let the backend know that it needs to keep APM UI disabled.
c.globalSampleRate = 1.0
c.traceRateLimitPerSecond = 1.0 / 60
c.tracingAsTransport = true
c.internalConfig.SetTracingAsTransport(true)
WithGlobalTag("_dd.apm.enabled", 0)(c)
// Disable runtime metrics. In `tracingAsTransport` mode, we'll still
// tell the agent we computed them, so it doesn't do it either.
Expand Down Expand Up @@ -1009,8 +1005,7 @@ func WithDebugStack(enabled bool) StartOption {
// WithDebugMode enables debug mode on the tracer, resulting in more verbose logging.
func WithDebugMode(enabled bool) StartOption {
return func(c *config) {
telemetry.RegisterAppConfig("trace_debug_enabled", enabled, telemetry.OriginCode)
c.debug = enabled
c.internalConfig.SetDebug(enabled, telemetry.OriginCode)
}
}

Expand Down
24 changes: 9 additions & 15 deletions ddtrace/tracer/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (

"github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
"github.com/DataDog/dd-trace-go/v2/internal"
internalconfig "github.com/DataDog/dd-trace-go/v2/internal/config"
"github.com/DataDog/dd-trace-go/v2/internal/globalconfig"
"github.com/DataDog/dd-trace-go/v2/internal/log"
"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
Expand Down Expand Up @@ -375,7 +374,7 @@ func TestTracerOptionsDefaults(t *testing.T) {
assert.Equal(x.Timeout, y.Timeout)
compareHTTPClients(t, x, y)
assert.True(getFuncName(x.Transport.(*http.Transport).DialContext) == getFuncName(internal.DefaultDialer(30*time.Second).DialContext))
assert.False(c.debug)
assert.False(c.internalConfig.Debug())
})

t.Run("http-client", func(t *testing.T) {
Expand Down Expand Up @@ -430,48 +429,43 @@ func TestTracerOptionsDefaults(t *testing.T) {
assert.NoError(t, err)
defer tracer.Stop()
c := tracer.config
assert.True(t, c.debug)
assert.True(t, c.internalConfig.Debug())
})
t.Run("env", func(t *testing.T) {
t.Setenv("DD_TRACE_DEBUG", "true")
internalconfig.ResetForTesting()
c, err := newTestConfig()
assert.NoError(t, err)
assert.True(t, c.debug)
assert.True(t, c.internalConfig.Debug())
})
t.Run("otel-env-debug", func(t *testing.T) {
t.Setenv("OTEL_LOG_LEVEL", "debug")
internalconfig.ResetForTesting()
c, err := newTestConfig()
assert.NoError(t, err)
assert.True(t, c.debug)
assert.True(t, c.internalConfig.Debug())
})
t.Run("otel-env-notdebug", func(t *testing.T) {
// any value other than debug, does nothing
t.Setenv("OTEL_LOG_LEVEL", "notdebug")
internalconfig.ResetForTesting()
c, err := newTestConfig()
assert.NoError(t, err)
assert.False(t, c.debug)
assert.False(t, c.internalConfig.Debug())
})
t.Run("override-chain", func(t *testing.T) {
assert := assert.New(t)
// option override otel
t.Setenv("OTEL_LOG_LEVEL", "debug")
internalconfig.ResetForTesting()
c, err := newTestConfig(WithDebugMode(false))
assert.NoError(err)
assert.False(c.debug)
assert.False(c.internalConfig.Debug())
// env override otel
t.Setenv("DD_TRACE_DEBUG", "false")
internalconfig.ResetForTesting()
c, err = newTestConfig()
assert.NoError(err)
assert.False(c.debug)
assert.False(c.internalConfig.Debug())
// option override env
c, err = newTestConfig(WithDebugMode(true))
assert.NoError(err)
assert.True(c.debug)
assert.True(c.internalConfig.Debug())
})
})

Expand Down Expand Up @@ -745,7 +739,7 @@ func TestTracerOptionsDefaults(t *testing.T) {
assert.NotNil(c.globalTags.get())
assert.Equal("v", c.globalTags.get()["k"])
assert.Equal("testEnv", c.env)
assert.True(c.debug)
assert.True(c.internalConfig.Debug())
})

t.Run("env-tags", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/tracer/telemetry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func TestTelemetryEnabled(t *testing.T) {
defer globalconfig.SetServiceName("")
defer Stop()

telemetrytest.CheckConfig(t, telemetryClient.Configuration, "trace_debug_enabled", true)
telemetrytest.CheckConfig(t, telemetryClient.Configuration, "DD_TRACE_DEBUG", true)
telemetrytest.CheckConfig(t, telemetryClient.Configuration, "service", "test-serv")
telemetrytest.CheckConfig(t, telemetryClient.Configuration, "env", "test-env")
telemetrytest.CheckConfig(t, telemetryClient.Configuration, "runtime_metrics_enabled", true)
Expand Down
8 changes: 4 additions & 4 deletions ddtrace/tracer/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ func (t *tracer) worker(tick <-chan time.Time) {
t.statsd.Incr("datadog.tracer.flush_triggered", []string{"reason:invoked"}, 1)
t.traceWriter.flush()
t.statsd.Flush()
if !t.config.tracingAsTransport {
if !t.config.internalConfig.TracingAsTransport() {
t.stats.flushAndSend(time.Now(), withCurrentBucket)
}
// TODO(x): In reality, the traceWriter.flush() call is not synchronous
Expand Down Expand Up @@ -905,7 +905,7 @@ func (t *tracer) Inject(ctx *SpanContext, carrier interface{}) error {
return nil
}

if t.config.tracingAsTransport {
if t.config.internalConfig.TracingAsTransport() {
// in tracing as transport mode, only propagate when there is an upstream appsec event
if ctx.trace != nil &&
!globalinternal.VerifyTraceSourceEnabled(ctx.trace.propagatingTag(keyPropagatedTraceSource), globalinternal.ASMTraceSource) {
Expand Down Expand Up @@ -952,7 +952,7 @@ func (t *tracer) Extract(carrier interface{}) (*SpanContext, error) {
return nil, nil
}
ctx, err := t.config.propagator.Extract(carrier)
if t.config.tracingAsTransport && ctx != nil {
if t.config.internalConfig.TracingAsTransport() && ctx != nil {
// in tracing as transport mode, reset upstream sampling decision to make sure we keep 1 trace/minute
if ctx.trace != nil &&
!globalinternal.VerifyTraceSourceEnabled(ctx.trace.propagatingTag(keyPropagatedTraceSource), globalinternal.ASMTraceSource) {
Expand Down Expand Up @@ -981,7 +981,7 @@ func (t *tracer) TracerConf() TracerConf {
EnvTag: t.config.env,
VersionTag: t.config.version,
ServiceTag: t.config.serviceName,
TracingAsTransport: t.config.tracingAsTransport,
TracingAsTransport: t.config.internalConfig.TracingAsTransport(),
isLambdaFunction: t.config.isLambdaFunction,
}
}
Expand Down
3 changes: 3 additions & 0 deletions ddtrace/tracer/tracer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
"github.com/DataDog/dd-trace-go/v2/ddtrace/internal/tracerstats"
"github.com/DataDog/dd-trace-go/v2/internal"
internalconfig "github.com/DataDog/dd-trace-go/v2/internal/config"
"github.com/DataDog/dd-trace-go/v2/internal/globalconfig"
"github.com/DataDog/dd-trace-go/v2/internal/log"
"github.com/DataDog/dd-trace-go/v2/internal/remoteconfig"
Expand Down Expand Up @@ -74,6 +75,8 @@ var (
)

func TestMain(m *testing.M) {
internalconfig.SetUseFreshConfig(true)
// defer internalconfig.SetUseFreshConfig(false)
if internal.BoolEnv("DD_APPSEC_ENABLED", false) {
// things are slower with AppSec; double wait times
timeMultiplicator = time.Duration(2)
Expand Down
70 changes: 61 additions & 9 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,33 @@ import (
"net/url"
"sync"
"time"

"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
)

var (
instance *config
configOnce sync.Once
useFreshConfig bool
instance *Config
// mu protects instance and useFreshConfig
mu sync.Mutex
)

// Origin represents where a configuration value came from.
// Re-exported so callers don't need to import internal/telemetry.
type Origin = telemetry.Origin

// Re-exported origin constants for common configuration sources
const (
OriginCode = telemetry.OriginCode
OriginCalculated = telemetry.OriginCalculated
)

// Config represents global configuration properties.
type config struct {
// Config instances should be obtained via Get() which always returns a non-nil value.
// Methods on Config assume a non-nil receiver and will panic if called on nil.
type Config struct {
mu sync.RWMutex
// Config fields are protected by the mutex.
agentURL *url.URL
debug bool
logStartup bool
Expand Down Expand Up @@ -45,12 +63,14 @@ type config struct {
ciVisibilityAgentless bool
logDirectory string
traceRateLimitPerSecond float64
// tracingAsTransport specifies whether the tracer is running in transport-only mode, where traces are only sent when other products request it.
tracingAsTransport bool
}

// loadConfig initializes and returns a new config by reading from all configured sources.
// This function is NOT thread-safe and should only be called once through Get's sync.Once.
func loadConfig() *config {
cfg := new(config)
func loadConfig() *Config {
cfg := new(Config)

// TODO: Use defaults from config json instead of hardcoding them here
cfg.agentURL = provider.getURL("DD_TRACE_AGENT_URL", &url.URL{Scheme: "http", Host: "localhost:8126"})
Expand Down Expand Up @@ -80,6 +100,7 @@ func loadConfig() *config {
cfg.ciVisibilityAgentless = provider.getBool("DD_CIVISIBILITY_AGENTLESS_ENABLED", false)
cfg.logDirectory = provider.getString("DD_TRACE_LOG_DIRECTORY", "")
cfg.traceRateLimitPerSecond = provider.getFloat("DD_TRACE_RATE_LIMIT", 0.0)
cfg.tracingAsTransport = false // TODO: Report telemetry?

return cfg
}
Expand All @@ -88,13 +109,44 @@ func loadConfig() *config {
// This function is thread-safe and can be called from multiple goroutines concurrently.
// The configuration is lazily initialized on first access using sync.Once, ensuring
// loadConfig() is called exactly once even under concurrent access.
func Get() *config {
configOnce.Do(func() {
func Get() *Config {
mu.Lock()
defer mu.Unlock()
if useFreshConfig || instance == nil {
instance = loadConfig()
})
}

return instance
}

func (c *config) Debug() bool {
func SetUseFreshConfig(use bool) {
mu.Lock()
defer mu.Unlock()
useFreshConfig = use
}

func (c *Config) Debug() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.debug
}

func (c *Config) SetDebug(enabled bool, origin telemetry.Origin) {
c.mu.Lock()
defer c.mu.Unlock()
c.debug = enabled
telemetry.RegisterAppConfig("DD_TRACE_DEBUG", enabled, origin)
}

func (c *Config) TracingAsTransport() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.tracingAsTransport
}

func (c *Config) SetTracingAsTransport(enabled bool) {
c.mu.Lock()
defer c.mu.Unlock()
c.tracingAsTransport = enabled
// TODO: Report telemetry?
}
Loading
Loading