Repository navigation
Conversation
This comment has been minimized.
This comment has been minimized.
| String ip = remoteAddress.getHostAddress(); | ||
| if (resolved && Config.get().isPeerHostNameEnabled()) { | ||
| span.setTag(Tags.PEER_HOSTNAME, hostName(remoteAddress, ip)); | ||
| span.setTag(KnownTags.PEER_HOSTNAME_ID, hostName(remoteAddress, ip)); |
There was a problem hiding this comment.
These are included because eventually we'll need to use ID to disambiguate some of these concepts for OTLP, and I wanted to illustrate usage.
That said, I'm happy to leave these to another PR if others prefer.
🟢 Java Benchmark SLOs — All performance SLOs passed
PR vs. master results
Commit: Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f38b1f88fe
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Builds on the registry directions (#12713) and id-keyed setters (#12715, included here): KnownTagCodec resolves names by direction, TagMap finds a shared name's tag whichever direction it holds, and DDSpanContext resolves direction-dependent names by the span's kind. Squashed from the review history of #12731. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c3c341e to
e49a183
Compare
Adds id-keyed setters, getters and removal to TagMap, TagMap.Entry and spans, so a writer that knows the tag skips the name lookup. Squashed from the review history of #12715. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c947ea5 to
55ab351
Compare
e49a183 to
44e132c
Compare
There was a problem hiding this comment.
Adds id-keyed setters, getters and removal to TagMap, TagMap.Entry and spans, so a writer that knows the tag skips the name lookup. Squashed from the review history of #12715. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds on the registry directions (#12713) and id-keyed setters (#12715, included here): KnownTagCodec resolves names by direction, TagMap finds a shared name's tag whichever direction it holds, and DDSpanContext resolves direction-dependent names by the span's kind. Squashed from the review history of #12731. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55ab351 to
b569cd2
Compare
|
|
||
| /** {@code tagId} is a known id, or 0 for the custom tag {@code customTag}. */ | ||
| private Entry(long tagId, String customTag, byte type, long prim, Object obj) { | ||
| super(tagId != 0 ? KnownTagCodec.nameOf(tagId) : customTag); |
There was a problem hiding this comment.
Preserve alias-based split-service rules for builder tags
With dd.trace.split-by-tags=db.system, a span built with withTag("db.system", "postgresql") should use postgresql as its service name. Entry construction instead canonicalizes the key to db.type before interception, while splitServiceTags still contains the literal db.system, so service splitting silently stops working. Default-tag maps have the same mismatch; setting the tag after creation still works because interception precedes canonicalization. Normalize configured split-service keys and interception inputs consistently across these paths.
Was this helpful? React 👍 or 👎
🤖 Bits Code Review · @DataDog review to ask questions · Open Bits AI session
Adds id-keyed setters, getters and removal to TagMap, TagMap.Entry and spans, so a writer that knows the tag skips the name lookup. Squashed from the review history of #12715. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds on the registry directions (#12713) and id-keyed setters (#12715, included here): KnownTagCodec resolves names by direction, TagMap finds a shared name's tag whichever direction it holds, and DDSpanContext resolves direction-dependent names by the span's kind. Squashed from the review history of #12731. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b569cd2 to
3ab5862
Compare
span.setTag(0L, "") or a null value reached TagMap.getAndRemove(long), which rejects unknown ids, so clearing threw IllegalArgumentException while every id-keyed setter ignores an unknown id. removeTag(long) now ignores it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The long-valued setters prechecked with needsIntercept, but when the interceptor declined the tag they discarded the box it was given and stored the primitive. Follow the same precheckIntercept -> setBox shape as the other primitive setters, so TagMap keeps the box. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
peer.port is declared per direction in the registry (#12713), so it has no direction-free id: BaseDecorator, shared by client and server decorators, sets it by name, and the set-by-id tests use an unshared int tag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds on the registry directions (#12713) and id-keyed setters (#12715, included here): KnownTagCodec resolves names by direction, TagMap finds a shared name's tag whichever direction it holds, and DDSpanContext resolves direction-dependent names by the span's kind. Squashed from the review history of #12731. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What Does This Do
Adds setting a known tag by its id to
TagMapand the span API, on top of the tag registry from #12354 (stacked on it; the diff here is only this PR).TagMap.set(long tagId, value)forObject,CharSequence, and the primitives. The entry is built under the tag's canonical name fromKnownTagCodec.nameOf, skipping the canonicalizing name lookup theStringsetters pay on every new entry. An id that names no known tag is rejected.TagMap.Entry.create(long tagId, value), the public way to build an entry ahead of time (as decorators do for cached entries), at the same layer asAgentSpan.setTag. It has the same contract as the name-keyedcreate: a null or empty value yields no entry.AgentSpan.setTag(long tagId, value), as default methods that resolve the id to its name and delegate to theStringsetter, so everyAgentSpanimplementation stays correct with no changes.DDSpan/DDSpanContextoverrides with the same behavior as theStringfamily:TagInterceptorstill runs on the tag's name (it is name-keyed), primitives still box only when the tag might be intercepted, and thehttp.status_codequirk is kept. The difference is that a stored tag is set by id.TagMaphashes entries by tag id.TagMap.Entryholds a 64-bit tag hash, computed once at construction: a known tag's id, or a custom tag's name hash in the low 32 bits. An id's serial sits in bits 63-48, so the two can never collide.tagId()becomes a field read instead of akeyOflookup.getEntry,getAndRemove) take onekeyOf, instead of canonicalizing and then hashing the name separately.BucketGroupkeeps itsinthashes, so onlyEntrygrows:inttolongcosts 8 bytes per entry because of alignment. That is an accepted temporary cost until the dense store.BaseDecorator.onPeerConnection/setPeerPort, which run on every client span, now setpeer.hostname,peer.ipv4/ipv6andpeer.portbyKnownTagsid.Motivation
Under the OTLP <-> Datadog mapping, an ID identifies one tag exactly, where a name may not. #12713 is making the tag registry direction-aware, and there
peer.portbecomes two tags (the server's port on outbound spans, the client's on inbound ones) that share a Datadog name. Writers then need to set the specific tag, which is what an id expresses. This PR provides that API, independent of the direction work:It follows the API shape of #11901, but implemented on the current
TagMaprather than the dense store, which #11901 is stacked on. The dense store can later swap the implementation behind the same signatures.Additional Notes
Ledger/builder support, which will arrive with a caller. Id-keyedTagMap.getEntry(long)/getAndRemove(long)are included, so clearing a tag by id removes it by id rather than round-tripping through its name.Mapcontract unchanged, deliberately:Entry.hashCode()still returns the bucket hash rather thankey.hashCode() ^ value.hashCode(), andTagMapstill compares by identity. Both were already the case before this PR; changing them is a separate behavior change.PEER_PORT_OUTBOUND_ID) hashes by that id, so on this PR alone a lookup by the shared name"peer.port"will not find it. Phase 3 (Resolve direction-dependent tag names by the span's kind (otlp - tag registry - phase 3) #12731) handles both sides: spans resolve the name by their direction, and aTagMaplookup by the shared name probes its per-direction ids.peer.port,KnownTags.PEER_PORT_IDno longer exists, soBaseDecorator.setPeerPortstops compiling until it picks a direction. That is intended: it forces the direction to be chosen explicitly.PEER_PORT_ID.setPeerPort:peer.hostname/ipv4/ipv6keep the same ids in Scope OpenTelemetry tag names by span direction in the tag registry (otlp - tag registry - phase 2) #12713, soonPeerConnectionstays a real caller with no coupling.peer.port, which server decorators also set for the client's port, moves to the direction-aware follow-up.peer.portinto a tag per direction, a tag stored under a per-direction id hashes by that id, but the shared name"peer.port"resolves to no id, so a lookup by name misses it. IfsetPeerPortswitched toPEER_PORT_OUTBOUND_ID, a latergetTag("peer.port")would come back empty. Until direction-aware reads land,setPeerPorttherefore has to keep settingpeer.portby name, which stores it as a custom tag that name lookups still find. So whichever of the two PRs merges second keepssetPeerPortname-keyed;onPeerConnection'speer.hostname/ipv4/ipv6keep the same ids in Scope OpenTelemetry tag names by span direction in the tag registry (otlp - tag registry - phase 2) #12713 and are unaffected.AgentSpanwould see the id-keyed call directly, since a mock intercepts the default method, so every call site moved to ids would break its test's expectations. Instead, the decorator specs now mockNameKeyedAgentSpan, a test-onlyAgentSpanwhose id-keyed overloads arefinaland delegate to the name-keyed ones. A mock can't override a final method, so it only seessetTag(name, value).BaseDecoratorTest.mockSpan()creates one. The specs' expectations are unchanged from master, and moving further call sites to ids needs no test change. (Spock only allowsMock()inside a spec class, so the factory lives on the base spec rather than in a trait. Other modules' 50-oddMock(AgentSpan)sites can adopt it as they migrate.)Tags.PEER_PORTand friends stay as they are; a@Deprecatedpointing at the ids comes once callers have migrated.Contributor Checklist
:internal-api:test(TagMap*incl.TagMapFuzzTest, 384 tests;KnownTags*; newTagMapSetByIdTest,TagMapTagHashTest,TagMapEntryCreateByIdTest),:internal-api:spotlessCheck:dd-trace-core:test(DDSpan*incl. newDDSpanSetTagByIdTest,*taginterceptor*,*otlp*,common.writer.*,common.metrics.*; 1279 tests),:dd-trace-core:spotlessCheck.DDAgentWriterCombinedTest.unixSocketTimeoutKeepsWorkerAliveAndReconnectsfailed once and passed on rerun here and on the base branch; it's a timing test.New
TagMapInsertBenchmark, before vs. after hashing entries by tag id. MacBook, JDK 21, default flags,@Fork(2)triage; 12 tags per op:knownByNameknownByIdcustomByNameThe first run caught a regression:
knownByIdwas +17%, because the id constructor resolved the name twice. That is fixed in 62f1026. Known tags net only +48 B because spreading them by serial removed a bucket collision.:dd-java-agent:agent-bootstrap:test(*Decorator*, 413 tests),:dd-java-agent:agent-bootstrap:spotlessCheckJira ticket
N/A
🤖 Generated with Claude Code