Skip to content
Open
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
21 changes: 16 additions & 5 deletions ddtrace/tracer/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,14 @@ type startupInfo struct {
PartialFlushMinSpans int `json:"partial_flush_min_spans"` // The min number of spans to trigger a partial flush
Orchestrion orchestrionConfig `json:"orchestrion"` // Orchestrion (auto-instrumentation) configuration.
FeatureFlags []string `json:"feature_flags"`
PropagationStyleInject string `json:"propagation_style_inject"` // Propagation style for inject
PropagationStyleExtract string `json:"propagation_style_extract"` // Propagation style for extract
TracingAsTransport bool `json:"tracing_as_transport"` // Whether the tracer is disabled and other products are using it as a transport
DogstatsdAddr string `json:"dogstatsd_address"` // Destination of statsd payloads
DataStreamsEnabled bool `json:"data_streams_enabled"` // Whether Data Streams is enabled
PropagationStyleInject string `json:"propagation_style_inject"` // Propagation style for inject
PropagationStyleExtract string `json:"propagation_style_extract"` // Propagation style for extract
TracingAsTransport bool `json:"tracing_as_transport"` // Whether the tracer is disabled and other products are using it as a transport
DogstatsdAddr string `json:"dogstatsd_address"` // Destination of statsd payloads
DataStreamsEnabled bool `json:"data_streams_enabled"` // Whether Data Streams is enabled
OTLPTracesExportEnabled bool `json:"otlp_traces_export_enabled"` // Whether traces are exported over OTLP
OTLPMetricsExportEnabled bool `json:"otlp_metrics_export_enabled"` // Whether metrics are exported over OTLP
OTLPLogsExportEnabled bool `json:"otlp_logs_export_enabled"` // Whether logs are exported over OTLP
}

// checkEndpoint tries to connect to the URL specified by endpoint.
Expand Down Expand Up @@ -187,6 +190,14 @@ func logStartup(t *tracer) {
TracingAsTransport: t.config.tracingAsTransport,
DogstatsdAddr: t.config.internalConfig.DogstatsdAddr(),
DataStreamsEnabled: t.config.internalConfig.DataStreamsMonitoringEnabled(),
OTLPTracesExportEnabled: t.otlpExportMode,
// Reports the tracer's resolved decision to start OTel runtime metrics over
// OTLP: the same gate the tracer uses (config enabled and opentelemetry/metric
// imported, so StartHook != nil), not a config-only guess.
OTLPMetricsExportEnabled: t.config.otelRuntimeMetricsShouldBeEnabled,
// The user starts the OTel logs integration (opentelemetry/log.Start) after
// this log is emitted, so no started-state is knowable here; report config intent.
OTLPLogsExportEnabled: t.config.internalConfig.LogsOTelEnabled(),
}
if limit, ok := t.rulesSampling.TraceRateLimit(); ok {
info.SampleRateLimit = fmt.Sprintf("%v", limit)
Expand Down
59 changes: 55 additions & 4 deletions ddtrace/tracer/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

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

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -25,6 +26,15 @@ const logPrefixRegexp = `Datadog Tracer v[0-9]+\.[0-9]+\.[0-9]+(-((rc|beta)\.[0-
var commonLogIgnore = []string{"appsec: ", "telemetry", "Runtime metrics v2 enabled"}

func TestStartupLog(t *testing.T) {
// Make the OTLP export fields deterministic regardless of any OTEL_*/DD_*
// env pollution in the dev/CI shell. Empty values are treated as unset by
// the config provider, so each field falls back to its default of false:
// traces export to the agent, the OTel metrics signal is off, and logs are
// off. This matches the golden output in the sub-tests below.
t.Setenv("OTEL_TRACES_EXPORTER", "")
t.Setenv("DD_METRICS_OTEL_ENABLED", "")
t.Setenv("DD_LOGS_OTEL_ENABLED", "")

t.Run("basic", func(t *testing.T) {
assert := assert.New(t)
tp := new(log.RecordLogger)
Expand All @@ -36,7 +46,7 @@ func TestStartupLog(t *testing.T) {
tp.Ignore(commonLogIgnore...)
logStartup(tracer)
require.Len(t, tp.Logs(), 2)
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v1.0/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","trace_sampling_rules":null,"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false},"feature_flags":\[\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false}`, tp.Logs()[1])
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v1.0/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","trace_sampling_rules":null,"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false},"feature_flags":\[\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false,"otlp_traces_export_enabled":false,"otlp_metrics_export_enabled":false,"otlp_logs_export_enabled":false}`, tp.Logs()[1])
})

t.Run("configured", func(t *testing.T) {
Expand Down Expand Up @@ -68,7 +78,7 @@ func TestStartupLog(t *testing.T) {
tp.Ignore(commonLogIgnore...)
logStartup(tracer)
require.Len(t, tp.Logs(), 2)
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v1.0/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"100","trace_sampling_rules":\[{"service":"mysql","sample_rate":0\.75}\],"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":(false|true,"metadata":{"version":"v\d+.\d+.\d+(-[^"]+)?"})},"feature_flags":\["discovery"\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false}`, tp.Logs()[1])
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v1.0/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"100","trace_sampling_rules":\[{"service":"mysql","sample_rate":0\.75}\],"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":(false|true,"metadata":{"version":"v\d+.\d+.\d+(-[^"]+)?"})},"feature_flags":\["discovery"\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false,"otlp_traces_export_enabled":false,"otlp_metrics_export_enabled":false,"otlp_logs_export_enabled":false}`, tp.Logs()[1])
})

t.Run("limit", func(t *testing.T) {
Expand Down Expand Up @@ -99,7 +109,7 @@ func TestStartupLog(t *testing.T) {
tp.Ignore(commonLogIgnore...)
logStartup(tracer)
require.Len(t, tp.Logs(), 2)
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v1.0/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"1000.001","trace_sampling_rules":\[{"service":"mysql","sample_rate":0\.75}\],"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false},"feature_flags":\[\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false}`, tp.Logs()[1])
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v1.0/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"1000.001","trace_sampling_rules":\[{"service":"mysql","sample_rate":0\.75}\],"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false},"feature_flags":\[\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false,"otlp_traces_export_enabled":false,"otlp_metrics_export_enabled":false,"otlp_logs_export_enabled":false}`, tp.Logs()[1])
})

t.Run("errors", func(t *testing.T) {
Expand All @@ -121,7 +131,7 @@ func TestStartupLog(t *testing.T) {
tp.Ignore(commonLogIgnore...)
logStartup(tracer)
assert.Len(tp.Logs(), 1)
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v1.0/traces","agent_error":"","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","trace_sampling_rules":null,"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"true","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false},"feature_flags":\[\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false}`, tp.Logs()[0])
assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v1.0/traces","agent_error":"","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","trace_sampling_rules":null,"span_sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"runtime_metrics_v2_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"true","appsec":((true)|(false)),"agent_features":{"DropP0s":true,"Stats":true,"StatsdPort":(0|8125)},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false},"feature_flags":\[\],"propagation_style_inject":"datadog,tracecontext,baggage","propagation_style_extract":"datadog,tracecontext,baggage","tracing_as_transport":false,"dogstatsd_address":"localhost:8125","data_streams_enabled":false,"otlp_traces_export_enabled":false,"otlp_metrics_export_enabled":false,"otlp_logs_export_enabled":false}`, tp.Logs()[0])
})

t.Run("integrations", func(t *testing.T) {
Expand All @@ -140,6 +150,47 @@ func TestStartupLog(t *testing.T) {
assert.Contains(tp.Logs()[1], expect, "expected integration %s", expect)
}
})

t.Run("otlp_metrics_export", func(t *testing.T) {
assert := assert.New(t)
tp := new(log.RecordLogger)
// otlp_metrics_export_enabled tracks otelRuntimeMetricsShouldBeEnabled, whose
// gate includes StartHook != nil (normally set when opentelemetry/metric is
// imported). withTestHooks stands in for that import so the pipeline is active.
withTestHooks(t)
t.Setenv("DD_METRICS_OTEL_ENABLED", "true")
t.Setenv("OTEL_METRICS_EXPORTER", "otlp")
tracer, _, _, stop, err := startTestTracer(t, WithLogger(tp))
require.NoError(t, err)
defer stop()
require.True(t, tracer.config.otelRuntimeMetricsShouldBeEnabled,
"OTLP metrics pipeline should be active for this assertion to be meaningful")
tp.Reset()
tp.Ignore(commonLogIgnore...)
logStartup(tracer)
require.Len(t, tp.Logs(), 2)
assert.Contains(tp.Logs()[1], `"otlp_metrics_export_enabled":true`)
})

t.Run("otlp_metrics_export_config_without_pipeline", func(t *testing.T) {
assert := assert.New(t)
tp := new(log.RecordLogger)
// Config intent is present but opentelemetry/metric is not imported, so
// StartHook is nil and the pipeline never starts: the field must report false
// rather than over-reporting from config alone.
require.Nil(t, otelmetricsinstall.StartHook, "test binary must not have the metric package imported")
t.Setenv("DD_METRICS_OTEL_ENABLED", "true")
t.Setenv("OTEL_METRICS_EXPORTER", "otlp")
tracer, _, _, stop, err := startTestTracer(t, WithLogger(tp))
require.NoError(t, err)
defer stop()
require.False(t, tracer.config.otelRuntimeMetricsShouldBeEnabled)
tp.Reset()
tp.Ignore(commonLogIgnore...)
logStartup(tracer)
require.Len(t, tp.Logs(), 2)
assert.Contains(tp.Logs()[1], `"otlp_metrics_export_enabled":false`)
})
}

func TestLogSamplingRules(t *testing.T) {
Expand Down
Loading