Skip to content

Apply SpanPrototype in decorator afterStart (phase 1b - SpanPrototype adoption) - #12037

Open
dougqh wants to merge 24 commits into
masterfrom
dougqh/decorator-base-prototype
Open

dougqh wants to merge 24 commits into
masterfrom
dougqh/decorator-base-prototype

Conversation

@dougqh

@dougqh dougqh commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Wires the SpanPrototype from #11894 into the decorator base classes, and modernizes the affected tests.

ProductionBaseDecorator / ServerDecorator / ClientDecorator now build a lazily-cached SpanPrototype whose extension chain mirrors the decorator hierarchy (super.buildSpanPrototype().extends_(...) → add this level's constants). afterStart applies it via span.setSpanType / span.setAllTags(prototype.tags()) / spanContext().setIntegrationName(...), replacing the previous N separate span.setTag(TagMap.Entry) calls.

This is behavior-identical: setAllTags runs the same constant tags through the same tag-interceptor path the per-tag calls used today; it's a consolidation, not a new code path. Bulk-share (skipping per-tag interception) is deliberately deferred to the dense-store / tag-registry work.

Tests — the four afterStart specs move off Spock mock-interaction assertions to a state-based harness:

  • RecordingSpan / RecordingSpanContext accumulate the applied state (extends the no-op ImmutableSpan, so only the ~7 mutators afterStart touches are overridden).
  • ExpectedSpanState builds the expected state per level and asserts it in one shot, with three leniency modes (identity / exact / allow-extra-tags) matching Spock's polymorphic feature-method inheritance down the decorator hierarchy.

Other specs (onPeerConnection / onConnection / onStatement / beforeFinish) are unchanged and still use mocks.

Motivation

Replace BaseDecorator.afterStart's per-tag setTag stamping with a single baked-once SpanPrototype applied via a fast bulk copy, and move the afterStart specs off brittle mock-interaction assertions onto a state-based harness that survives the consolidation.

Additional Notes

Stacked on #11894 (dougqh/span-prototype-api). Review/merge that first; this PR's base retargets to master once #11894 lands.

Drops the born-dead SpanPrototype.Builder.initInstrumentationNames(String[]) overload (no caller; initInstrumentationName covers the single-name case) — shows as a 1-line deletion against the #11894 base.

Test plan:

  • :dd-java-agent:agent-bootstrap:test — green (afterStart specs exercise the new setAllTags path)
  • :dd-java-agent:agent-bootstrap:spotbugsMain, spotlessJavaCheck — green
  • techdebt + perf-review over the branch diff — clean (no hot-path regression; afterStart is a consolidation of existing per-tag work)

🤖 Generated with Claude Code

dougqh and others added 5 commits July 22, 2026 08:11
The builder API (extends_/init*) plus its per-mechanism microbenchmark and a
pure-API test, split out from the combined span-prototype work so the
abstraction lands independently of the decorator demo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A prototype constant that is null or an empty CharSequence should be "no tag"
-- matching AgentSpan.setTag and the decorators' cached-Entry path -- not a
baked empty tag. Add TagMap.Entry.isEmptyValue as the single definition of an
empty value (both Entry.create overloads now delegate to it), and gate
SpanPrototype.Builder.initTag on it via the plain set(key, value) path so no
Entry is allocated (the wrong path once tags are stored densely).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Thread a SpanPrototype through span construction: AgentTracer gains
buildSpan/startSpan(SpanPrototype, operationName) (defaults seed identity only,
correct for the noop tracer, with an explicit NoopTracerAPI.startSpan override).
CoreTracer overrides buildSpan to seed the prototype's frozen constant tags in
buildSpanContext at the precedence slot just before the builder's own tags
(prototype and builder form one precedence atom; explicit builder tags win), and
overrides startSpan to seed builder-free via the static CoreSpanBuilder.startSpan
path (no MultiSpanBuilder allocation, mirroring startSpan(String,...)). Explicit
operationName wins; null falls back to the prototype's. Intercepted constants
(e.g. span.kind) seed through the interceptor so their context side-effects still
fire. Prototype params @nonnull.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…formly init*

BaseDecorator.afterStart sets the integration name as a side effect alongside
the component tag (setIntegrationName(component)), which IntegrationAdder later
serializes as _dd.integration. A prototype baking only the component tag would
drop that. Add initComponentAndIntegration(component): sets the component tag
AND records it as the integration name (inherited via extends_), applied via
setIntegrationName at construction.

Rename the builder setters to a uniform init* surface now that a component
sibling exists and to convey "everything here bakes the prototype's initial
state": initComponent -> initComponentOnly, instrumentationName ->
initInstrumentationName(s), operationName -> initOperationName, spanType ->
initSpanType. Accessors are unchanged. Renames are confined to
SpanPrototype.Builder and its callers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dd-trace-core JMH benchmark covering the full create -> (tag) -> finish
lifecycle, finished against a no-op DropWriter so -prof gc isolates
create/tag/finish allocation from serialization. Pairs baseline shapes
(web-server 7 tags, JDBC 9 tags; setTag and builder-withTag) with prototype
arms: buildSpan(SpanPrototype).start() and the builder-free startSpan(SpanPrototype).

Measured (Threads(8), -f3 -wi5 -i5 -prof gc): prototype construction cuts
gc.alloc.rate.norm ~-5% web (-80 B/op) / ~-10% jdbc (-120 B/op) vs baseline --
tracking the number of baked constants (fewer per-span TagMap.Entry allocations).
The builder-free startSpan is deterministic (no MultiSpanBuilder); buildSpan's
builder is escape-analyzed away in this shallow micro, so startSpan is the
EA-independent path for production's deeper/megamorphic call sites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dougqh

dougqh commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@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: refactoring labels Jul 22, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 284eae7d8a

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

@datadog-datadog-prod-us1

This comment has been minimized.

@datadog-datadog-prod-us1

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

Copy link
Copy Markdown
Contributor

Bits has a CI fix ready

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

SpanPrototype.Builder did not meet the coverage threshold because several initialization paths were untested. Added focused coverage for identity initialization, null bases and entries, and populated and empty tag inputs in internal-api/src/test/java/datadog/trace/bootstrap/instrumentation/api/SpanPrototypeTest.java.

Commit fix to this PR


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

@dd-octo-sts

dd-octo-sts Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

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.03 s 13.99 s [-0.3%; +0.9%] (no difference)
startup:insecure-bank:tracing:Agent 12.95 s 13.00 s [-1.0%; +0.2%] (no difference)
startup:petclinic:appsec:Agent 16.43 s 16.95 s [-7.3%; +1.2%] (no difference)
startup:petclinic:iast:Agent 16.89 s 16.98 s [-1.2%; +0.1%] (no difference)
startup:petclinic:profiling:Agent 16.74 s 16.78 s [-1.2%; +0.7%] (no difference)
startup:petclinic:sca:Agent 16.87 s 16.73 s [-0.2%; +1.8%] (no difference)
startup:petclinic:tracing:Agent 16.19 s 15.79 s [-1.8%; +6.7%] (no difference)

Commit: a9661638 · 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.

dougqh and others added 2 commits July 22, 2026 17:53
…tion through it

Introduce apply(SpanPrototype) as the single seam for stamping a prototype's
constant initial state. It applies span type, constant tags, and integration
name as fallback defaults -- only where the span has not already set them --
so it never clobbers explicit values, is order-independent, and self-neutralizes
once construction has already seeded the same prototype.

DDSpanContext.apply is the authoritative implementation (the context owns the
tag map and will host the eventual bulk-share fast path + identity short-circuit);
DDSpan.apply routes straight to it. The AgentSpan default is the best-effort
fallback for non-core spans. The construction path (CoreSpanBuilder) now calls
context.apply(prototype) instead of inlining the tag + integration-name seeding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… off mocks

Have BaseDecorator/ServerDecorator/ClientDecorator build a lazily-cached
SpanPrototype (extension chain mirroring the decorator hierarchy) and apply it
in afterStart via span.setSpanType/setAllTags/setIntegrationName, replacing the
per-Entry setTag calls. Behavior-identical: setAllTags runs the same constant
tags through the same interceptor path the per-tag calls used.

Migrate the four afterStart specs from Spock mock-interaction assertions to a
state-based harness (RecordingSpan/RecordingSpanContext accumulate applied
state; ExpectedSpanState asserts the whole state at once), with three leniency
modes matching Spock's polymorphic feature-method inheritance across the
decorator hierarchy. Other specs (onPeerConnection/onConnection/onStatement/
beforeFinish) are unchanged.

Also drop the born-dead SpanPrototype.Builder.initInstrumentationNames(String[])
overload (no caller); initInstrumentationName covers the single-name case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dougqh
dougqh force-pushed the dougqh/decorator-base-prototype branch from 284eae7 to 911fc57 Compare July 22, 2026 21:59
@dougqh dougqh changed the title Apply SpanPrototype in decorator afterStart Apply SpanPrototype in decorator afterStart (phase 1b) Jul 23, 2026
dougqh and others added 9 commits July 29, 2026 08:12
The builder API (extends_/init*) plus its per-mechanism microbenchmark and a
pure-API test, split out from the combined span-prototype work so the
abstraction lands independently of the decorator demo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A prototype constant that is null or an empty CharSequence should be "no tag"
-- matching AgentSpan.setTag and the decorators' cached-Entry path -- not a
baked empty tag. Add TagMap.Entry.isEmptyValue as the single definition of an
empty value (both Entry.create overloads now delegate to it), and gate
SpanPrototype.Builder.initTag on it via the plain set(key, value) path so no
Entry is allocated (the wrong path once tags are stored densely).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Thread a SpanPrototype through span construction: AgentTracer gains
buildSpan/startSpan(SpanPrototype, operationName) (defaults seed identity only,
correct for the noop tracer, with an explicit NoopTracerAPI.startSpan override).
CoreTracer overrides buildSpan to seed the prototype's frozen constant tags in
buildSpanContext at the precedence slot just before the builder's own tags
(prototype and builder form one precedence atom; explicit builder tags win), and
overrides startSpan to seed builder-free via the static CoreSpanBuilder.startSpan
path (no MultiSpanBuilder allocation, mirroring startSpan(String,...)). Explicit
operationName wins; null falls back to the prototype's. Intercepted constants
(e.g. span.kind) seed through the interceptor so their context side-effects still
fire. Prototype params @nonnull.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…formly init*

BaseDecorator.afterStart sets the integration name as a side effect alongside
the component tag (setIntegrationName(component)), which IntegrationAdder later
serializes as _dd.integration. A prototype baking only the component tag would
drop that. Add initComponentAndIntegration(component): sets the component tag
AND records it as the integration name (inherited via extends_), applied via
setIntegrationName at construction.

Rename the builder setters to a uniform init* surface now that a component
sibling exists and to convey "everything here bakes the prototype's initial
state": initComponent -> initComponentOnly, instrumentationName ->
initInstrumentationName(s), operationName -> initOperationName, spanType ->
initSpanType. Accessors are unchanged. Renames are confined to
SpanPrototype.Builder and its callers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A dd-trace-core JMH benchmark covering the full create -> (tag) -> finish
lifecycle, finished against a no-op DropWriter so -prof gc isolates
create/tag/finish allocation from serialization. Pairs baseline shapes
(web-server 7 tags, JDBC 9 tags; setTag and builder-withTag) with prototype
arms: buildSpan(SpanPrototype).start() and the builder-free startSpan(SpanPrototype).

Measured (Threads(8), -f3 -wi5 -i5 -prof gc): prototype construction cuts
gc.alloc.rate.norm ~-5% web (-80 B/op) / ~-10% jdbc (-120 B/op) vs baseline --
tracking the number of baked constants (fewer per-span TagMap.Entry allocations).
The builder-free startSpan is deterministic (no MultiSpanBuilder); buildSpan's
builder is escape-analyzed away in this shallow micro, so startSpan is the
EA-independent path for production's deeper/megamorphic call sites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion through it

Introduce apply(SpanPrototype) as the single seam for stamping a prototype's
constant initial state. It applies span type, constant tags, and integration
name as fallback defaults -- only where the span has not already set them --
so it never clobbers explicit values, is order-independent, and self-neutralizes
once construction has already seeded the same prototype.

DDSpanContext.apply is the authoritative implementation (the context owns the
tag map and will host the eventual bulk-share fast path + identity short-circuit);
DDSpan.apply routes straight to it. The AgentSpan default is the best-effort
fallback for non-core spans. The construction path (CoreSpanBuilder) now calls
context.apply(prototype) instead of inlining the tag + integration-name seeding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add getIntegrationName() (default null) to AgentSpanContext, symmetric with
the existing no-op setIntegrationName, so the default apply() can honor
never-clobber like DDSpanContext.apply instead of unconditionally overwriting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the existing buildSpan(String,...) noop contract so the prototype
builder path matches. startSpan(SpanPrototype) was already noop-safe. A
chainable NoopSpanBuilder to fix the null-vs-NoopSpan asymmetry is left to a
separate PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover every Builder branch and getter so the SpanPrototype.Builder jacoco
rule (branch >= 0.7, instr >= 0.8) that failed test_base is satisfied:
initInstrumentationNames null/empty/multi, extends_(null) + full copy,
initComponentAndIntegration set/empty, initTag(Object)/initTag(EntryReader)
null and non-null, and all five getters incl. integrationName().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dougqh
dougqh force-pushed the dougqh/span-prototype-api branch from eadba23 to c2d5fb9 Compare July 29, 2026 12:17
dougqh added 2 commits August 28, 2026 21:43
# Conflicts:
#	dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
…HEAD

# Conflicts:
#	dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/BaseDecorator.java
#	dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/ClientDecorator.java
#	dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/decorator/ServerDecorator.java
#	dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/instrumentation/decorator/BaseDecoratorTest.groovy
#	dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
#	internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentSpan.java
#	internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/SpanPrototype.java
#	internal-api/src/test/java/datadog/trace/bootstrap/instrumentation/api/SpanPrototypeTest.java
No caller uses this generic setter -- tag population goes through the
more specific component()/spanKind()/language() builder methods.
Base automatically changed from dougqh/span-prototype-api to master September 11, 2026 17:23
…se-prototype

# Conflicts:
#	dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
@dougqh
dougqh marked this pull request as ready for review September 15, 2026 19:37
@dougqh
dougqh requested a review from a team as a code owner September 15, 2026 19:37
@dougqh
dougqh requested review from mhdatie and removed request for a team September 15, 2026 19:37
@mhdatie
mhdatie removed their request for review September 15, 2026 19:38

@datadog-datadog-prod-us1 datadog-datadog-prod-us1 Bot 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.

Datadog Autotest: FAIL

Global span tags can now keep a conflicting span.kind or component value when a decorator starts a span. This can give client and server spans the wrong identity.

Open Bits AI session

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

* then any specialization). {@link #assertAppliedTo(RecordingSpan)} verifies the whole accumulated
* state at once instead of asserting individual mock interactions.
*/
final class ExpectedSpanState {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure how I feel about this testing method. I'm open to other ideas. Do we want a similar fluent-API to what was just introduced for smoke tests?

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.

Having a similar SpanMatcher would help consistency

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I wasn't necessarily planning on making a new assertion mechanism in this PR. I just didn't want to keep adding to the Groovy tests either.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Went with expectedSpan() (skipping the "Of") and gave it a SpanMatcher-style factory/Javadoc — statically imported so call sites now read expectedSpan().spanType(...).component(...)..., matching span().service(...).tag(...). Kept the assert methods and RecordingSpan-specific fields as-is since they check mock-call state rather than wire fields, so a full SpanMatcher port didn't fit. Pushed in a966163.

@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: 2d899a8b43

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

@dougqh dougqh changed the title Apply SpanPrototype in decorator afterStart (phase 1b) Apply SpanPrototype in decorator afterStart (phase 1b - adoption) Sep 15, 2026
@dougqh dougqh changed the title Apply SpanPrototype in decorator afterStart (phase 1b - adoption) Apply SpanPrototype in decorator afterStart (phase 1b - SpanPrototype adoption) Sep 15, 2026
dougqh and others added 2 commits September 15, 2026 15:55
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BaseDecorator.afterStart was reusing AgentSpan#apply's fill-absent
semantics, so a global tag from DD_TAGS/DD_TRACE_SPAN_TAGS on a
decorator-owned key (component, span.kind, span.type) could silently
suppress the decorator's own value, since the global tag is seeded
onto the span before afterStart runs. Add applyOverwriting, an
unconditional variant matching the old setTag/setSpanType behavior,
and point the decorator seam at it, keeping apply's fill-absent
semantics for the construction seam.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dougqh
dougqh requested review from a team as code owners September 15, 2026 20:58
@dougqh
dougqh requested review from mcculls and removed request for a team September 15, 2026 20:58

@datadog-datadog-prod-us1 datadog-datadog-prod-us1 Bot 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.

Datadog Autotest: FAIL

When split-by-tags has both component and language, server spans now use java as the service name. The old code uses the decorator component because it handles component last.

Open Bits AI session

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

if (spanType != null) {
setSpanType(spanType);
}
setAllTags(prototype.tags(), true);

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.

P2 Keep server tag processing order

Affected server spans use the wrong service name and split APM data into the java service.

Assertion details
  • Input: A server span when trace.split-by-tags contains both component and language.
  • Expected: Keep the old order. Handle language first and component last, so the decorator component sets the final service name.
  • Actual: Bulk tag processing handles component before language. The tag interceptor then leaves the service name as java.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest · Open Bits AI session

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.

This looks like a legitimate concern

@dougqh dougqh Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm a bit torn about this issue. I tend to think this falls into undefined behavior where the precedence rules are really accidental and untested.

However for the sake of progress, I'm willing to tolerate some ugly code and maintain compatibility whereever I reasonably can. I do think there may come a point where that won't be possible, but time will tell I suppose.

// The base spec runs polymorphically against every subclass decorator, so it only asserts the
// baseline identity every decorator applies, tolerating the tags subclasses layer on. Each
// level's exact tag set is asserted by its own afterStart spec.
ExpectedSpanState.expected()

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 statically imported expectedSpanOf()... method or similar might read better?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Okay, we can do that.

@mcculls mcculls 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.

Some of the comments could be sharpened, and it would be nice to align span matching with a similar fluent API to that added for smoke tests.

Otherwise only the codex comment about "server tag processing order" needs resolving before merge.

dougqh and others added 2 commits September 21, 2026 16:03
BaseDecorator now applies component/language/span.kind together via a
single SpanPrototype, so which one wins the service name for
trace.split-by-tags depended on TagMap iteration order instead of the
fixed precedence the old sequential setTag calls guaranteed (component
last, so component always won). Add SplitByTagsPriorities and gate
DDSpanContext's split-by-tags service name updates on it, so component
beats language regardless of application order.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rename ExpectedSpanState.expected() to expectedSpan() and switch call
sites to a static import, so usage reads like the smoke tests'
SpanMatcher.span()...tag(...) builder instead of a qualified factory
call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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: refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants