Skip to content

Commit ff5099c

Browse files
ericmustinmx-psi
andauthored
Datadog update env clobbering behavior (#3851)
* [exporter/datadog]: handle multiple env tag edge case * [exporter/datadog]: fix typos in code comments * Update exporter/datadogexporter/translate_traces_test.go Co-authored-by: Pablo Baeyens <pablo.baeyens@datadoghq.com> * Update exporter/datadogexporter/translate_traces.go Co-authored-by: Pablo Baeyens <pablo.baeyens@datadoghq.com> * [exporter/datadog]: linting and formatting Co-authored-by: Pablo Baeyens <pablo.baeyens@datadoghq.com>
1 parent 9d30ab8 commit ff5099c

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

exporter/datadogexporter/translate_traces.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const (
6262
// tagContainersTags specifies the name of the tag which holds key/value
6363
// pairs representing information about the container (Docker, EC2, etc).
6464
tagContainersTags = "_dd.tags.container"
65+
tagDatadogEnv = "env"
6566
)
6667

6768
// converts Traces into an array of datadog trace payloads grouped by env
@@ -230,7 +231,7 @@ func spanToDatadogSpan(s pdata.Span,
230231
tags := aggregateSpanTags(s, datadogTags)
231232

232233
// otel specification resource service.name takes precedence
233-
// and configuration DD_ENV as fallback if it exists
234+
// and configuration DD_SERVICE as fallback if it exists
234235
if cfg.Service != "" {
235236
// prefer the collector level service name over an empty string or otel default
236237
if serviceName == "" || serviceName == tracetranslator.ResourceNoServiceName {
@@ -313,8 +314,8 @@ func resourceToDatadogServiceNameAndAttributeMap(
313314
resource pdata.Resource,
314315
) (serviceName string, datadogTags map[string]string) {
315316
attrs := resource.Attributes()
316-
// predefine capacity where possible with extra for _dd.tags.container payload
317-
datadogTags = make(map[string]string, attrs.Len()+1)
317+
// predefine capacity where possible with extra for _dd.tags.container payload and duplicate env tag
318+
datadogTags = make(map[string]string, attrs.Len()+2)
318319

319320
if attrs.Len() == 0 {
320321
return tracetranslator.ResourceNoServiceName, datadogTags
@@ -325,6 +326,17 @@ func resourceToDatadogServiceNameAndAttributeMap(
325326
return true
326327
})
327328

329+
// specification states that the resource level deployment.environment should be used for passing env,
330+
// and also a number of Datadog UI components are hardcoded to point to / look for / search with `env`.
331+
// So we ensure that anytime deployment.environment is set, `env` is the same value.
332+
// In the future, we can probably just set `env` using the deployment.environment value, but drop the duplicate
333+
// deployment.environment tag afterward. Without knowing whether this would break existing user setups, its better
334+
// to simply set both tags.
335+
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.4.0/specification/resource/semantic_conventions/deployment_environment.md#deployment
336+
if resourceEnv, ok := datadogTags[conventions.AttributeDeploymentEnvironment]; ok {
337+
datadogTags[tagDatadogEnv] = utils.NormalizeTag(resourceEnv)
338+
}
339+
328340
serviceName = extractDatadogServiceName(datadogTags)
329341
return serviceName, datadogTags
330342
}

exporter/datadogexporter/translate_traces_test.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ func TestTracesTranslationErrorsAndResource(t *testing.T) {
374374
// ensure that version gives resource service.version priority
375375
assert.Equal(t, "test-version", datadogPayload.Traces[0].Spans[0].Meta["version"])
376376

377-
assert.Equal(t, 19, len(datadogPayload.Traces[0].Spans[0].Meta))
377+
assert.Equal(t, 20, len(datadogPayload.Traces[0].Spans[0].Meta))
378378

379379
assert.Contains(t, datadogPayload.Traces[0].Spans[0].Meta[tagContainersTags], "container_id:3249847017410247")
380380
assert.Contains(t, datadogPayload.Traces[0].Spans[0].Meta[tagContainersTags], "pod_name:example-pod-name")
@@ -617,7 +617,7 @@ func TestTracesTranslationOkStatus(t *testing.T) {
617617
// ensure that version gives resource service.version priority
618618
assert.Equal(t, "test-version", datadogPayload.Traces[0].Spans[0].Meta["version"])
619619

620-
assert.Equal(t, 19, len(datadogPayload.Traces[0].Spans[0].Meta))
620+
assert.Equal(t, 20, len(datadogPayload.Traces[0].Spans[0].Meta))
621621
}
622622

623623
// ensure that the datadog span uses the configured unified service tags
@@ -664,7 +664,7 @@ func TestTracesTranslationConfig(t *testing.T) {
664664
// ensure that version gives resource service.version priority
665665
assert.Equal(t, "test-version", datadogPayload.Traces[0].Spans[0].Meta["version"])
666666

667-
assert.Equal(t, 16, len(datadogPayload.Traces[0].Spans[0].Meta))
667+
assert.Equal(t, 17, len(datadogPayload.Traces[0].Spans[0].Meta))
668668
}
669669

670670
// ensure that the translation returns early if no resource instrumentation library spans
@@ -1399,3 +1399,54 @@ func TestSpanNameMapping(t *testing.T) {
13991399

14001400
assert.Equal(t, "bang.client", aggregatedTraces[0].Traces[0].Spans[0].Name)
14011401
}
1402+
1403+
// ensure that sanitization of trace payloads occurs
1404+
func TestSpanEnvClobbering(t *testing.T) {
1405+
mockTraceID := [16]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}
1406+
mockSpanID := [8]byte{0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8}
1407+
mockParentSpanID := [8]byte{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8}
1408+
endTime := time.Now().Round(time.Second)
1409+
pdataEndTime := pdata.TimestampFromTime(endTime)
1410+
startTime := endTime.Add(-90 * time.Second)
1411+
pdataStartTime := pdata.TimestampFromTime(startTime)
1412+
1413+
denylister := newDenylister([]string{})
1414+
buildInfo := component.BuildInfo{
1415+
Version: "1.0",
1416+
}
1417+
1418+
traces := pdata.NewTraces()
1419+
traces.ResourceSpans().Resize(1)
1420+
rs := traces.ResourceSpans().At(0)
1421+
resource := rs.Resource()
1422+
resource.Attributes().InitFromMap(map[string]pdata.AttributeValue{
1423+
conventions.AttributeDeploymentEnvironment: pdata.NewAttributeValueString("correctenv"),
1424+
"env": pdata.NewAttributeValueString("incorrectenv"),
1425+
})
1426+
1427+
rs.InstrumentationLibrarySpans().Resize(1)
1428+
ilss := rs.InstrumentationLibrarySpans().At(0)
1429+
instrumentationLibrary := ilss.InstrumentationLibrary()
1430+
instrumentationLibrary.SetName("flash")
1431+
instrumentationLibrary.SetVersion("v1")
1432+
span := ilss.Spans().AppendEmpty()
1433+
1434+
traceID := pdata.NewTraceID(mockTraceID)
1435+
spanID := pdata.NewSpanID(mockSpanID)
1436+
parentSpanID := pdata.NewSpanID(mockParentSpanID)
1437+
span.SetTraceID(traceID)
1438+
span.SetSpanID(spanID)
1439+
span.SetParentSpanID(parentSpanID)
1440+
span.SetName("End-To-End Here")
1441+
span.SetKind(pdata.SpanKindServer)
1442+
span.SetStartTimestamp(pdataStartTime)
1443+
span.SetEndTimestamp(pdataEndTime)
1444+
1445+
config := config.Config{Traces: config.TracesConfig{SpanNameRemappings: map[string]string{"flash.server": "bang.client"}}}
1446+
1447+
outputTraces, _ := convertToDatadogTd(traces, "test-host", &config, denylister, buildInfo)
1448+
1449+
// Ensure the deployment.environment value is copied to both deployment.environment and env
1450+
assert.Equal(t, "correctenv", outputTraces[0].Traces[0].Spans[0].Meta["env"])
1451+
assert.Equal(t, "correctenv", outputTraces[0].Traces[0].Spans[0].Meta["deployment.environment"])
1452+
}

0 commit comments

Comments
 (0)