Skip to content

Commit 69353ec

Browse files
dougqhclaude
andcommitted
Pre-build id-bearing shared Entries for common tags (InternalTagsAdder)
Handle the per-span "common" tags (base.service / version) via the tag-id fast path. These values are fixed for the tracer's life, so build their TagMap.Entry once and share across every span (Entry is immutable + safe to share) — dropping InternalTagsAdder's per-span Entry allocation to zero (cf. PR #11555, the string-keyed precursor), and making the entries tag-id-bearing so they also land in their positional slot. - TagMap.Entry.create(long, Object)/create(long, CharSequence): tag-id keyed, null/empty-rejecting factories mirroring the String create(). - CoreTagIds.BASE_SERVICE / VERSION (stored range) + resolver entries. - InternalTagsAdder prebuilds baseServiceEntry/versionEntry in its ctor and set()s the shared entry; empty DD_SERVICE early-returns (regression test added). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b3a5068 commit 69353ec

4 files changed

Lines changed: 61 additions & 9 deletions

File tree

dd-trace-core/src/main/java/datadog/trace/core/CoreTagIds.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ public final class CoreTagIds {
3030
public static final int PARENT_ID_SERIAL = KnownTags.FIRST_STORED_SERIAL;
3131
public static final long PARENT_ID = KnownTags.tagId(PARENT_ID_SERIAL, 0, DDTags.PARENT_ID);
3232

33+
// common (process-constant) tags added by InternalTagsAdder to ~every span
34+
public static final int BASE_SERVICE_SERIAL = KnownTags.FIRST_STORED_SERIAL + 1;
35+
public static final long BASE_SERVICE =
36+
KnownTags.tagId(BASE_SERVICE_SERIAL, 1, DDTags.BASE_SERVICE);
37+
38+
public static final int VERSION_SERIAL = KnownTags.FIRST_STORED_SERIAL + 2;
39+
public static final long VERSION = KnownTags.tagId(VERSION_SERIAL, 2, Tags.VERSION);
40+
3341
static final KnownTags.Resolver RESOLVER =
3442
new KnownTags.Resolver() {
3543
@Override
@@ -39,6 +47,10 @@ public String nameOf(long tagId) {
3947
return Tags.ERROR;
4048
case PARENT_ID_SERIAL:
4149
return DDTags.PARENT_ID;
50+
case BASE_SERVICE_SERIAL:
51+
return DDTags.BASE_SERVICE;
52+
case VERSION_SERIAL:
53+
return Tags.VERSION;
4254
default:
4355
return null;
4456
}
@@ -51,6 +63,10 @@ public long keyOf(String name) {
5163
return ERROR;
5264
case DDTags.PARENT_ID:
5365
return PARENT_ID;
66+
case DDTags.BASE_SERVICE:
67+
return BASE_SERVICE;
68+
case Tags.VERSION:
69+
return VERSION;
5470
default:
5571
return 0L;
5672
}

dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/InternalTagsAdder.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,48 @@
22

33
import static datadog.trace.bootstrap.instrumentation.api.Tags.VERSION;
44

5-
import datadog.trace.api.DDTags;
65
import datadog.trace.api.TagMap;
76
import datadog.trace.bootstrap.instrumentation.api.AppendableSpanLinks;
87
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
8+
import datadog.trace.core.CoreTagIds;
99
import datadog.trace.core.DDSpanContext;
1010
import javax.annotation.Nullable;
1111

1212
public final class InternalTagsAdder extends TagsPostProcessor {
1313
private final UTF8BytesString ddService;
14-
private final UTF8BytesString version;
14+
15+
// base.service / version are fixed for the life of the tracer, so their TagMap.Entry objects are
16+
// pre-built once and shared across every span (Entry is immutable and safe to share between
17+
// maps).
18+
// The entries are tag-id-bearing (CoreTagIds), so they also land in their positional slot. null
19+
// when the corresponding value is absent/empty. See PR #11555 for the string-keyed precursor.
20+
@Nullable private final TagMap.Entry baseServiceEntry;
21+
@Nullable private final TagMap.Entry versionEntry;
1522

1623
public InternalTagsAdder(@Nullable final String ddService, @Nullable final String version) {
1724
this.ddService = ddService != null ? UTF8BytesString.create(ddService) : null;
18-
this.version = version != null && !version.isEmpty() ? UTF8BytesString.create(version) : null;
25+
this.baseServiceEntry = TagMap.Entry.create(CoreTagIds.BASE_SERVICE, this.ddService);
26+
this.versionEntry =
27+
version != null && !version.isEmpty()
28+
? TagMap.Entry.create(CoreTagIds.VERSION, UTF8BytesString.create(version))
29+
: null;
1930
}
2031

2132
@Override
2233
public void processTags(
2334
TagMap unsafeTags, DDSpanContext spanContext, AppendableSpanLinks spanLinks) {
24-
if (spanContext == null || ddService == null) {
35+
if (spanContext == null || ddService == null || ddService.length() == 0) {
2536
return;
2637
}
2738

2839
if (!ddService.toString().equalsIgnoreCase(spanContext.getServiceName())) {
29-
// service name != DD_SERVICE
30-
unsafeTags.set(DDTags.BASE_SERVICE, ddService);
40+
// service name != DD_SERVICE
41+
unsafeTags.set(baseServiceEntry);
3142
} else {
3243
// as per config consistency, the version tag is added across tracers only if
33-
// the service name is DD_SERVICE and version tag is not manually set
34-
if (version != null && !unsafeTags.containsKey(VERSION)) {
35-
unsafeTags.set(VERSION, version);
44+
// the service name is DD_SERVICE and version tag is not manually set
45+
if (versionEntry != null && !unsafeTags.containsKey(VERSION)) {
46+
unsafeTags.set(versionEntry);
3647
}
3748
}
3849
}

dd-trace-core/src/test/java/datadog/trace/core/tagprocessor/InternalTagsAdderTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import datadog.trace.test.util.DDJavaSpecification;
1515
import java.util.Collections;
1616
import java.util.Objects;
17+
import org.junit.jupiter.api.Test;
1718
import org.tabletest.junit.TableTest;
1819

1920
class InternalTagsAdderTest extends DDJavaSpecification {
@@ -67,4 +68,16 @@ void shouldAddVersionWhenDdServiceEqualsServiceNameAndVersionSet(
6768
verify(spanContext, times(1)).getServiceName();
6869
assertEquals(expected, Objects.toString(unsafeTags.get(VERSION), null));
6970
}
71+
72+
// Regression: empty DD_SERVICE is treated the same as unset — processTags exits early and writes
73+
// no tags, regardless of the span's service name (the prebuilt base.service entry is null).
74+
@Test
75+
void emptyDdServiceWritesNoTags() {
76+
InternalTagsAdder adder = new InternalTagsAdder("", "1.0");
77+
DDSpanContext spanContext = mock(DDSpanContext.class);
78+
79+
TagMap tags = TagMap.fromMap(Collections.emptyMap());
80+
adder.processTags(tags, spanContext, link -> {});
81+
assertTrue(tags.isEmpty());
82+
}
7083
}

internal-api/src/main/java/datadog/trace/api/TagMap.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,18 @@ public static final Entry create(String tag, CharSequence value) {
455455
: TagMap.Entry.newObjectEntry(tag, value);
456456
}
457457

458+
/** Tag-id keyed {@link #create(String, Object)}: null value yields null. */
459+
public static final Entry create(long tagId, Object value) {
460+
return (value == null) ? null : TagMap.Entry.newAnyEntry(tagId, value);
461+
}
462+
463+
/** Tag-id keyed {@link #create(String, CharSequence)}: null/empty value yields null. */
464+
public static final Entry create(long tagId, CharSequence value) {
465+
return (value == null || value.length() == 0)
466+
? null
467+
: TagMap.Entry.newObjectEntry(tagId, value);
468+
}
469+
458470
public static final Entry create(String tag, boolean value) {
459471
return TagMap.Entry.newBooleanEntry(tag, value);
460472
}

0 commit comments

Comments
 (0)