Skip to content
Draft
4 changes: 2 additions & 2 deletions ddtrace/tracer/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/DataDog/dd-trace-go/v2/internal"
appsecconfig "github.com/DataDog/dd-trace-go/v2/internal/appsec/config"
"github.com/DataDog/dd-trace-go/v2/internal/civisibility/constants"
internalconfig "github.com/DataDog/dd-trace-go/v2/internal/config"
"github.com/DataDog/dd-trace-go/v2/internal/env"
"github.com/DataDog/dd-trace-go/v2/internal/globalconfig"
llmobsconfig "github.com/DataDog/dd-trace-go/v2/internal/llmobs/config"
Expand Down Expand Up @@ -606,10 +607,9 @@ func newConfig(opts ...StartOption) (*config, error) {
if c.logger != nil {
log.UseLogger(c.logger)
}
if c.debug {
if internalconfig.GlobalConfig().IsDebugEnabled() {
log.SetLevel(log.LevelDebug)
}

// Check if CI Visibility mode is enabled
if internal.BoolEnv(constants.CIVisibilityEnabledEnvironmentVariable, false) {
c.ciVisibilityEnabled = true // Enable CI Visibility mode
Expand Down
121 changes: 121 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2025 Datadog, Inc.

package config

import (
"net/url"
"time"
)

var globalConfig *Config

// Config represents global configuration properties.
type Config struct {
// AgentURL is the URL of the Datadog agent.
AgentURL *url.URL `json:"DD_AGENT_URL"`

// Debug enables debug logging.
Debug bool `json:"DD_TRACE_DEBUG"` // has trace in the name, but impacts all products?

LogStartup bool `json:"DD_TRACE_STARTUP_LOGS"`

ServiceName string `json:"DD_SERVICE"`

Version string `json:"DD_VERSION"`

Env string `json:"DD_ENV"`

ServiceMappings map[string]string `json:"DD_SERVICE_MAPPING"`

Hostname string `json:"DD_TRACE_SOURCE_HOSTNAME"`

RuntimeMetrics bool `json:"DD_RUNTIME_METRICS_ENABLED"`

RuntimeMetricsV2 bool `json:"DD_RUNTIME_METRICS_V2_ENABLED"`

ProfilerHotspots bool `json:"DD_PROFILING_CODE_HOTSPOTS_COLLECTION_ENABLED"`

ProfilerEndpoints bool `json:"DD_PROFILING_ENDPOINT_COLLECTION_ENABLED"`

SpanAttributeSchemaVersion int `json:"DD_TRACE_SPAN_ATTRIBUTE_SCHEMA"`

PeerServiceDefaultsEnabled bool `json:"DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED"`

PeerServiceMappings map[string]string `json:"DD_TRACE_PEER_SERVICE_MAPPING"`

DebugAbandonedSpans bool `json:"DD_TRACE_DEBUG_ABANDONED_SPANS"`

SpanTimeout time.Duration `json:"DD_TRACE_SPAN_TIMEOUT"`

PartialFlushMinSpans int `json:"DD_TRACE_PARTIAL_FLUSH_MIN_SPANS"`

PartialFlushEnabled bool `json:"DD_TRACE_PARTIAL_FLUSH_ENABLED"`

StatsComputationEnabled bool `json:"DD_TRACE_STATS_COMPUTATION_ENABLED"`

DataStreamsMonitoringEnabled bool `json:"DD_DATA_STREAMS_ENABLED"`

DynamicInstrumentationEnabled bool `json:"DD_DYNAMIC_INSTRUMENTATION_ENABLED"`

GlobalSampleRate float64 `json:"DD_TRACE_SAMPLE_RATE"`

CIVisibilityEnabled bool `json:"DD_CIVISIBILITY_ENABLED"`

CIVisibilityAgentless bool `json:"DD_CIVISIBILITY_AGENTLESS_ENABLED"`

LogDirectory string `json:"DD_TRACE_LOG_DIRECTORY"`

TraceRateLimitPerSecond float64 `json:"DD_TRACE_RATE_LIMIT"`

TraceProtocol float64 `json:"DD_TRACE_AGENT_PROTOCOL_VERSION"`
}

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"})
cfg.Debug = provider.getBool("DD_TRACE_DEBUG", false)
cfg.LogStartup = provider.getBool("DD_TRACE_STARTUP_LOGS", false)
cfg.ServiceName = provider.getString("DD_SERVICE", "")
cfg.Version = provider.getString("DD_VERSION", "")
cfg.Env = provider.getString("DD_ENV", "")
cfg.ServiceMappings = provider.getMap("DD_SERVICE_MAPPING", nil)
cfg.Hostname = provider.getString("DD_TRACE_SOURCE_HOSTNAME", "")
cfg.RuntimeMetrics = provider.getBool("DD_RUNTIME_METRICS_ENABLED", false)
cfg.RuntimeMetricsV2 = provider.getBool("DD_RUNTIME_METRICS_V2_ENABLED", false)
cfg.ProfilerHotspots = provider.getBool("DD_PROFILING_CODE_HOTSPOTS_COLLECTION_ENABLED", false)
cfg.ProfilerEndpoints = provider.getBool("DD_PROFILING_ENDPOINT_COLLECTION_ENABLED", false)
cfg.SpanAttributeSchemaVersion = provider.getInt("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA", 0)
cfg.PeerServiceDefaultsEnabled = provider.getBool("DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED", false)
cfg.PeerServiceMappings = provider.getMap("DD_TRACE_PEER_SERVICE_MAPPING", nil)
cfg.DebugAbandonedSpans = provider.getBool("DD_TRACE_DEBUG_ABANDONED_SPANS", false)
cfg.SpanTimeout = provider.getDuration("DD_TRACE_ABANDONED_SPAN_TIMEOUT", 0)
cfg.PartialFlushMinSpans = provider.getInt("DD_TRACE_PARTIAL_FLUSH_MIN_SPANS", 0)
cfg.PartialFlushEnabled = provider.getBool("DD_TRACE_PARTIAL_FLUSH_ENABLED", false)
cfg.StatsComputationEnabled = provider.getBool("DD_TRACE_STATS_COMPUTATION_ENABLED", false)
cfg.DataStreamsMonitoringEnabled = provider.getBool("DD_DATA_STREAMS_ENABLED", false)
cfg.DynamicInstrumentationEnabled = provider.getBool("DD_DYNAMIC_INSTRUMENTATION_ENABLED", false)
cfg.GlobalSampleRate = provider.getFloat("DD_TRACE_SAMPLE_RATE", 0.0)
cfg.CIVisibilityEnabled = provider.getBool("DD_CIVISIBILITY_ENABLED", false)
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.TraceProtocol = provider.getFloat("DD_TRACE_AGENT_PROTOCOL_VERSION", 0.0)

return cfg
}

func GlobalConfig() *Config {
if globalConfig == nil {
globalConfig = loadConfig()
}
return globalConfig
}

func (c *Config) IsDebugEnabled() bool {
return c.Debug
}
88 changes: 88 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2025 Datadog, Inc.

package config

// func TestConfigHasFields(t *testing.T) {
// // TODO: Use supported configurations JSON as expectedFields instead
// expectedFields := map[string]reflect.Type{
// "AgentURL": reflect.TypeOf((*url.URL)(nil)),
// "Debug": reflect.TypeOf(false),
// "LogToStdout": reflect.TypeOf(false),
// "LogStartup": reflect.TypeOf(false),
// "ServiceName": reflect.TypeOf(""),
// "Version": reflect.TypeOf(""),
// "Env": reflect.TypeOf(""),
// //"Sampler": reflect.TypeOf((*RateSampler)(nil)),
// // "OriginalAgentURL": reflect.TypeOf((*url.URL)(nil)), // We probably don't need this anymore
// "ServiceMappings": reflect.TypeOf((map[string]string)(nil)),
// // "GlobalTags": reflect.TypeOf((*dynamicConfig[map[string]interface{}])(nil)),
// // "Transport": reflect.TypeOf((*transport)(nil)),
// "HTTPClientTimeout": reflect.TypeOf(int64(0)),
// // "Propagator": reflect.TypeOf((*Propagator)(nil)),
// "Hostname": reflect.TypeOf(""),
// // "Logger": reflect.TypeOf((*Logger)(nil)),
// "RuntimeMetrics": reflect.TypeOf(false),
// "RuntimeMetricsV2": reflect.TypeOf(false),
// // "StatsdClient": reflect.TypeOf((*internal.StatsdClient)(nil)),
// // "SpanRules": reflect.TypeOf((*[]SamplingRule)(nil)),
// // "TraceRules": reflect.TypeOf((*[]SamplingRule)(nil)),
// "ProfilerHotspots": reflect.TypeOf(false),
// "ProfilerEndpoints": reflect.TypeOf(false),
// // "TracingEnabled": reflect.TypeOf((*dynamicConfig[bool])(nil)),
// "EnableHostnameDetection": reflect.TypeOf(false),
// "SpanAttributeSchemaVersion": reflect.TypeOf(0),
// "PeerServiceDefaultsEnabled": reflect.TypeOf(false),
// "PeerServiceMappings": reflect.TypeOf((map[string]string)(nil)),
// "DebugAbandonedSpans": reflect.TypeOf(false),
// "SpanTimeout": reflect.TypeOf(time.Duration(int64(0))),
// "PartialFlushMinSpans": reflect.TypeOf(0),
// "PartialFlushEnabled": reflect.TypeOf(false),
// "StatsComputationEnabled": reflect.TypeOf(false),
// "DataStreamsMonitoringEnabled": reflect.TypeOf(false),
// // "OrchestrionCfg": reflect.TypeOf((*orchestrionConfig)(nil)),
// // "TraceSampleRate": reflect.TypeOf((*dynamicConfig[float64])(nil)),
// // "TraceSampleRules": reflect.TypeOf((*dynamicConfig[[]SamplingRule])(nil)),
// // "HeaderAsTags": reflect.TypeOf((*dynamicConfig[[]string])(nil)),
// "DynamicInstrumentationEnabled": reflect.TypeOf(false),
// "GlobalSampleRate": reflect.TypeOf(float64(0)),
// "CIVisibilityEnabled": reflect.TypeOf(false),
// "CIVisibilityAgentless": reflect.TypeOf(false),
// "LogDirectory": reflect.TypeOf(""),
// "TracingAsTransport": reflect.TypeOf(false),
// "TraceRateLimitPerSecond": reflect.TypeOf(float64(0)),
// "TraceProtocol": reflect.TypeOf(float64(0)),
// // "LLMObsEnabled": reflect.TypeOf(false),
// // "LLMObsMLApp": reflect.TypeOf(""),
// // "LLMObsAgentlessEnabled": reflect.TypeOf(false),
// // "LLMObsProjectName": reflect.TypeOf(""),
// }

// // Get the Config struct type
// configType := reflect.TypeOf(Config{})

// // Verify the number of expected fields matches the actual number of fields
// actualFieldCount := configType.NumField()
// expectedFieldCount := len(expectedFields)
// assert.Equal(t, expectedFieldCount, actualFieldCount,
// "Expected %d fields in Config struct, but found %d. Update the test when adding/removing fields.",
// expectedFieldCount, actualFieldCount)

// // Verify each expected field exists with the correct type
// for fieldName, expectedType := range expectedFields {
// field, found := configType.FieldByName(fieldName)
// assert.True(t, found, "Field %s should exist on Config struct", fieldName)

// if found {
// assert.Equal(t, expectedType, field.Type,
// "Field %s should have type %s, but has type %s",
// fieldName, expectedType, field.Type)
// }
// }

// // Verify we can instantiate the config
// cfg := new(Config)
// assert.NotNil(t, cfg, "Should be able to create new Config instance")
// }
Loading
Loading