Conversation
Declare the export mode and semantics mode on the wire so the backend can measure OTLP adoption instead of inferring it at ingest time. Native payloads carry _dd.sdk.otlp_export=false once per payload (first span meta for v0.4, payload attributes for v1); OTLP exports carry _dd.sdk.otlp_export=true and datadog.sdk.semantics as resource attributes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Config Audit |
|
✅ All CI checks and tests passed. 🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 45224d7 | Docs | View more details | Give us feedback! |
Writing the marker on the writer goroutine mutated spans after they were handed off, which the race detector flagged. Set it under the trace lock in setTraceTagsLocked, like process tags, only when the agent writer is currently sending v0.4. The v1 payload attribute and OTLP resource attributes are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…keys Drop the agentExport field: native "false" applies to every non-OTLP export, so the existing otlpExportMode flag plus the effective protocol is enough. Move the marker keys next to the OTLP resource code. Compare the pre-grow v1 payloads decoded, since payload attributes now hold two keys and map iteration order makes the raw bytes nondeterministic. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Build the OTLP adoption resource attributes in buildBaseResourceAttrs so trace and metrics resources share them. Set the native marker on the first span of every chunk regardless of v0.4/v1, since the agent prefers the v1 payload attribute and the values agree, and skip it only for OTLP export. Drop the OTLP span-attribute filter, matching dd-trace-java. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…an.go datadog.sdk.semantics is only written in buildBaseResourceAttrs, so inline it like the other resource attribute keys. _dd.sdk.otlp_export is also used for span meta and the v1 payload attribute, so keep it with the other internal span keys. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The CI Visibility wrapper path can omit the native marker, and metrics-resource marker coverage remains incomplete.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Adds OTLP export and semantic-convention markers to trace and metrics payloads.
Changes:
- Adds OTLP resource attributes for export mode and semantics.
- Adds native v0.4/v1 chunk and payload markers.
- Updates marker, resource, and payload integrity tests.
The CI Visibility no-op tracer path may omit the native marker. Metrics-resource tests also need coverage for enabled OTel semantics and both markers.
| File | Summary |
|---|---|
ddtrace/tracer/stats_to_otlp_metrics.go |
Propagates markers to metrics resources. |
ddtrace/tracer/stats_to_otlp_metrics_test.go |
Updates metrics resource tests. |
ddtrace/tracer/spancontext.go |
Sets native markers on chunk-leading spans. |
ddtrace/tracer/spancontext_test.go |
Tests marker behavior across chunks and protocols. |
ddtrace/tracer/span.go |
Defines the marker key. |
ddtrace/tracer/span_to_otlp.go |
Adds OTLP resource attributes. |
ddtrace/tracer/span_to_otlp_test.go |
Tests trace resource markers. |
ddtrace/tracer/payload_v1.go |
Adds the v1 payload marker. |
ddtrace/tracer/payload_test.go |
Tests v1 marker encoding. |
ddtrace/tracer/payload_pregrow_validation_test.go |
Adjusts payload integrity validation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
BenchmarksBenchmark execution time: 2026-09-24 20:42:44 Comparing candidate commit 45224d7 in PR branch Found 0 performance improvements and 6 performance regressions! Performance is the same for 328 metrics, 1 unstable metrics, 1 flaky benchmarks without significant changes.
|

What does this PR do?
Adds two wire markers that let the backend attribute ingested spans to the SDK's export path and semantic conventions:
_dd.sdk.otlp_export:"false"goes into the meta of the first span of each chunk. It's set insetTraceTagsLockedunder the trace lock, the same place_dd.tags.processis set, and skipped when the tracer exports over OTLP (otlpExportMode)."false"also goes into the payload-levelattributesmap (field 10), next to_dd.tags.process, on the first push into a payload."true"is a resource attribute.datadog.sdk.semantics, an OTLP resource attribute:"otel"whenOTelSemanticsEnabled()is true (DD_TRACE_OTEL_SEMANTICS_ENABLED),"datadog"otherwise.Both resource attributes are built in
buildBaseResourceAttrs, so every OTLP resource carries them: the trace resource and the client-side stats metrics resource. This also lets future OTLP metrics and logs support pick them up.Motivation
Today, "how many customers run Datadog SDKs emitting OTLP" is answered by
dd.trace.intake.ingested_spans, tagged withsdk_name/span_source. Those tags are inferred at ingest time rather than declared by the tracer. Thedatadogreceiverhardcodestelemetry.sdk.name:Datadogon native payloads it converts, so a native payload that only passed through a Collector looks the same as one a Datadog SDK exported over OTLP. Andspan_sourcerecords which endpoint the payload reached, not the tracer's export mode.Declaring both modes on the wire lets the backend resolve them into explicit tags and measure adoption directly. Related changes: DataDog/dd-trace-java#12516, DataDog/dd-trace-dotnet#9310 and DataDog/dd-trace-js#10482.
Coordinated change: this PR emits the markers. The Agent (DataDog/datadog-agent#56749) hoists
_dd.sdk.otlp_exportinto the outgoing payload,logs-backendpropagates it into chunk tags, anddd-goresolves it into thedatadog.sdk.otlp_exportmetric tag. The wire marker and the metric tag are named differently on purpose.Notes for reviewers:
payloadV04.push, which mutated spans on the writer goroutine after they had been handed off. The race detector flagged it inTestTracerReportsHostname. The marker is now set before hand-off, under the trace lock, on the first span of each chunk. That matches the RFC's wording and how process tags are handled here; dd-trace-java writes it once per payload."false". This avoids gating on the effective protocol, which can change at runtime."false"while the OTLP resource says"true". The agent's OTLP receiver deliberately doesn't hoist, and OTLP-origin spans keep the value in span meta, so the contradictory value would reach intake. The check reuses the existingotlpExportModefield. CI Visibility and the log/stdout (Lambda) writers are native Datadog export and get"false".attributesmap now holds two keys and is encoded in map iteration order. SoTestPayloadV1PreGrowWireIntegritycompares the size-hinted and reference payloads decoded (plus byte length) instead of byte-for-byte.TestOTLPExportMarkerOnChunks: partial flush makes two chunks; the marker is on each chunk's first span under v0.4 and v1, and absent under OTLP.gofmtandgo vetare clean. The affectedddtrace/tracertests pass with-race.TestAutoDetectStatsdfails locally, as it did on main in an earlier local run (it looks sensitive to a local agent).scripts/checklocks.shwasn't run locally because the tool isn't installed here; CI covers it.Reviewer's Checklist
make lintlocally.make testlocally.make generatelocally.make fix-moduleslocally.🤖 Generated with Claude Code