diff --git a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java index 89e5a01adb8..bed308b7e57 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java @@ -27,6 +27,7 @@ import datadog.trace.api.Config; import datadog.trace.api.ConfigDefaults; import datadog.trace.api.DDTags; +import datadog.trace.api.KnownTagCodec; import datadog.trace.api.Pair; import datadog.trace.api.TagMap; import datadog.trace.api.config.GeneralConfig; @@ -41,6 +42,7 @@ import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.core.DDSpanContext; import java.net.URI; +import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; @@ -77,9 +79,9 @@ public TagInterceptor( boolean jeeSplitByDeployment) { this.isServiceNameSetByUser = isServiceNameSetByUser; this.inferredServiceName = inferredServiceName; - this.splitServiceTags = splitServiceTags; + this.splitServiceTags = withCanonicalNames(splitServiceTags); this.ruleFlags = ruleFlags; - splitByServletContext = splitServiceTags.contains(SERVLET_CONTEXT); + splitByServletContext = this.splitServiceTags.contains(SERVLET_CONTEXT); shouldSet404ResourceName = ruleFlags.isEnabled(URL_AS_RESOURCE_NAME) @@ -89,6 +91,25 @@ public TagInterceptor( this.jeeSplitByDeployment = jeeSplitByDeployment; } + /** + * Adds the Datadog name of each configured tag: entries set on the span builder arrive under it, + * so a tag configured by its OpenTelemetry name (e.g. {@code db.system}) would otherwise be + * missed. + */ + private static Set withCanonicalNames(Set tags) { + Set result = null; + for (String tag : tags) { + String canonical = KnownTagCodec.canonicalTagName(tag); + if (!canonical.equals(tag) && !tags.contains(canonical)) { + if (result == null) { + result = new HashSet<>(tags); + } + result.add(canonical); + } + } + return result == null ? tags : result; + } + public boolean needsIntercept(TagMap map) { for (TagMap.EntryReader entry : map) { if (needsIntercept(entry.tag())) return true; diff --git a/dd-trace-core/src/test/java/datadog/trace/core/taginterceptor/TagInterceptorTest.java b/dd-trace-core/src/test/java/datadog/trace/core/taginterceptor/TagInterceptorTest.java index 91f3da0910f..7430a747382 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/taginterceptor/TagInterceptorTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/taginterceptor/TagInterceptorTest.java @@ -9,6 +9,7 @@ import static datadog.trace.bootstrap.instrumentation.api.Tags.HTTP_STATUS; import static datadog.trace.bootstrap.instrumentation.api.Tags.HTTP_URL; import static datadog.trace.test.junit.utils.config.WithConfigExtension.injectSysConfig; +import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -48,8 +49,11 @@ import datadog.trace.test.junit.utils.config.WithConfig; import datadog.trace.test.junit.utils.converter.ConfigDefaultsConverter; import datadog.trace.test.junit.utils.converter.TagsConverter; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; import org.junit.jupiter.api.Test; @@ -190,15 +194,52 @@ void mappingCausesServletContextToNotChangeServiceName(String serviceName) { assertEquals("new-service", span.getServiceName()); } + @TableTest({ + "scenario | configured | tag | expectedBuilder | expectedSetter", + "configured by OTel name | db.system | db.system | split | split ", + "configured by Datadog name | db.type | db.type | split | split ", + "Datadog config, OTel tag | db.type | db.system | split | my-service ", + "OTel config, Datadog tag | db.system | db.type | split | split ", + "custom tag | custom.tag | custom.tag | split | split ", + "unconfigured tag | db.system | other.tag | my-service | my-service ", + "empty configuration | '' | db.system | my-service | my-service ", + "both names, OTel tag | db.system,db.type | db.system | split | split ", + "both names, Datadog tag | db.system,db.type | db.type | split | split ", + "custom tag survives expansion | db.system,custom.tag | custom.tag | split | split ", + "first of two renamed tags | db.system,db.operation.name | db.type | split | split ", + "second of two renamed tags | db.system,db.operation.name | db.operation | split | split " + }) + void splitByTagsMatchesCanonicalAndOriginalNames( + String configured, String tag, String expectedBuilder, String expectedSetter) { + Set configuredTags = + configured.isEmpty() ? emptySet() : new HashSet<>(Arrays.asList(configured.split(","))); + CoreTracer tracer = createSplittingTracer(configuredTags, emptyMap()); + + AgentSpan builderSpan = tracer.buildSpan("datadog", "some span").withTag(tag, "split").start(); + AgentSpan setterSpan = tracer.buildSpan("datadog", "some span").start(); + setterSpan.setTag(tag, "split"); + + assertEquals(expectedBuilder, builderSpan.getServiceName()); + assertEquals(expectedSetter, setterSpan.getServiceName()); + + CoreTracer defaultTagTracer = createSplittingTracer(configuredTags, singletonMap(tag, "split")); + AgentSpan defaultTagSpan = defaultTagTracer.buildSpan("datadog", "some span").start(); + + assertEquals(expectedBuilder, defaultTagSpan.getServiceName()); + } + private CoreTracer createSplittingTracer(String tag) { + return createSplittingTracer(Collections.singleton(tag), emptyMap()); + } + + private CoreTracer createSplittingTracer(Set tags, Map defaultSpanTags) { return tracerBuilder() .serviceName("my-service") .writer(new LoggingWriter()) .sampler(new AllSampler()) - // equivalent to split-by-tags: tag - .tagInterceptor( - new TagInterceptor( - true, "my-service", Collections.singleton(tag), new RuleFlags(), false)) + .defaultSpanTags(defaultSpanTags) + // equivalent to split-by-tags: tags + .tagInterceptor(new TagInterceptor(true, "my-service", tags, new RuleFlags(), false)) .build(); }