Repository navigation
Scope OpenTelemetry tag names by span direction in the tag registry (otlp - tag registry - phase 2) - #12713
Conversation
This comment has been minimized.
This comment has been minimized.
🟢 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.
The two most critical issues are: (1) inherited shared-name tags from a directional parent are not validated against a child's differing direction, allowing ambiguous resolved tag sets containing both inbound and outbound identities; and (2) shared-name aliases marked span-kind-neutral are incorrectly included in direction-free runtime tables, causing their resolved IDs to be lost and OTLP to emit the wrong tag name.
🤖 Bits Code Review · Commit d1d526e · @DataDog review to ask questions
peer.port@outbound only ever exists on outbound spans, so its OpenTelemetry name (server.port) needs no direction to emit, as #12713 intended; only resolving the name to the tag does. The generated openTelemetryNameOf now returns it unconditionally, so the direction-less openTelemetryTagOf(id) is right for every tag but a single tag whose rename holds in one direction (http.hostname -> server.address on inbound spans). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sarahchen6
left a comment
There was a problem hiding this comment.
One minor comment, but otherwise LGTM
There was a problem hiding this comment.
More details
Direction-scoped renames remain outside the runtime lookup tables. Shared peer.port names resolve as unknown while retaining their exported Datadog name, consistent with the intended phase-2 boundary.
🤖 Bits Code Review · Commit bfc66c5 · @DataDog review to ask questions
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
bfc66c5 to
653c907
Compare
e49a183 to
44e132c
Compare
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>
653c907 to
5d44b60
Compare
There was a problem hiding this comment.
More details
Current tag conventions generate valid Java and preserve direction-free OpenTelemetry renames. The sole candidate requires a hypothetical shared tag name containing a comment terminator and falls below the requested reporting threshold.
🤖 Bits Code Review · Commit 5d44b60 · @DataDog review to ask questions
Adds span-kind directions to span types and mixins, per-direction tag declarations (peer.port@inbound / peer.port@outbound), direction-scoped renames, and the validation that keeps names unambiguous per direction. Squashed from the review history of #12713. Co-Authored-By: Brice Dutheil <brice.dutheil@gmail.com> 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>
5d44b60 to
885bdb5
Compare
|
/merge |
|
View all feedbacks in Devflow UI.
The expected merge time in
|
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
Follow-on to #12354 (stacked on it; the diff here is only the follow-on). #12354 had to drop several OpenTelemetry renames because their meaning depends on span direction, and a flat rename cannot express that. This PR teaches the tag registry about direction. This phase (2) is registry-only. Every rename
TagMapand OTLP apply today is unchanged; the one generated change is thatpeer.portbecomes one tag per direction (see below).The problem
OpenTelemetry names fall into two frames:
server.address,server.port,client.address,client.port.network.peer.address,network.peer.port.Datadog mixes the two as well.
http.hostnameandhttp.client_ipare absolute, butpeer.hostname,peer.ipv4/6, andpeer.portare relative.HttpServerDecoratorsetspeer.portto the client's port, and the client decorators set it to the server's. Soserver.addressishttp.hostnameon an inbound span andpeer.hostnameon an outbound one, andpeer.portisclient.portinbound butserver.portoutbound.Same-frame mappings never need context. Mapping a relative name to an absolute one needs exactly one bit: the span's direction.
What changes (phase 2)
The conventions are arranged like an object model: span types are classes, mixins are traits. Direction now fits that model.
span-kindon span types and mixins (server|client|producer|consumer|internal, inherited throughextends). It sets the direction: server/consumer are inbound, client/producer are outbound, internal has none. A directional mixin may only reach span types of its direction, and a span type may not change the direction it inherits throughextends; the generator checks both, so no type can receive both sides of a tag declared per direction.Where a rename is declared scopes it. In a scope without a
span-kind(trace_level, an abstract type, a plain mixin) it applies in every direction; in a directional scope, only in that direction.A tag whose meaning flips with direction is declared once per direction, each declaration in its own directional mixin. Each becomes its own tag,
<dd-name>@<direction>, with fixed names in both namespaces:peer.port@outboundandpeer.port@inboundshare the Datadog name, but each has one OTel name, so the reverse translation needs no context: input resolves to the specific tag, and output is a plain id→name lookup per namespace. Any other repeated declaration is still an error, and arefto a shared name resolves by the referencing scope's direction.Shared Datadog names are explicit.
peer.portis one name for two tags. It isn't 1-to-1, so the mapping from name to tag needs context, while the mapping from tag to name doesn't.KnownTagsgenerates no name constant for it, since a bare"peer.port"would only invite direction-blind use. Callers use the unambiguous IDs (PEER_PORT_OUTBOUND_ID,PEER_PORT_INBOUND_ID), each documented with its name and direction.tag-assignment.txtlists shared names:Names are unique per direction, in both namespaces.
server.addressishttp.hostnameinbound andpeer.hostnameoutbound;peer.portis one tag per direction.Only direction-free renames feed the generated tables, because
TagMapand OTLP do not know a span's direction yet. Direction-scoped renames are validated and listed intag-assignment.txt:The one interim runtime change: the bare
peer.portnames two tags, so the direction-freekeyOf("peer.port")returns 0 (unknown) instead of guessing. Its output is unchanged: it had no direction-free rename, so it still exports aspeer.port.span-kind-neutralnarrows to "apply this directional rename in every direction" (for example,db.systemonly ever names a database). It is still required on a concrete type with nospan-kind, and is rejected on a tag declared per direction, whose meaning flips with direction by definition. Phase 3 removes it.YAML: every concrete type declares a
span-kind.peer.*is split intopeer_address(peer.ipv4/6, shared),outbound_peer, andinbound_peer, sohttp.servernow carries its client'speer.*, fixing Add the tag registry and map OpenTelemetry tag names through it (otlp - tag registry - phase 1) #12354's client-only modeling.Planned phases
KnownTags, and the direction-free OpenTelemetry renames.DDSpanContext), using its span kind; the per-direction lookup is generated intoKnownTags. This retiresspan-kind-neutral.network.peer.addressforpeer.ipv4/6, which splits by address family rather than direction, so the registry can't resolve it. The plan is aresolved-by: <layer>marker naming the layer responsible (e.g. the OTel API layer), which allows the mutually exclusive shared output name and keeps it out of every lookup table. Also: messaging consumers, where it needs checking whetherserver.addressmeans the broker; and proxies, which may carry both frames on one span.Phase 3 is #12731, a separate PR. It builds on this PR and on #12715, which adds setting known tags by id to
TagMapand spans (a sibling of this PR in phase 2, also stacked on #12354):Motivation
Resolution belongs at the writer, which has the context to pick the right concept. Datadog instrumentation writes by Datadog name, the OTel bridge knows its span kind, and a serializer knows its target namespace. Each tag needs one identity and unique input names, while output names may collide across directions. dd-trace-dotnet reaches the same result with typed
ClientTags/ServerTagsclasses; here the registry carries that context instead.Additional Notes
span-kind-neutralwidening, plus 10 invalid configurations.peer.portbecomes two tags; ids are not a stable contract. The direction-free rename table is identical to Add the tag registry and map OpenTelemetry tag names through it (otlp - tag registry - phase 1) #12354's.Generated report changes
The generated reports now live under
internal-api/build/generated/tag-registry/(regenerate with./gradlew :internal-api:generateKnownTags), so they no longer show up in the PR diff. Here they are against #12354. Labels likepeer.port@inboundare the generator's derived identities for a name declared per direction, not YAML syntax.resolved-tags.txt:http.servergains its client'speer.*;peer.portbecomes one tag per directiontag-assignment.txt:peer.portsplits in two (serials after it shift by one); new direction-scoped and shared-name sectionsContributor Checklist
build-logic:tag-registry:test(71 tests) and:tag-registry:spotlessCheckgreen:internal-api:test(KnownTags*,TagMap*),:dd-trace-core:test(*otlp*,*taginterceptor*),OpenTelemetry14ConventionsTestgreenJira ticket
N/A
🤖 Generated with Claude Code