feat(tracer): wire OTLP span metrics into tracer flush path#4900
feat(tracer): wire OTLP span metrics into tracer flush path#4900rachelyangdog wants to merge 18 commits into
Conversation
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 8c04d86 | Docs | Datadog PR Page | Give us feedback! |
Codecov Report❌ Patch coverage is Additional details and impacted files
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 539a23ef3e
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
BenchmarksBenchmark execution time: 2026-07-23 20:02:05 Comparing candidate commit 8c04d86 in PR branch Found 0 performance improvements and 6 performance regressions! Performance is the same for 320 metrics, 0 unstable metrics, 1 known flaky benchmarks.
|
32cba4f to
043ce14
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 043ce14f49
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
043ce14 to
fd2834e
Compare
053dd7f to
98e8123
Compare
fd2834e to
2e9aa5e
Compare
98e8123 to
167f231
Compare
2e9aa5e to
dc1c997
Compare
Config Audit |
167f231 to
6050a97
Compare
dc1c997 to
cd990d9
Compare
6050a97 to
3bada97
Compare
cd990d9 to
2ddc244
Compare
92fe58f to
3bce049
Compare
9c0fc21 to
1eca7a0
Compare
2a6ce8c to
fe80161
Compare
1eca7a0 to
faba486
Compare
feddb4b to
bc12a8f
Compare
7e1dd9d to
5df63a6
Compare
0b26f2b to
3df57a5
Compare
… path (PR 4) - Add otlpExporter field to concentrator; flushAndSend branches to OTLP or native /v0.6/stats - Add newOTLPMetricsConcentrator constructor; select it in newUnstartedTracer when OTLPSpanMetricsEnabled - FR15: set Datadog-Client-Computed-Stats: yes header when OTLPSpanMetricsEnabled - FR15: append _dd.stats_computed=true resource attr on OTLP trace payloads - Fix sketchToHistogram to decode protobuf-serialized DDSketches (agent format) - Add SetOTLPSpanMetricsEnabled setter in internal/config for test wiring Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ics flush interval Two bugs prevented OTLP span metrics from being exported in system tests: 1. _DD_TRACE_METRICS_OTEL_FLUSH_INTERVAL was read via env.Get() (allowlist), which returned "" for this internal-only override, defaulting to 10s. Switch to os.Getenv for this test-only internal variable. 2. tracer.Flush() called flushAndSend() directly while spans may still be pending in the concentrator's In channel (not yet processed by runIngester). Drain the In channel before flushing when includeCurrent=true so spans submitted just before Flush() are included in the export. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add otlpPeerTags to concentrator; inject span.kind="client" only when the span carries a peer-tag value (http.route, grpc.method.name) so non-top-level unmeasured spans are not made eligible via eligibleSpanKind - Set Datadog-Client-Computed-Stats header statically in transport headers when OTLPSpanMetricsEnabled so it is present from the first request - Pass transport headers to checkEndpoint startup probe so the header is included on the initial 0-trace-chunk connection check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace for-loop map copy with maps.Copy in stats.go (mapsloop) - Replace bare int32/int64 atomic vars with atomic.Int32/Int64 types in otlp_transport_test.go and otlp_writer_test.go (atomictypes) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e as first-class field
_dd.stats_computed is a string-valued Datadog convention; change from BoolValue(true)
to StringValue("true") to match libdatadog and the system-test spec.
http.route (ext.HTTPRoute) is now captured as the HTTPEndpoint first-class field
with a fallback after ext.HTTPEndpoint, so OTel-instrumented spans get http.route
emitted as a data-point attribute via buildDataPointAttributes. Remove "http.route"
from otlpDefaultPeerTags since it had no effect on attribute emission (only grpc.method.name
peer tags are converted to attributes).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…port field Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…feedback) Remove the goroutine + shared WaitGroup pattern from flush(). The shared WaitGroup caused a misuse panic when multiple goroutines called flush() concurrently (each calling Add then Wait on the same WaitGroup). Making flush synchronous also fixes FR15_3: tracer.Flush() now blocks until the OTLP trace payload reaches the collector, consistent with how the concentrator sends metrics synchronously. The worker goroutine already serialises flush calls in production; the climit concurrency cap was redundant and is removed along with the WaitGroup fields. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…es back to t The t→yes rename was an unrelated silent behavior change riding along with the OTLPSpanMetricsEnabled condition. The agent checks header presence, not value, so both are functionally equivalent, but keeping t avoids a gratuitous diff. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…emanticsEnabled) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…view) - Remove grpc.method.name from otlpDefaultPeerTags: peer tags increase stats bucket cardinality per gRPC method, contradicting the June 25 spec change that removed rpc.method to prevent cardinality inflation. Peer tags are explicitly out of scope per the RFC attribute table. - Fix BucketInterval hardcoded to defaultStatsBucketSize in newConcentrator; pass bucketSize so _DD_TRACE_STATS_INTERVAL controls the aggregation window. - Fix redundant s.meta.Map(false) call in peer-tag span.kind injection path. - Extract sendWithRetry helper to deduplicate identical retry loops in flushAndSend's OTLP and native /v0.6/stats branches. - Add comment explaining Datadog-Client-Computed-Stats value change from "t" to "yes" (spec-correct per FR15; both values accepted by the Agent). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…4 downgrade for OTLP span metrics - Emit tracer_dd_tags array resource attribute for non-reserved DD_TAGS/ OTEL_RESOURCE_ATTRIBUTES key:value pairs (fixes fr08_10, fr08_11) - Emit additional_metric_tags array data-point attribute from ClientGroupedStats.AdditionalMetricTags (fixes fr08_12) - Downgrade trace transport to v0.4 when OTLP span metrics are enabled so the Datadog-Client-Computed-Stats header is visible to the test agent (fixes fr02_3, fr15_1) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… and v0.4 downgrade for OTLP span metrics" This reverts commit 2183b17.
…s and v0.4 downgrade for OTLP span metrics" This reverts commit 861a599.
…15_3) The test_fr15_3 system test decodes the OTLP trace payload body as UTF-8 JSON. The writer was sending protobuf, causing a UnicodeDecodeError. Switch proto.Marshal → protojson.Marshal and update the content type to application/json. Update unit tests to decode with protojson.Unmarshal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…fixes fr15_3)" This reverts commit 44e38fe.
4e8c55a to
a597b80
Compare
…P attrs Both were Go-specific extensions carrying over DD_TAGS/DD_TRACE_STATS_ADDITIONAL_TAGS semantics from the native /v0.6/stats path, but neither appears in the RFC's attribute tables. datadog.<key> per-process-tag resource attributes (which are RFC-required) are unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ng/otlp-span-metrics-wireup # Conflicts: # ddtrace/tracer/stats.go
OTLP Span Metrics — Part 4 of 4: Tracer Integration
Wires the OTLP span metrics exporter into the tracer.
Stack order (merge in sequence):
otlp-span-metrics-config— configuration (PR feat(config): add OTLP span metrics config #4895)otlp-span-metrics-conversion— ClientStatsPayload → OTLP protobuf conversion (PR feat(tracer): stats to OTLP histogram conversion for OTLP span metrics #4897)otlp-span-metrics-transport— OTLP HTTP exporter (PR feat(tracer): add OTLP metrics HTTP exporter for span metrics #4899)What this PR does
Tracer integration (
tracer.go,stats.go):OTLPSpanMetricsEnabled=true, instantiates anewOTLPMetricsConcentratorinstead of the standard concentrator; this routes flushed stats to the OTLP endpoint rather than the agent's/v0.6/statspathPeer tag handling (
stats.go):otlpPeerTagsto the concentrator; when set, overrides agent-advertised peer tags with a fixed OTel-convention set (http.route,grpc.method.name)span.kind=clientonly when the span carries a peer-tag value, so non-top-level unmeasured spans are not incorrectly made eligible for stats computationDatadog-Client-Computed-Statsheader (option.go,log.go):OTLPSpanMetricsEnabled=true, ensuring it is present from the very first request (including the startup probe), before the agent/infopoll completesTesting
tests/parametric/test_otlp_trace_metrics.py— 44 passed, 2 xfailedotlp_metrics_wireup_test.goMotivation
Reviewer's Checklist
make lintlocally.make testlocally.make generatelocally.make fix-moduleslocally.Unsure? Have a question? Request a review!