diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a0d014e78bc..f2d847b96804 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - `prometheusremotewriteexporter`: Handling Staleness flag from OTLP (#6679) - `mysqlreceiver`: Add Integration test (#6916) - `datadogexporter`: Add compatibility with ECS Fargate semantic conventions (#6670) +- `datadogexporter`: Add configuration option to use OTel span name into the Datatog resource name (#6611) - `k8s_observer`: discover k8s.node endpoints (#6820) - `redisreceiver`: Add missing description fields to keyspace metrics (#6940) - `kafkaexporter`: Allow controlling Kafka acknowledgment behaviour (#6301) diff --git a/exporter/datadogexporter/README.md b/exporter/datadogexporter/README.md index df94f1262835..fd24c42127ea 100644 --- a/exporter/datadogexporter/README.md +++ b/exporter/datadogexporter/README.md @@ -28,6 +28,16 @@ datadog: site: datadoghq.eu ``` + If you want to use the OpenTelemetry Span Name as the Datadog Resource Name you can set the `span_name_as_resource_name` configuration option to `true` (default is `false`). For more info on the downsides of this option check [this](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/1909) issue. + + ```yaml +datadog: + api: + key: "" + traces: + span_name_as_resource_name: true +``` + The hostname, environment, service and version can be set in the configuration for unified service tagging. The exporter will try to retrieve a hostname following the OpenTelemetry semantic conventions if there is one available. diff --git a/exporter/datadogexporter/config/config.go b/exporter/datadogexporter/config/config.go index a2359da6c7c2..467bf07c62a5 100644 --- a/exporter/datadogexporter/config/config.go +++ b/exporter/datadogexporter/config/config.go @@ -153,6 +153,11 @@ type TracesConfig struct { // instrumentation:express.server: express // go.opentelemetry.io_contrib_instrumentation_net_http_otelhttp.client: http.client SpanNameRemappings map[string]string `mapstructure:"span_name_remappings"` + + // If set to true the OpenTelemetry span name will used in the Datadog resource name. + // If set to false the resource name will be filled with the instrumentation library name + span kind. + // The default value is `false`. + SpanNameAsResourceName bool `mapstructure:"span_name_as_resource_name"` } // TagsConfig defines the tag-related configuration diff --git a/exporter/datadogexporter/example/config.yaml b/exporter/datadogexporter/example/config.yaml index 3c3466842d60..20089322de33 100644 --- a/exporter/datadogexporter/example/config.yaml +++ b/exporter/datadogexporter/example/config.yaml @@ -148,6 +148,13 @@ exporters: # instrumentation:express.server: express # go.opentelemetry.io_contrib_instrumentation_net_http_otelhttp.client: http.client + ## @param span_name_as_resource_name - use OpenTelemetry semantic convention for span naming - optional + ## Option created to maintain similarity with the OpenTelemetry semantic conventions as discussed in the issue below. + ## https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions + ## https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/1909 + # + # span_name_as_resource_name: true + service: pipelines: diff --git a/exporter/datadogexporter/translate_traces.go b/exporter/datadogexporter/translate_traces.go index 8b5b6293d987..64799ba95643 100644 --- a/exporter/datadogexporter/translate_traces.go +++ b/exporter/datadogexporter/translate_traces.go @@ -304,10 +304,15 @@ func spanToDatadogSpan(s pdata.Span, resourceName := getDatadogResourceName(s, tags) + name := s.Name() + if !cfg.Traces.SpanNameAsResourceName { + name = getDatadogSpanName(s, tags) + } + span := &pb.Span{ TraceID: decodeAPMTraceID(s.TraceID().Bytes()), SpanID: decodeAPMSpanID(s.SpanID().Bytes()), - Name: remapDatadogSpanName(getDatadogSpanName(s, tags), spanNameMap), + Name: remapDatadogSpanName(name, spanNameMap), Resource: resourceName, Service: normalizedServiceName, Start: int64(startTime), diff --git a/exporter/datadogexporter/translate_traces_test.go b/exporter/datadogexporter/translate_traces_test.go index 9d382c6de317..4fa910787b13 100644 --- a/exporter/datadogexporter/translate_traces_test.go +++ b/exporter/datadogexporter/translate_traces_test.go @@ -1571,3 +1571,32 @@ func TestSpanRateLimitTag(t *testing.T) { assert.Equal(t, 0.5, outputTraces[0].Traces[0].Spans[0].Metrics["_sample_rate"]) } + +func TestTracesSpanNamingOption(t *testing.T) { + hostname := "testhostname" + denylister := newDenylister([]string{}) + + // generate mock trace, span and parent span ids + mockTraceID := [16]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} + mockSpanID := [8]byte{0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8} + mockParentSpanID := [8]byte{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8} + + mockEndTime := time.Now().Round(time.Second) + + // create mock resource span data + // toggle on errors and custom service naming to test edge case code paths + rs := NewResourceSpansData(mockTraceID, mockSpanID, mockParentSpanID, pdata.StatusCodeUnset, false, mockEndTime) + + // start with span name as resource name set to true + cfgSpanNameAsResourceName := config.Config{ + Traces: config.TracesConfig{ + SpanNameAsResourceName: true, + }, + } + + // translate mocks to datadog traces + datadogPayloadSpanNameAsResourceName := resourceSpansToDatadogSpans(rs, hostname, &cfgSpanNameAsResourceName, denylister, map[string]string{}) + + // ensure the resource name is replaced with the span name when the option is set + assert.Equal(t, "End-To-End Here", datadogPayloadSpanNameAsResourceName.Traces[0].Spans[0].Name) +}