Skip to content
Closed
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
2 changes: 1 addition & 1 deletion 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 Down
16 changes: 5 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 @@ -325,9 +325,6 @@ type config struct {
// ciVisibilityNoopTracer controls if CI Visibility must set a wrapper to behave like a noop tracer. default false
ciVisibilityNoopTracer bool

// 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

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,8 +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 {
c.enabled.cfgOrigin = telemetry.OriginEnvVar
Expand Down Expand Up @@ -613,7 +608,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 @@ -1009,8 +1004,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.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func startTelemetry(c *config) telemetry.Client {
{Name: "trace_peer_service_defaults_enabled", Value: c.peerServiceDefaultsEnabled},
{Name: "orchestrion_enabled", Value: c.orchestrionCfg.Enabled, Origin: telemetry.OriginCode},
{Name: "trace_enabled", Value: c.enabled.current, Origin: c.enabled.cfgOrigin},
{Name: "trace_log_directory", Value: c.logDirectory},
{Name: "trace_log_directory", Value: c.internalConfig.LogDirectory()},
c.traceSampleRate.toTelemetry(),
c.headerAsTags.toTelemetry(),
c.globalTags.toTelemetry(),
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
5 changes: 3 additions & 2 deletions ddtrace/tracer/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,11 +435,12 @@ func newUnstartedTracer(opts ...StartOption) (t *tracer, err error) {
dataStreamsProcessor = datastreams.NewProcessor(statsd, c.env, c.serviceName, c.version, c.agentURL, c.httpClient)
}
var logFile *log.ManagedFile
if v := c.logDirectory; v != "" {
if v := c.internalConfig.LogDirectory(); v != "" {
logFile, err = log.OpenFileAtPath(v)
if err != nil {
log.Warn("%s", err.Error())
c.logDirectory = ""
// TODO: Report errors to telemetry
c.internalConfig.SetLogDirectory("", telemetry.OriginCode)
}
}
t = &tracer{
Expand Down
7 changes: 5 additions & 2 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 Expand Up @@ -271,7 +274,7 @@ func TestTracerLogFile(t *testing.T) {
tracer, err := newTracer()
defer tracer.Stop()
assert.Nil(t, err)
assert.Equal(t, dir, tracer.config.logDirectory)
assert.Equal(t, dir, tracer.config.internalConfig.LogDirectory())
assert.NotNil(t, tracer.logFile)
assert.Equal(t, dir+"/"+log.LoggerFile, tracer.logFile.Name())
})
Expand All @@ -280,7 +283,7 @@ func TestTracerLogFile(t *testing.T) {
tracer, err := newTracer()
assert.Nil(t, err)
defer tracer.Stop()
assert.Empty(t, tracer.config.logDirectory)
assert.Empty(t, tracer.config.internalConfig.LogDirectory())
assert.Nil(t, tracer.logFile)
})
}
Expand Down
72 changes: 61 additions & 11 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 All @@ -43,14 +61,15 @@ type config struct {
globalSampleRate float64
ciVisibilityEnabled bool
ciVisibilityAgentless bool
logDirectory string
traceRateLimitPerSecond float64
// logDirectory is directory for tracer logs specified by user-setting DD_TRACE_LOG_DIRECTORY
logDirectory string
traceRateLimitPerSecond float64
}

// 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 @@ -88,13 +107,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) LogDirectory() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.logDirectory
}

func (c *Config) SetLogDirectory(directory string, origin telemetry.Origin) {
c.mu.Lock()
defer c.mu.Unlock()
c.logDirectory = directory
telemetry.RegisterAppConfig("DD_TRACE_LOG_DIRECTORY", directory, origin)
}
Loading
Loading