Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3ab5862
Set known tags by id on TagMap and spans
dougqh Oct 9, 2026
4bf02bb
Ignore unknown ids when clearing a tag by id; fix a comment wrap
dougqh Oct 9, 2026
a2829d1
Precheck and reuse the box in long-valued setters
dougqh Oct 9, 2026
2d87cde
Keep peer.port off the id path
dougqh Oct 9, 2026
7a017a5
Merge set-by-id (#12715) onto master
dougqh Oct 9, 2026
0401a43
Mark intercepted tags with an id bit, via a tracer overlay
dougqh Oct 9, 2026
8949a57
Dispatch TagInterceptor on tag ids
dougqh Oct 9, 2026
cfe2368
Set tags on spans by id first
dougqh Oct 9, 2026
38c9947
Look up tag names by serial in an array, not a switch
dougqh Oct 9, 2026
3545c15
Point stale comments at the tracer overlay and the intercepted bit
dougqh Oct 9, 2026
1642cc9
Index the split-by-tags table by serial, sized to every known tag
dougqh Oct 9, 2026
b9dd573
Check for a known tag id with a range check, not a name lookup
dougqh Oct 9, 2026
75d3fbc
Pin interceptTag(long) above C2's inlining size
dougqh Oct 9, 2026
ecb4f73
Tighten comments on the id-keyed interceptor
dougqh Oct 9, 2026
3f65031
Treat overlay tags as intercepted; tidy from review
dougqh Oct 9, 2026
69d7849
Reject shared-name tag ids on the id-keyed TagMap and span paths
dougqh Oct 9, 2026
46c3b47
Test rejection of shared-name tag ids
dougqh Oct 9, 2026
ad2fa97
Build custom tags by name and known tags by id
dougqh Oct 10, 2026
628da33
Merge master into id-tag-interceptor (#12817)
dougqh Oct 10, 2026
dac7060
Give the tracer core a restricted, trusted path into TagMap
dougqh Oct 10, 2026
fbab551
Mark TagMap.Internals @NoEscape
dougqh Oct 10, 2026
f08c1c3
Add datadog.apicontract to CODEOWNERS
dougqh Oct 13, 2026
f796149
Merge branch 'master' into dougqh/id-tag-interceptor
dougqh Oct 13, 2026
22d0fd1
Give peer.port one direction-neutral id
dougqh Oct 13, 2026
ba715de
Name peer.port by the span's kind in OTLP export
dougqh Oct 13, 2026
584310c
Let C2 inline the constructors of TagMap's nested classes
dougqh Oct 11, 2026
3842b16
Remove TagMap's remaining synthetic accessors and guard against them
dougqh Oct 11, 2026
64d6cf3
Merge master into id-tag-interceptor
dougqh Oct 14, 2026
bb0bbb0
Cover TagMap.Internals, KnownTagCodec and EntryReader paths
dougqh Oct 14, 2026
fb6410e
Format the tag-registry generator to its spotless rules
dougqh Oct 14, 2026
97a508b
Order the tag-id carve bits by position
dougqh Oct 14, 2026
fa54aca
Merge master into id-tag-interceptor
dougqh Oct 14, 2026
1c88d5e
Re-apply split-by-tags language by its id
dougqh Oct 14, 2026
5ba0a09
Say what a tag id's intercepted bit does and doesn't decide
dougqh Oct 14, 2026
0faed2d
Color known tags into co-occurrence slots in their ids
dougqh Oct 15, 2026
683d11d
Keep TagMap.Ledger in parallel arrays keyed by tag id
dougqh Oct 15, 2026
8610d72
Allocate the ledger's names array only for custom tags
dougqh Oct 15, 2026
22b39e4
Merge the rebased tag-slot-coloring (#12882) into ledger-ids
dougqh Oct 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static java.util.concurrent.TimeUnit.MICROSECONDS;

import datadog.trace.api.KnownTags;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.SpanPrototype;
import datadog.trace.bootstrap.instrumentation.api.Tags;
Expand Down Expand Up @@ -222,6 +223,26 @@ public void webServerSpanViaBuilder() {
span.finish();
}

/**
* {@link #webServerSpanViaBuilder}, setting each builder tag by its {@link KnownTags} id: no name
* lookup when the tag is recorded, and the ledger stores the id directly.
*/
@Benchmark
public void webServerSpanViaBuilderById() {
AgentSpan span =
tracer
.buildSpan(INSTRUMENTATION_NAME, SERVER_OPERATION_NAME)
.withTag(KnownTags.COMPONENT_ID, COMPONENT_VALUE)
.withTag(KnownTags.SPAN_KIND_ID, Tags.SPAN_KIND_SERVER)
.withTag(KnownTags.HTTP_METHOD_ID, HTTP_METHOD_VALUE)
.withTag(KnownTags.HTTP_ROUTE_ID, HTTP_ROUTE_VALUE)
.withTag(KnownTags.HTTP_URL_ID, HTTP_URL_VALUE)
.withTag(KnownTags.HTTP_STATUS_CODE_ID, HTTP_STATUS_VALUE)
.withTag(KnownTags.PEER_PORT_ID, PEER_PORT_VALUE)
.start();
span.finish();
}

/** JDBC/DB-client-shaped span: create -> set the typical DB known tags (9) -> finish. */
@Benchmark
public void jdbcClientSpan() {
Expand Down
19 changes: 19 additions & 0 deletions dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import datadog.trace.api.EndpointTracker;
import datadog.trace.api.IdGenerationStrategy;
import datadog.trace.api.InstrumenterConfig;
import datadog.trace.api.KnownTagCodec;
import datadog.trace.api.Pair;
import datadog.trace.api.TagMap;
import datadog.trace.api.TraceConfig;
Expand Down Expand Up @@ -2075,6 +2076,24 @@ public final CoreSpanBuilder withTag(final String tag, final Object value) {
return this;
}

@Override
public final CoreSpanBuilder withTag(final long tagId, final Object value) {
if (!KnownTagCodec.isKnown(tagId)) {
return this;
}
TagMap.Ledger tagLedger = this.tagLedger;
if (tagLedger == null) {
this.tagLedger = tagLedger = TagMap.ledger();
}
if (value == null) {
// see withTag(String, Object): only a removal of a tag this builder set is recorded
tagLedger.smartRemove(tagId);
} else {
tagLedger.set(tagId, value);
}
return this;
}

@Override
public final <T> AgentTracer.SpanBuilder withRequestContextData(
RequestContextSlot slot, T data) {
Expand Down
47 changes: 33 additions & 14 deletions dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -1322,22 +1322,41 @@ void setAllTags(final TagMap.Ledger ledger) {
}

synchronized (unsafeTags) {
for (final TagMap.EntryChange entryChange : ledger) {
String tag = entryChange.tag();
if (entryChange.isRemoval()) {
if (tagEquals(tag, Tags.SPAN_KIND)) {
// mirror removeTag(String): keep the cached ordinal in sync with unsafeTags
spanKindOrdinal = SPAN_KIND_UNSET;
}
unsafeTags.remove(tag);
} else {
TagMap.Entry entry = (TagMap.Entry) entryChange;
// Interception needs an intercepted id in the ledger, or a split-by-tags entry, which is
// config rather than an id bit. Without either, every change goes straight to storage.
if (ledger.hasInterceptedTags() || tagInterceptor.hasSplitServiceTags()) {
ledger.forEach(this, DDSpanContext::interceptOrApplyLedgerChange);
} else {
ledger.forEach(this, DDSpanContext::applyLedgerChange);
}
}
}

if (!tagInterceptor.interceptTag(this, entry)) {
unsafeTags.set(entry);
}
}
/** Call while holding the {@code unsafeTags} lock. */
private static void interceptOrApplyLedgerChange(
DDSpanContext ctx, TagMap.EntryChangeReader change) {
if (change.isRemoval() || !ctx.tagInterceptor.interceptTag(ctx, change)) {
applyLedgerChange(ctx, change);
}
}

/** Call while holding the {@code unsafeTags} lock. */
private static void applyLedgerChange(DDSpanContext ctx, TagMap.EntryChangeReader change) {
long tagId = change.tagId();
if (change.isRemoval()) {
if (tagId == KnownTags.SPAN_KIND_ID) {
// mirror removeTag(String): keep the cached ordinal in sync with unsafeTags
ctx.spanKindOrdinal = SPAN_KIND_UNSET;
}
if (tagId != 0) {
ctx.unsafeTags.getAndRemove(tagId);
} else {
ctx.unsafeTags.remove(change.tag());
}
} else if (tagId != 0) {
ctx.unsafeTags.internals().setKnown(tagId, change.objectValue());
} else {
ctx.unsafeTags.set(change);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public class TagInterceptor {
/** split-by-tags entries naming custom tags; null when there are none. */
private final Set<String> splitServiceCustomNames;

/** Whether any split-by-tags entry is configured; see {@link #hasSplitServiceTags}. */
private final boolean hasSplitServiceTags;

private final boolean shouldSet404ResourceName;
private final boolean shouldSetUrlResourceAsName;
private final boolean jeeSplitByDeployment;
Expand All @@ -84,6 +87,7 @@ public TagInterceptor(
this.inferredServiceName = inferredServiceName;
this.splitServiceSerials = knownTagSerials(splitServiceTags);
this.splitServiceCustomNames = customTagNames(splitServiceTags);
this.hasSplitServiceTags = !splitServiceTags.isEmpty();
this.ruleFlags = ruleFlags;
splitByServletContext = isSplitServiceTag(SERVLET_CONTEXT_ID);

Expand Down Expand Up @@ -129,6 +133,15 @@ private static Set<String> customTagNames(Set<String> names) {
return custom;
}

/**
* Whether any split-by-tags entry is configured. split-by-tags is tracer config, not an id bit,
* so a batch of tags needs no interception only when this is false and no id in the batch has
* {@link KnownTagCodec#INTERCEPTED}.
*/
public boolean hasSplitServiceTags() {
return hasSplitServiceTags;
}

/** Requires a known id, whose serial is always in range of the table. */
private boolean isSplitServiceTag(long tagId) {
return splitServiceSerials[KnownTagCodec.serialNum(tagId)];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package datadog.trace.core;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import datadog.trace.api.KnownTagCodec;
import datadog.trace.api.KnownTags;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.common.writer.ListWriter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/** {@code SpanBuilder.withTag(long, ...)}: a builder tag set by id lands like one set by name. */
class CoreSpanBuilderWithTagByIdTest extends DDCoreJavaSpecification {
private CoreTracer tracer;

@BeforeEach
void setup() {
tracer = tracerBuilder().writer(new ListWriter()).build();
}

@AfterEach
void tearDown() {
tracer.close();
}

@Test
void aTagSetByIdIsStoredUnderItsName() {
AgentSpan span =
tracer
.buildSpan("datadog", "op")
.withTag(KnownTags.HTTP_ROUTE_ID, "/users/{id}")
.withTag(KnownTags.HTTP_STATUS_CODE_ID, 200)
.withTag("my.custom.tag", "x")
.start();

assertEquals("/users/{id}", span.getTag(KnownTags.HTTP_ROUTE_NAME));
assertEquals(200, span.getTag(KnownTags.HTTP_STATUS_CODE_NAME));
assertEquals("x", span.getTag("my.custom.tag"));
span.finish();
}

@Test
void anInterceptedTagSetByIdIsRouted() {
AgentSpan span =
tracer.buildSpan("datadog", "op").withTag(KnownTags.RESOURCE_NAME_ID, "GET /x").start();

assertEquals("GET /x", span.getResourceName().toString());
span.finish();
}

@Test
void aNullValueRemovesOnlyATagTheBuilderSet() {
AgentSpan span =
tracer
.buildSpan("datadog", "op")
.withTag(KnownTags.HTTP_ROUTE_ID, "/users")
.withTag(KnownTags.HTTP_ROUTE_ID, null)
.withTag(KnownTags.HTTP_METHOD_ID, null)
.start();

assertNull(span.getTag(KnownTags.HTTP_ROUTE_NAME));
span.finish();
}

@Test
void anUnknownIdIsIgnored() {
AgentSpan span =
tracer.buildSpan("datadog", "op").withTag(KnownTagCodec.makeTagId(9999), "x").start();

assertNull(span.getTag("x"));
span.finish();
}
}
Loading
Loading