Skip to content

Commit

Permalink
ddtrace/tracer: add support for OTEL env vars (#2715)
Browse files Browse the repository at this point in the history
Co-authored-by: Munir Abdinur <[email protected]>
Co-authored-by: Dario Castañé <[email protected]>
  • Loading branch information
3 people authored May 31, 2024
1 parent a43e166 commit 92095e9
Show file tree
Hide file tree
Showing 12 changed files with 562 additions and 66 deletions.
29 changes: 22 additions & 7 deletions ddtrace/tracer/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ type config struct {
// dynamicInstrumentationEnabled controls if the target application can be modified by Dynamic Instrumentation or not.
// Value from DD_DYNAMIC_INSTRUMENTATION_ENABLED, default false.
dynamicInstrumentationEnabled bool

// globalSampleRate holds sample rate read from environment variables.
globalSampleRate float64
}

// orchestrionConfig contains Orchestrion configuration.
Expand Down Expand Up @@ -302,8 +305,20 @@ const partialFlushMinSpansDefault = 1000
func newConfig(opts ...StartOption) *config {
c := new(config)
c.sampler = NewAllSampler()
defaultRate, err := strconv.ParseFloat(getDDorOtelConfig("sampleRate"), 64)
if err != nil {
log.Warn("ignoring DD_TRACE_SAMPLE_RATE, error: %v", err)
defaultRate = math.NaN()
} else if defaultRate < 0.0 || defaultRate > 1.0 {
log.Warn("ignoring DD_TRACE_SAMPLE_RATE: out of range %f", defaultRate)
defaultRate = math.NaN()
}
c.globalSampleRate = defaultRate
c.httpClientTimeout = time.Second * 10 // 10 seconds

if v := os.Getenv("OTEL_LOGS_EXPORTER"); v != "" {
log.Warn("OTEL_LOGS_EXPORTER is not supported")
}
if internal.BoolEnv("DD_TRACE_ANALYTICS_ENABLED", false) {
globalconfig.SetAnalyticsRate(1.0)
}
Expand All @@ -325,23 +340,23 @@ func newConfig(opts ...StartOption) *config {
return r == ',' || r == ' '
})...)(c)
}
if v := os.Getenv("DD_SERVICE"); v != "" {
if v := getDDorOtelConfig("service"); v != "" {
c.serviceName = v
globalconfig.SetServiceName(v)
}
if ver := os.Getenv("DD_VERSION"); ver != "" {
c.version = ver
}
if v := os.Getenv("DD_SERVICE_MAPPING"); v != "" {
internal.ForEachStringTag(v, func(key, val string) { WithServiceMapping(key, val)(c) })
internal.ForEachStringTag(v, internal.DDTagsDelimiter, func(key, val string) { WithServiceMapping(key, val)(c) })
}
c.headerAsTags = newDynamicConfig("trace_header_tags", nil, setHeaderTags, equalSlice[string])
if v := os.Getenv("DD_TRACE_HEADER_TAGS"); v != "" {
c.headerAsTags.update(strings.Split(v, ","), telemetry.OriginEnvVar)
// Required to ensure that the startup header tags are set on reset.
c.headerAsTags.startup = c.headerAsTags.current
}
if v := os.Getenv("DD_TAGS"); v != "" {
if v := getDDorOtelConfig("resourceAttributes"); v != "" {
tags := internal.ParseTagString(v)
internal.CleanGitMetadataTags(tags)
for key, val := range tags {
Expand All @@ -356,9 +371,9 @@ func newConfig(opts ...StartOption) *config {
c.logToStdout = true
}
c.logStartup = internal.BoolEnv("DD_TRACE_STARTUP_LOGS", true)
c.runtimeMetrics = internal.BoolEnv("DD_RUNTIME_METRICS_ENABLED", false)
c.debug = internal.BoolEnv("DD_TRACE_DEBUG", false)
c.enabled = newDynamicConfig("tracing_enabled", internal.BoolEnv("DD_TRACE_ENABLED", true), func(b bool) bool { return true }, equal[bool])
c.runtimeMetrics = internal.BoolVal(getDDorOtelConfig("metrics"), false)
c.debug = internal.BoolVal(getDDorOtelConfig("debugMode"), false)
c.enabled = newDynamicConfig("tracing_enabled", internal.BoolVal(getDDorOtelConfig("enabled"), true), func(b bool) bool { return true }, equal[bool])
if _, ok := os.LookupEnv("DD_TRACE_ENABLED"); ok {
c.enabled.cfgOrigin = telemetry.OriginEnvVar
}
Expand Down Expand Up @@ -406,7 +421,7 @@ func newConfig(opts ...StartOption) *config {
}
c.peerServiceMappings = make(map[string]string)
if v := os.Getenv("DD_TRACE_PEER_SERVICE_MAPPING"); v != "" {
internal.ForEachStringTag(v, func(key, val string) { c.peerServiceMappings[key] = val })
internal.ForEachStringTag(v, internal.DDTagsDelimiter, func(key, val string) { c.peerServiceMappings[key] = val })
}

for _, fn := range opts {
Expand Down
139 changes: 132 additions & 7 deletions ddtrace/tracer/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,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(defaultDialer.DialContext))
assert.False(c.debug)
})

t.Run("http-client", func(t *testing.T) {
Expand Down Expand Up @@ -443,6 +444,45 @@ func TestTracerOptionsDefaults(t *testing.T) {
})
})

t.Run("debug", func(t *testing.T) {
t.Run("option", func(t *testing.T) {
tracer := newTracer(WithDebugMode(true))
defer tracer.Stop()
c := tracer.config
assert.True(t, c.debug)
})
t.Run("env", func(t *testing.T) {
t.Setenv("DD_TRACE_DEBUG", "true")
c := newConfig()
assert.True(t, c.debug)
})
t.Run("otel-env-debug", func(t *testing.T) {
t.Setenv("OTEL_LOG_LEVEL", "debug")
c := newConfig()
assert.True(t, c.debug)
})
t.Run("otel-env-notdebug", func(t *testing.T) {
// any value other than debug, does nothing
t.Setenv("OTEL_LOG_LEVEL", "notdebug")
c := newConfig()
assert.False(t, c.debug)
})
t.Run("override-chain", func(t *testing.T) {
assert := assert.New(t)
// option override otel
t.Setenv("OTEL_LOG_LEVEL", "debug")
c := newConfig(WithDebugMode(false))
assert.False(c.debug)
// env override otel
t.Setenv("DD_TRACE_DEBUG", "false")
c = newConfig()
assert.False(c.debug)
// option override env
c = newConfig(WithDebugMode(true))
assert.True(c.debug)
})
})

t.Run("dogstatsd", func(t *testing.T) {
t.Run("default", func(t *testing.T) {
tracer := newTracer(WithAgentTimeout(2))
Expand Down Expand Up @@ -884,6 +924,18 @@ func TestServiceName(t *testing.T) {
assert.Equal("api-intake", globalconfig.ServiceName())
})

t.Run("otel-env", func(t *testing.T) {
defer func() {
globalconfig.SetServiceName("")
}()
t.Setenv("OTEL_SERVICE_NAME", "api-intake")
assert := assert.New(t)
c := newConfig()

assert.Equal("api-intake", c.serviceName)
assert.Equal("api-intake", globalconfig.ServiceName())
})

t.Run("WithGlobalTag", func(t *testing.T) {
defer globalconfig.SetServiceName("")
assert := assert.New(t)
Expand All @@ -892,6 +944,18 @@ func TestServiceName(t *testing.T) {
assert.Equal("api-intake", globalconfig.ServiceName())
})

t.Run("OTEL_RESOURCE_ATTRIBUTES", func(t *testing.T) {
defer func() {
globalconfig.SetServiceName("")
}()
t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "service.name=api-intake")
assert := assert.New(t)
c := newConfig()

assert.Equal("api-intake", c.serviceName)
assert.Equal("api-intake", globalconfig.ServiceName())
})

t.Run("DD_TAGS", func(t *testing.T) {
defer globalconfig.SetServiceName("")
t.Setenv("DD_TAGS", "service:api-intake")
Expand All @@ -903,12 +967,21 @@ func TestServiceName(t *testing.T) {
})

t.Run("override-chain", func(t *testing.T) {
defer func() {
globalconfig.SetServiceName("")
}()
assert := assert.New(t)
globalconfig.SetServiceName("")
c := newConfig()
assert.Equal(c.serviceName, filepath.Base(os.Args[0]))
assert.Equal("", globalconfig.ServiceName())

t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "service.name=testService6")
globalconfig.SetServiceName("")
c = newConfig()
assert.Equal(c.serviceName, "testService6")
assert.Equal("testService6", globalconfig.ServiceName())

t.Setenv("DD_TAGS", "service:testService")
globalconfig.SetServiceName("")
c = newConfig()
Expand All @@ -920,17 +993,22 @@ func TestServiceName(t *testing.T) {
assert.Equal(c.serviceName, "testService2")
assert.Equal("testService2", globalconfig.ServiceName())

t.Setenv("DD_SERVICE", "testService3")
t.Setenv("OTEL_SERVICE_NAME", "testService3")
globalconfig.SetServiceName("")
c = newConfig(WithGlobalTag("service", "testService2"))
assert.Equal(c.serviceName, "testService3")
assert.Equal("testService3", globalconfig.ServiceName())

t.Setenv("DD_SERVICE", "testService4")
globalconfig.SetServiceName("")
c = newConfig(WithGlobalTag("service", "testService2"), WithService("testService4"))
c = newConfig(WithGlobalTag("service", "testService2"))
assert.Equal(c.serviceName, "testService4")
assert.Equal("testService4", globalconfig.ServiceName())
defer globalconfig.SetServiceName("")

globalconfig.SetServiceName("")
c = newConfig(WithGlobalTag("service", "testService2"), WithService("testService5"))
assert.Equal(c.serviceName, "testService5")
assert.Equal("testService5", globalconfig.ServiceName())
})
}

Expand All @@ -947,6 +1025,17 @@ func TestStartWithLink(t *testing.T) {
assert.Equal(span.SpanLinks[1].SpanID, uint64(4))
}

func TestOtelResourceAtttributes(t *testing.T) {
t.Run("max 10", func(t *testing.T) {
assert := assert.New(t)
t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "tag1=val1,tag2=val2,tag3=val3,tag4=val4,tag5=val5,tag6=val6,tag7=val7,tag8=val8,tag9=val9,tag10=val10,tag11=val11,tag12=val12")
c := newConfig()
globalTags := c.globalTags.get()
// runtime-id tag is added automatically, so we expect runtime-id + our first 10 tags
assert.Len(globalTags, 11)
})
}

func TestTagSeparators(t *testing.T) {
assert := assert.New(t)

Expand Down Expand Up @@ -1066,6 +1155,14 @@ func TestVersionConfig(t *testing.T) {
assert.Equal("1.2.3", c.version)
})

t.Run("OTEL_RESOURCE_ATTRIBUTES", func(t *testing.T) {
t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "service.version=1.2.3")
assert := assert.New(t)
c := newConfig()

assert.Equal("1.2.3", c.version)
})

t.Run("DD_TAGS", func(t *testing.T) {
t.Setenv("DD_TAGS", "version:1.2.3")
assert := assert.New(t)
Expand All @@ -1079,6 +1176,10 @@ func TestVersionConfig(t *testing.T) {
c := newConfig()
assert.Equal(c.version, "")

t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "service.version=1.1.0")
c = newConfig()
assert.Equal("1.1.0", c.version)

t.Setenv("DD_TAGS", "version:1.1.1")
c = newConfig()
assert.Equal("1.1.1", c.version)
Expand Down Expand Up @@ -1118,6 +1219,14 @@ func TestEnvConfig(t *testing.T) {
assert.Equal("testing", c.env)
})

t.Run("OTEL_RESOURCE_ATTRIBUTES", func(t *testing.T) {
t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "deployment.environment=testing")
assert := assert.New(t)
c := newConfig()

assert.Equal("testing", c.env)
})

t.Run("DD_TAGS", func(t *testing.T) {
t.Setenv("DD_TAGS", "env:testing")
assert := assert.New(t)
Expand All @@ -1131,6 +1240,10 @@ func TestEnvConfig(t *testing.T) {
c := newConfig()
assert.Equal(c.env, "")

t.Setenv("OTEL_RESOURCE_ATTRIBUTES", "deployment.environment=testing0")
c = newConfig()
assert.Equal("testing0", c.env)

t.Setenv("DD_TAGS", "env:testing1")
c = newConfig()
assert.Equal("testing1", c.env)
Expand Down Expand Up @@ -1205,18 +1318,30 @@ func TestWithTraceEnabled(t *testing.T) {
assert.False(c.enabled.current)
})

t.Run("env", func(t *testing.T) {
t.Run("otel-env", func(t *testing.T) {
assert := assert.New(t)
t.Setenv("DD_TRACE_ENABLED", "false")
t.Setenv("OTEL_TRACES_EXPORTER", "none")
c := newConfig()
assert.False(c.enabled.current)
})

t.Run("env-override", func(t *testing.T) {
t.Run("dd-env", func(t *testing.T) {
assert := assert.New(t)
t.Setenv("DD_TRACE_ENABLED", "false")
c := newConfig(WithTraceEnabled(true))
c := newConfig()
assert.False(c.enabled.current)
})

t.Run("override-chain", func(t *testing.T) {
assert := assert.New(t)
// dd env overrides otel env
t.Setenv("OTEL_TRACES_EXPORTER", "none")
t.Setenv("DD_TRACE_ENABLED", "true")
c := newConfig()
assert.True(c.enabled.current)
// tracer option overrides dd env
c = newConfig(WithTraceEnabled(false))
assert.False(c.enabled.current)
})
}

Expand Down
Loading

0 comments on commit 92095e9

Please sign in to comment.