Skip to content

Add trace.builder.tags.precedence.enabled to let explicit builder tags win (phase 1a) - #11738

Open
dougqh wants to merge 9 commits into
masterfrom
dougqh/builder-tags-precedence
Open

Add trace.builder.tags.precedence.enabled to let explicit builder tags win (phase 1a)#11738
dougqh wants to merge 9 commits into
masterfrom
dougqh/builder-tags-precedence

Conversation

@dougqh

@dougqh dougqh commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Adjusts the order in which tags are added to be more logical.
General idea is that tags closer to the individual span should take precedence - e.g. be added last.

Currently, spans from the builder can be clobbered by coreTags, rootSpanTags, and contextualTags.

Change is currently controlled by a config flag, so we can opt into the new behavior gradually.

Motivation

Provide more intuitive semantics in the event of a tag collision between maps.
Remove an obstacle to later map optimization

Additional Notes

The wart: at span build, tagLedger (explicit builder tags) is applied 2nd and then silently overwritten by coreTags / rootSpanTags / contextualTags. Flagged in-code since 2020 (git blame: Björn — "maybe the tags set in the builder should come last, so that they override other tags").

Blast radius: coreTags/rootSpanTags are only non-null on root/extraction-path spans (both null for local children), and the one plausibly-authoritative set (rootSpanTags, _dd.*/runtime tags) isn't something users set on a builder — so a real collision is effectively zero. Documented wart, safe to correct, low blast radius.

The change: off-by-default flag trace.builder.tags.precedence.enabled. When enabled, tagLedger is applied last so explicit builder tags win over coreTags/rootSpanTags/contextualTags. Implemented as an instance field (not static final) so an embedded tracer built via CoreTracerBuilder#withProperties/#config picks up its own config rather than whatever loaded first.

"Override" here only means key collisions: every tag from coreTags/rootSpanTags/contextualTags that doesn't share a key with a builder tag is kept as-is regardless of this flag — there's nothing to resolve. The flag only changes which value wins when two sources set the same key (a tag can only hold one value): today the header/root/contextual value silently wins; with it enabled, the explicit builder value does.

Known limitations:

  • Doesn't cover interceptor-backed sampling tags (manual.keep, manual.drop, asm.keep, ai_guard.keep, sampling.priority) — those apply as immediate side effects when each contributor map is set, so an earlier map's sampling decision sticks regardless of ordering. Fixing that needs deferred interception until all contributor maps are merged — out of scope here.
  • Same applies to _dd.measured: TagInterceptor.interceptMeasured only ever calls setMeasured(true) and never setMeasured(false), so an explicit builder _dd.measured=false can't clear a measured state set earlier, regardless of this flag. Pre-existing since the tag was added in 2022, not introduced by this change. Whether the one-way behavior is an intentional "opt-in, never opt-out" ratchet (like forceKeep) or an oversight is unclear from history; fixing it (if desired) requires the interceptor itself to accept clearing plus interceptor-backed tags to participate in this ordering at all — tracked as a follow-up, out of scope for this phase-1a flag.

Testing: CoreSpanBuilderTest and all taginterceptor.* pass with the new order forced on; BuilderTagsPrecedenceTest pins the default (off) order, the flag-enabled order, and that the flag is read per-tracer (not a cached global default). Full dd-trace-core suite isn't clean to run locally (writer/intake/testcontainer tests fail for env reasons unrelated to this flag), deferred to CI.

Rollout: default-off → opt-in canary → telemetry on real collision rate (root spans only, not yet built) → flip default → remove historical path + flag.


tag: ai generated · default-off, no behavior change unless explicitly enabled.

🤖 Generated with Claude Code

…s win

Today the span builder applies tag contributors in this last-wins order:
mergedTracerTags, tagLedger (builder tags), coreTags (inbound header tags),
rootSpanTags, contextualTags. So inbound header / root-span / contextual tags
silently OVERRIDE explicit per-span tags set via the builder -- a wart flagged
in-code by Björn since 2020 ("maybe the builder tags should come last").

This adds an off-by-default flag that applies the ledger LAST so explicit
builder tags take precedence (the logical order), gated for gradual rollout.
The flag is a constant-folded `static final` (mirroring SPAN_BUILDER_REUSE_ENABLED)
so the JIT dead-code-eliminates the unused ordering branch on the hot span-build
path -- the flag is process-constant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dougqh dougqh added comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes type: feature Enhancements and improvements labels Jun 25, 2026
@dd-octo-sts

dd-octo-sts Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

🟡 Java Benchmark SLOs — Performance SLO warning (near threshold)

Suite Status
Startup 🟡 warning

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 14.84 s 14.77 s [-0.4%; +1.4%] (no difference)
startup:insecure-bank:tracing:Agent 13.65 s 13.70 s [-1.3%; +0.5%] (no difference)
startup:petclinic:appsec:Agent 17.67 s 17.40 s [+0.6%; +2.5%] (maybe worse)
startup:petclinic:iast:Agent 17.37 s 17.71 s [-2.6%; -1.1%] (significantly better)
startup:petclinic:profiling:Agent 17.54 s 17.24 s [+0.5%; +3.0%] (maybe worse)
startup:petclinic:sca:Agent 17.57 s 17.51 s [-0.5%; +1.3%] (no difference)
startup:petclinic:tracing:Agent 16.25 s 16.71 s [-6.9%; +1.4%] (no difference)

Commit: 36481036 · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Jul 20, 2026

Copy link
Copy Markdown

🎯 Code Coverage (details)
Patch Coverage: 69.23%
Overall Coverage: 59.12% (-0.02%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 3648103 | Docs | View more details | Give us feedback!

@dougqh dougqh changed the title Add trace.builder.tags.precedence.enabled to let explicit builder tags win Add trace.builder.tags.precedence.enabled to let explicit builder tags win (phase 1a) Jul 20, 2026
@dougqh
dougqh marked this pull request as ready for review July 22, 2026 16:09
@dougqh
dougqh requested a review from a team as a code owner July 22, 2026 16:09
@dougqh
dougqh requested a review from PerfectSlayer July 22, 2026 16:09

@datadog-datadog-us1-prod datadog-datadog-us1-prod Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Autotest was unable to start this review.

Please try again by commenting @autotest review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1de3faef5f

ℹ️ 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".

Comment thread internal-api/src/main/java/datadog/trace/api/Config.java
Comment thread dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java Outdated
Comment thread dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
- Add DD_TRACE_BUILDER_TAGS_PRECEDENCE_ENABLED to supported-configurations.json
  so it's recognized as documented config instead of tripping
  config-inversion/STRICT_TEST validation.
- Convert BUILDER_TAGS_PRECEDENCE from a static final (sourced from the global
  Config.get() singleton at class-load) to a final instance field read from
  the constructor's config param, so a CoreTracer built via
  CoreTracerBuilder#withProperties/#config actually honors its own
  configuration for this flag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dougqh
dougqh requested a review from a team as a code owner July 23, 2026 00:46
@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Jul 23, 2026

Copy link
Copy Markdown

Bits found no code fix to apply

🟢 Investigated · ⚪ No code fix needed

No actionable CI failures found for this PR — no code changes needed.


View in Datadog | Reviewed commit ddebb2b · Any feedback? Reach out in #deveng-pr-agent

@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Jul 26, 2026

Copy link
Copy Markdown

Bits has a CI fix ready

Warning

This comment is related to an earlier commit.

🟢 Investigated · 🟢 Fix prepared · ⚪ Validation skipped · 🟠 Ready

Updated metadata/agent-jar-checks.properties to raise the agent jar size budget by 11 bytes for the intentional builder-tag precedence implementation.

Commit fix to this PR


View in Datadog | Reviewed commit b994882 · Any feedback? Reach out in #deveng-pr-agent

@PerfectSlayer
PerfectSlayer requested review from a team and removed request for a team August 20, 2026 07:17

@datadog-datadog-us1-prod datadog-datadog-us1-prod Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datadog Autotest: FAIL

When the option is on, an earlier _dd.measured=true value still keeps the span measured after the builder sets _dd.measured=false. The measured state does not follow the new last-wins order.

Open Bits AI session

🤖 Datadog Autotest · Commit 0f090a0 · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

Comment thread dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
# Conflicts:
#	dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
#	metadata/supported-configurations.json

@sarahchen6 sarahchen6 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor comments but otherwise LGTM

Comment thread dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java Outdated
dougqh and others added 3 commits September 1, 2026 17:18
Moves the tag-ordering wart's historical context out of the in-code
comment (kept in the PR description instead) and adds test coverage
for trace.builder.tags.precedence.enabled=true and for per-tracer
(not global-default) flag resolution, per review discussion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…into dougqh/builder-tags-precedence

# Conflicts:
#	dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
@dougqh
dougqh requested review from mcculls and removed request for PerfectSlayer September 10, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes type: feature Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants