Skip to content

feat(data-pipeline)!: export client-computed span stats as OTLP trace metrics#2067

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 10 commits into
mainfrom
munir/otlp-trace-metrics
Jun 22, 2026
Merged

feat(data-pipeline)!: export client-computed span stats as OTLP trace metrics#2067
gh-worker-dd-mergequeue-cf854d[bot] merged 10 commits into
mainfrom
munir/otlp-trace-metrics

Conversation

@mabdinur

@mabdinur mabdinur commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Overview

Adds native OTLP trace-metrics export to `libdd-data-pipeline`. The span concentrator's per-cell stats are encoded as a `traces.span.sdk.metrics.duration` delta histogram and POSTed to a configured `/v1/metrics` endpoint on a 10s cadence. This is the shared Rust implementation consumed by dd-trace-py and other Datadog tracers — tracers only supply configuration.

Non-obvious details for reviewers

  • `count`/`sum`/`min`/`max` are exact (from per-cell accumulators, ns→s), not reconstructed from the DDSketch. The bucket distribution is projected from the DDSketch onto a fixed 17-bucket layout matching the OTel spanmetrics-connector defaults so payloads are comparable across tracers.
  • With OTLP metrics enabled, `check_agent_info` still runs V1 protocol negotiation but skips stats enable/disable — the concentrator starts unconditionally and is not affected by agent info.
  • `_dd.stats_computed: "true"` is added to every outbound OTLP trace `ResourceSpans` (and `Datadog-Client-Computed-Stats: yes` to the HTTP request) when stats are enabled. The Agent's OTLP receiver checks this attribute to skip its concentrator, preventing double-counted APM metrics. Set unconditionally regardless of OTel semantics mode. Both signals are backwards compatible with released Agents.
  • `grpc_method` is stored in `GroupedStats` (value side), not in `FixedAggregationKey`. This keeps it out of `Hash`/`PartialEq` so gRPC spans with different methods merge into one agent stats bucket — matching pre-existing behaviour. The method is still surfaced on `OtlpExactGroup` for OTLP data points. No breaking changes.
  • `TraceExporterBuilder::set_runtime_id` lets callers pass the language tracer's existing runtime ID. When set, it is shared across OTLP trace exports and OTLP trace-metrics so all signals can be correlated by the backend; when not set, a UUID is generated.
  • OTLP metrics exports forward `OTEL_EXPORTER_OTLP_METRICS_HEADERS` and `OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` from the builder, matching the behaviour of the OTLP trace exporter.

Testing: 160 nextest tests pass across `libdd-trace-stats` and `libdd-data-pipeline`. Companion: DataDog/dd-trace-py#18354.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Clippy Allow Annotation Report

Comparing clippy allow annotations between branches:

  • Base Branch: origin/main
  • PR Branch: origin/munir/otlp-trace-metrics

Summary by Rule

Rule Base Branch PR Branch Change
unwrap_used 4 5 ⚠️ +1 (+25.0%)
Total 4 5 ⚠️ +1 (+25.0%)

Annotation Counts by File

File Base Branch PR Branch Change
libdd-data-pipeline/src/otlp/metrics.rs 0 1 ⚠️ +1 (N/A)
libdd-data-pipeline/src/trace_exporter/mod.rs 2 2 No change (0%)
libdd-ddsketch/src/lib.rs 2 2 No change (0%)

Annotation Stats by Crate

Crate Base Branch PR Branch Change
clippy-annotation-reporter 5 5 No change (0%)
datadog-ffe-ffi 1 1 No change (0%)
datadog-ipc 22 21 ✅ -1 (-4.5%)
datadog-live-debugger 4 4 No change (0%)
datadog-live-debugger-ffi 10 10 No change (0%)
datadog-profiling-replayer 4 4 No change (0%)
datadog-sidecar 45 45 No change (0%)
libdd-common 13 13 No change (0%)
libdd-common-ffi 12 12 No change (0%)
libdd-data-pipeline 5 6 ⚠️ +1 (+20.0%)
libdd-ddsketch 2 2 No change (0%)
libdd-dogstatsd-client 1 1 No change (0%)
libdd-profiling 13 13 No change (0%)
libdd-remote-config 3 3 No change (0%)
libdd-telemetry 20 20 No change (0%)
libdd-tinybytes 4 4 No change (0%)
libdd-trace-normalization 2 2 No change (0%)
libdd-trace-obfuscation 3 3 No change (0%)
libdd-trace-stats 1 1 No change (0%)
libdd-trace-utils 11 12 ⚠️ +1 (+9.1%)
Total 181 182 ⚠️ +1 (+0.6%)

About This Report

This report tracks Clippy allow annotations for specific rules, showing how they've changed in this PR. Decreasing the number of these annotations generally improves code quality.

@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Jun 1, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 84.33%
Overall Coverage: 73.86% (+0.48%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 3bd3773 | Docs | Datadog PR Page | Give us feedback!

@codecov-commenter

codecov-commenter commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.94737% with 132 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.46%. Comparing base (a1da9fc) to head (cac8c75).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2067      +/-   ##
==========================================
+ Coverage   73.42%   73.46%   +0.04%     
==========================================
  Files         465      466       +1     
  Lines       77949    78552     +603     
==========================================
+ Hits        57231    57706     +475     
- Misses      20718    20846     +128     
Components Coverage Δ
libdd-crashtracker 65.46% <ø> (-0.02%) ⬇️
libdd-crashtracker-ffi 37.68% <ø> (ø)
libdd-alloc 98.77% <ø> (ø)
libdd-data-pipeline 86.33% <77.75%> (-0.80%) ⬇️
libdd-data-pipeline-ffi 77.03% <ø> (ø)
libdd-common 79.89% <ø> (ø)
libdd-common-ffi 74.41% <ø> (ø)
libdd-telemetry 73.34% <ø> (-0.03%) ⬇️
libdd-telemetry-ffi 31.36% <ø> (ø)
libdd-dogstatsd-client 82.64% <ø> (ø)
datadog-ipc 76.22% <ø> (ø)
libdd-profiling 81.69% <ø> (ø)
libdd-profiling-ffi 64.79% <ø> (ø)
libdd-sampling 97.41% <ø> (ø)
datadog-sidecar 34.59% <ø> (-0.02%) ⬇️
datdog-sidecar-ffi 8.61% <ø> (ø)
spawn-worker 48.86% <ø> (ø)
libdd-tinybytes 93.80% <ø> (ø)
libdd-trace-normalization 81.71% <ø> (ø)
libdd-trace-obfuscation 87.30% <ø> (ø)
libdd-trace-protobuf 68.25% <ø> (ø)
libdd-trace-utils 89.21% <ø> (ø)
libdd-tracer-flare 86.88% <ø> (ø)
libdd-log 74.83% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dd-octo-sts

dd-octo-sts Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Artifact Size Benchmark Report

aarch64-alpine-linux-musl
Artifact Baseline Commit Change
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 7.76 MB 7.76 MB +0% (+48 B) 👌
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 84.02 MB 84.55 MB +.63% (+548.25 KB) 🔍
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 95.13 MB 95.70 MB +.60% (+590.92 KB) 🔍
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.36 MB 10.44 MB +.74% (+79.29 KB) 🔍
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 24.93 MB 25.11 MB +.73% (+186.50 KB) 🔍
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 87.33 KB 87.33 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 181.51 MB 182.37 MB +.47% (+888.00 KB) 🔍
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 928.21 MB 932.36 MB +.44% (+4.14 MB) 🔍
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 8.12 MB 8.22 MB +1.26% (+105.00 KB) ⚠️
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 87.33 KB 87.33 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 24.03 MB 24.20 MB +.71% (+176.00 KB) 🔍
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 47.96 MB 48.33 MB +.76% (+375.97 KB) 🔍
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 21.62 MB 21.78 MB +.74% (+166.00 KB) 🔍
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 88.71 KB 88.71 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 185.58 MB 186.48 MB +.48% (+928.00 KB) 🔍
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 921.15 MB 925.39 MB +.45% (+4.23 MB) 🔍
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 6.27 MB 6.33 MB +.98% (+63.50 KB) 🔍
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 88.71 KB 88.71 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 25.76 MB 25.96 MB +.75% (+200.00 KB) 🔍
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 45.59 MB 45.94 MB +.75% (+354.10 KB) 🔍
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 74.91 MB 75.37 MB +.61% (+473.75 KB) 🔍
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 8.61 MB 8.68 MB +.72% (+64.03 KB) 🔍
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 90.33 MB 90.83 MB +.55% (+508.98 KB) 🔍
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.48 MB 10.56 MB +.69% (+74.78 KB) 🔍

@mabdinur
mabdinur force-pushed the munir/otlp-trace-metrics branch from ad8610e to dbc8c4b Compare June 15, 2026 19:46
@mabdinur mabdinur changed the title feat(data-pipeline): export client-computed span stats as OTLP trace metrics feat(data-pipeline)!: export client-computed span stats as OTLP trace metrics Jun 15, 2026
@mabdinur
mabdinur force-pushed the munir/otlp-trace-metrics branch from dbc8c4b to 43fdf9b Compare June 15, 2026 20:01
mabdinur and others added 5 commits June 15, 2026 16:46
Foundation pieces consumed by the OTLP trace-metrics exporter that follows.
These are pure additions with no breaking changes.

- libdd-ddsketch: `DDSketch::from_pb` rebuilds a sketch from its protobuf
  representation (or `None` when the mapping is missing/invalid); a thin
  `DDSketch::from_encoded` wraps protobuf decoding + `from_pb`. Lets callers
  read back the ok/error sketches that the span concentrator publishes.
  Includes a roundtrip test that goes `encode_to_vec` -> `from_encoded` and
  asserts bin count + total weight survive the trip.

- libdd-trace-utils: extend `OtlpResourceInfo` with two new fields:
  `hostname` (emitted as the `host.name` resource attribute when set) and
  `process_tags` (comma-separated `key:value` pairs, each becoming a
  `dd.<key>` resource attribute). The struct is `#[non_exhaustive]`, so
  adding fields is forward-compatible.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Make the span concentrator accumulate exact per-cell (ok/error) duration
totals and min/max in nanoseconds alongside the existing combined `duration`
that the /v0.6/stats agent payload uses, and publish them on a new sidecar
that the OTLP trace-metrics path can read.

- `GroupedStats` gains six pub(super) accumulators (`ok_duration`/`ok_min`/
  `ok_max` + the error trio) updated inside `insert`. They are seeded on the
  first span in each cell (count == 1) so the natural `0` default cannot
  masquerade as a real minimum.

- New public types `OtlpExactCell`, `OtlpExactGroup`, `OtlpStatsBucket` carry
  the exact scalars alongside an unmodified `pb::ClientStatsBucket`. The
  `grpc_method` field on `OtlpExactGroup` is intentionally introduced here
  but only ever populated with `String::new()`; a later commit fills it in.

- `StatsBucket::flush` now delegates to a new `flush_with_otlp_exact` which
  produces both the protobuf bucket (identical bytes) and the parallel
  sidecar. `SpanConcentrator::flush` and `flush_with_otlp_exact` share a
  generic `drain_due_buckets` helper so the bucket-window/buffer-len logic
  stays in one place.

- A new concentrator test drives the full path through `add_span` for 3 ok
  + 2 error spans and asserts each cell's count/duration/min/max plus
  `ok_duration + error_duration == group.duration` (the agent field).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…nfig

Prepare the data-pipeline OTLP layer to host a second exporter (trace
metrics) without changing the existing trace path's behavior or public API.

- `otlp/exporter.rs`: factor the actual POST + retry plumbing into a new
  crate-private `send_otlp_http(endpoint_url, headers, timeout, ...)` helper.
  `send_otlp_traces_http` becomes a thin wrapper that pulls fields out of
  `OtlpTraceConfig` and calls it; the existing public function signature is
  unchanged, so external callers see no diff. Two new pub(crate) constants
  (`OTLP_MAX_ATTEMPTS`, `OTLP_SHUTDOWN_MAX_ATTEMPTS`) replace the previous
  `OTLP_MAX_RETRIES` literal so the trace-metrics worker can use a single
  attempt on shutdown.

- `otlp/config.rs`: add `OtlpMetricsConfig` mirroring `OtlpTraceConfig`
  plus an `otel_trace_semantics_enabled` flag for `DD_TRACE_OTEL_SEMANTICS_ENABLED`.
  Annotated `#[allow(dead_code)]` until a follow-up commit consumes it.

- `trace_exporter/builder.rs`: factor the inline OTLP header-map builder
  out of `build_async` into a small `build_otlp_header_map` helper and
  refactor the existing OTLP traces config building to use it. No behavior
  change; this dedup makes the metrics-config branch trivial when it lands.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…metrics

Wire up the actual OTLP trace-metrics exporter on top of the foundation
pieces from earlier commits.

- New `libdd-data-pipeline/src/otlp/metrics.rs`:
  - `map_stats_to_otlp_metrics` builds an `ExportMetricsServiceRequest`
    JSON value from `&[OtlpStatsBucket]` (one histogram data point per
    aggregation-key (ok|error) cell). `count`/`sum`/`min`/`max` come from
    the sidecar's exact accumulators (ns -> s); `bucketCounts` is projected
    from the per-cell DDSketch onto a fixed 17-bucket spanmetrics-style
    layout. Empty cells are suppressed.
  - `OtlpStatsExporter<C>` runs as a `libdd_shared_runtime::Worker`:
    `trigger` waits one flush interval, `run` flushes + sends with
    `OTLP_MAX_ATTEMPTS`, `shutdown` force-flushes with
    `OTLP_SHUTDOWN_MAX_ATTEMPTS` (single attempt) so the final bucket is
    delivered inside the bounded shutdown window.
  - The mapper consumes `exact.grpc_method` (always empty here) so the
    later breaking-change commit only has to fill it in.

- `otlp/mod.rs`: declare the new `metrics` module, re-export
  `OtlpMetricsConfig` and `OtlpStatsExporter`, and extend the module-level
  doc to describe the trace-metrics path.

- `trace_exporter/builder.rs`: add `otlp_metrics_endpoint`,
  `otlp_metrics_headers` and `otel_trace_semantics_enabled` fields with
  matching setters (`set_otlp_metrics_endpoint`, `set_otlp_metrics_headers`,
  `enable_otel_trace_semantics`). When both an OTLP metrics endpoint and a
  stats bucket size are configured, spawn an `OtlpStatsExporter` worker on
  the shared runtime against an unconditionally-started
  `SpanConcentrator`; set a new `otlp_stats_enabled` flag on `TraceExporter`
  so the agent-info gate cannot later disable stats. The agent /v0.6/stats
  payload bytes are unchanged when no OTLP metrics endpoint is set.

- `trace_exporter/mod.rs`: add the `otlp_stats_enabled` field on
  `TraceExporter`.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add the gRPC method name to the aggregation key so spans sharing the same
service/resource/etc. but different `grpc.method.name` aggregate into
distinct groups, and surface the value via the OTLP trace-metrics sidecar
introduced earlier on this branch.

- `aggregation.rs`:
  - New `GRPC_METHOD_FIELD` lookup list (`grpc.method.name`, fallback
    `rpc.method`) consumed by a new `get_grpc_method` helper.
  - New `FixedAggregationKey<T>.grpc_method` field, appended at the END of
    the struct so the `PartialOrd` derive's field order (and therefore the
    ordering of any existing comparisons) is unaffected for the pre-existing
    fields.
  - `BorrowedAggregationKey::from_obfuscated_span` now picks up
    `grpc_method`; `OwnedAggregationKey::From<pb::ClientGroupedStats>` sets
    it to `""` (the agent stats protobuf does not carry it).
  - `StatsBucket::flush_with_otlp_exact` does `std::mem::take` on the key's
    `grpc_method` and moves it into `OtlpExactGroup.grpc_method` before
    encoding the agent payload, so the OTLP path reads it from the sidecar
    while the /v0.6/stats wire format stays byte-for-byte unchanged.
  - Aggregation test gains a case asserting that `grpc.method.name` (and
    by fallthrough, `rpc.method`) are extracted into the key.

- `datadog-ipc/src/shm_stats.rs`: the SHM concentrator's
  `FixedAggregationKey` test fixture grows a `grpc_method: ""` field.

BREAKING CHANGE: `FixedAggregationKey<T>` (re-exported from
`libdd_trace_stats::span_concentrator`) gains a public `grpc_method: T`
field. External callers that construct it via a struct literal must add
the field; callers using `Default::default()` are unaffected. The /v0.6/stats
agent protobuf wire format and behavior are unchanged.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@mabdinur
mabdinur force-pushed the munir/otlp-trace-metrics branch from 43fdf9b to f330f8b Compare June 15, 2026 20:47
@mabdinur
mabdinur marked this pull request as ready for review June 15, 2026 22:54
@mabdinur
mabdinur requested review from a team as code owners June 15, 2026 22:55
@mabdinur
mabdinur requested review from vpellan and removed request for a team June 15, 2026 22:55

@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: f330f8bee6

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

Comment thread libdd-trace-stats/src/span_concentrator/aggregation.rs Outdated
Comment thread libdd-data-pipeline/src/trace_exporter/mod.rs Outdated
mabdinur and others added 3 commits June 15, 2026 19:49
…n SDK computes stats

When otlp_stats_enabled, add _dd.stats_computed="true" to the OTLP ResourceSpans resource
attributes and Datadog-Client-Computed-Stats: yes to the HTTP request headers. The Agent's
OTLP receiver already checks both signals (otlp.go:372, otlp.go:272) and skips its
concentrator when either is set, preventing double-counted APM metrics.

The resource attribute survives Collector hops (unlike HTTP headers); the header covers direct
SDK→Agent connections. Both are backwards compatible: older Agents and non-Datadog OTLP
receivers silently ignore unknown resource attributes and headers.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… negotiation with OTLP stats

grpc_method was part of FixedAggregationKey (Hash+PartialEq), splitting same-service gRPC spans
into separate buckets that encode_grouped_stats then serialised with an empty method — producing
duplicate indistinguishable ClientGroupedStats rows on the /v0.6/stats path. Move it to
GroupedStats (value side), set on group creation, and surface it to OtlpExactGroup from there.
This also removes the one breaking change introduced by the prior commit.

check_agent_info returned before refresh_v1_active when otlp_stats_enabled, preventing V1
protocol negotiation for exporters that combine enable_v1_protocol with OTLP metrics. Move the
early return to after the V1 refresh so only stats enable/disable is skipped.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
… negotiation with OTLP stats

grpc_method was part of FixedAggregationKey (Hash+PartialEq), splitting same-service gRPC spans
into separate buckets that encode_grouped_stats then serialised with an empty method — producing
duplicate indistinguishable ClientGroupedStats rows on the /v0.6/stats path. Move it to
GroupedStats (value side), set on group creation, and surface it to OtlpExactGroup from there.
This also removes the one breaking change introduced by the prior commit.

check_agent_info returned before refresh_v1_active when otlp_stats_enabled, preventing V1
protocol negotiation for exporters that combine enable_v1_protocol with OTLP metrics. Move the
early return to after the V1 refresh so only stats enable/disable is skipped.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@gh-worker-ownership-write-b05516
gh-worker-ownership-write-b05516 Bot removed the request for review from a team June 16, 2026 00:47
@mabdinur
mabdinur requested a review from rachelyangdog June 16, 2026 13:50
Comment thread libdd-trace-utils/src/otlp_encoder/mapper.rs
const OTLP_MAX_RETRIES: u32 = 4;
/// Initial backoff between retries (milliseconds).
/// Max total attempts for OTLP export (initial + retries on transient failures).
pub(crate) const OTLP_MAX_ATTEMPTS: u32 = 5;

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.

nit: Any reason to use attempts instead of retry. I'm trying to converge to using retries as mixing the two can cause bugs.

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.

Fair nit — happy to rename back to retries for consistency. The rename to attempts was to make the semantics clearer (OTLP_SHUTDOWN_MAX_ATTEMPTS = 1 means one try, no retries), but retries is fine too if that's the established convention.

Comment thread libdd-trace-stats/src/span_concentrator/aggregation.rs
Comment thread libdd-data-pipeline/src/trace_exporter/builder.rs Outdated
Comment thread libdd-data-pipeline/src/trace_exporter/mod.rs

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

Looks good from the Data Pipeline point of view

- Rename OTLP_MAX_ATTEMPTS/OTLP_SHUTDOWN_MAX_ATTEMPTS to OTLP_MAX_RETRIES/
  OTLP_SHUTDOWN_MAX_RETRIES and rename the max_attempts parameter to
  max_retries throughout, converging on the retries convention used elsewhere
- Add TraceExporterBuilder::set_runtime_id so callers can supply the language
  tracer's existing runtime_id; falls back to a generated UUID when not set,
  ensuring OTLP trace exports and OTLP trace-metrics share the same runtime_id
  for backend correlation

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot merged commit cc2d696 into main Jun 22, 2026
89 checks passed
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot deleted the munir/otlp-trace-metrics branch June 22, 2026 02:52
gh-worker-dd-mergequeue-cf854d Bot pushed a commit that referenced this pull request Jun 26, 2026
# What does this PR do?

Removes \`grpc_method\` / \`rpc.method\` entirely from the OTLP trace metrics pipeline.

Previously \`grpc_method\` was a sidecar on \`GroupedStats\` — present in the OTLP metric payload as an explicit attribute, but not part of the aggregation key. This PR removes it from both:

- **\`FixedAggregationKey\`** — it is no longer an aggregation dimension.
- **\`OtlpExactGroup\`** — it is no longer carried in the per-cell sidecar.
- **\`build_attributes()\`** — \`rpc.method\` is no longer emitted on OTLP histogram data points.

## Rationale

For gRPC spans, \`span.resource\` already carries the full method path (e.g. \`/package.Service/Method\`). Emitting \`rpc.method\` as a separate OTLP attribute is redundant — \`span.resource\` (mapped to \`span.name\` on the data point) is the authoritative carrier of gRPC method identity and is already an aggregation dimension.

Adding \`rpc.method\` as a separate dimension would also increase cardinality for gRPC services without providing any information not already present on the metric.

Note: \`rpc.response.status_code\` (from \`grpc.status.code\`) is **kept** as a first-class aggregation dimension — it is not redundant with any existing field.

# Motivation

Correctness and cleanliness for the OTLP trace metrics feature landed in #2067.

# Risk

**Breaking changes (major semver):**

- \`FixedAggregationKey<T>\` loses its \`grpc_method: T\` field. Callers constructing the struct by name must remove the field.
- \`OtlpExactGroup\` loses its \`grpc_method\` field.
- \`build_attributes()\` loses the \`grpc_method\` parameter.

**Blast radius is limited** because:

1. The OTLP trace metrics feature (#2067) landed on \`main\` after the v36 release (June 19). No released version is affected — this only exists in unreleased code.
2. The OTLP metrics path is opt-in: the sidecar only activates it when \`set_otlp_metrics_endpoint\` is configured. No SDK enables this today.
3. Agent \`/v0.6/stats\` protobuf wire format is unchanged — \`ClientGroupedStats\` has no \`grpc_method\` field.

**Urgency: low.** No live production systems are affected. This must land before the next release that ships the OTLP metrics feature.

# How to test the change?

```
cargo nextest run -p libdd-trace-stats -p datadog-ipc -p libdd-data-pipeline
```

Co-authored-by: munir.abdinur <[email protected]>
gh-worker-dd-mergequeue-cf854d Bot pushed a commit that referenced this pull request Jun 29, 2026
## chore: release v37.0.0

Bumps the workspace version from `36.0.0` to `37.0.0` in `Cargo.toml` and regenerates `Cargo.lock`.

## Post-merge steps

1. Trigger the `create_release` job on GitLab — builds artifacts and creates a draft GitHub release.
2. Ask someone from `libdatadog-core` or `libdatadog-release` to publish the draft release.
3. Trigger the `release-proposal-dispatch` GitHub Actions workflow for per-crate crates.io publishing:
   crates: libdd-data-pipeline,libdd-trace-stats,libdd-trace-utils,libdd-ddsketch

## Notable changes since v36.0.0

- `feat(data-pipeline)`: export client-computed span stats as OTLP trace metrics (#2067)
- `feat(otel-thread-ctx)`: add self check capability (#2095)
- `fix(trace-stats)`: add `grpc_method` to aggregation key (#2151)
- `feat(data-pipeline)`: add stdout log trace exporter (#2074)
- `feat(remote-config)`: use the proto file from the agent (#2165)
- `feat(sidecar)`: expose `default_service_name` for `svc.*` process tags (#2053)
- feat(otlp)!: Export OTLP spans with attribute-level OTel compatibility (#2091)

Co-authored-by: munir.abdinur <[email protected]>
iunanua added a commit that referenced this pull request Jul 7, 2026
…ibdd-data-pipeline, libdd-li... (#2201)

# Release proposal for libdd-capabilities-impl, libdd-common,
libdd-data-pipeline, libdd-library-config, libdd-remote-config,
libdd-sampling, libdd-telemetry, libdd-tinybytes, libdd-trace-utils and
their dependencies

This PR contains version bumps based on public API changes and commits
since last release.

## libdd-capabilities
**Next version:** `2.1.0`
**Semver bump:** `minor`
**Tag:** `libdd-capabilities-v2.1.0`

### Commits

- feat(data-pipeline)!: add stdout log trace exporter (#2074)

## libdd-common
**Next version:** `5.1.0`
**Semver bump:** `minor`
**Tag:** `libdd-common-v5.1.0`

### Commits

- refactor(clippy): prefer core and alloc imports (#2196)
- fix: update rustls-webpki to 0.103.13 (#2187)
- fix: update anyhow for unsoundness (#2186)
- feat(machine id): Add helpers in ddcommon to fetch the machine UUID l…
(#2163)

## libdd-ddsketch
**Next version:** `1.1.0`
**Semver bump:** `minor`
**Tag:** `libdd-ddsketch-v1.1.0`

### Commits

- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- test(ddsketch): add microbenchmarks for add/encode/collapse (#2125)

## libdd-trace-protobuf
**Next version:** `4.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-protobuf-v4.0.0`

### Commits

- chore!: update protobufs to be in sync with datadog-agent (#2180)
- feat(stats)!: add whole key cardinality limit (#2158)
- feat(remote-config)!: use the proto file from the agent (#2165)
- feat(data-pipeline): OTLP HTTP/protobuf trace export (#2115)

## libdd-capabilities-impl
**Next version:** `3.0.0`
**Semver bump:** `major`
**Tag:** `libdd-capabilities-impl-v3.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.1.0 → ^5.1.0

### Commits

- feat(data-pipeline)!: add stdout log trace exporter (#2074)

## libdd-library-config
**Next version:** `3.0.0`
**Semver bump:** `major`
**Tag:** `libdd-library-config-v3.0.0`

### ⚠️ major bump forced due to:

- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0

### Commits

- refactor(clippy): prefer core and alloc imports (#2196)
- feat(library-config)!: caller-supplied threadlocal schema and extra
process-context attributes (#2162)
- fix(otel-thread-ctx): put the threadlocal attributes at the right
place in the context (#2167)

## libdd-remote-config
**Next version:** `2.0.0`
**Semver bump:** `major`
**Tag:** `libdd-remote-config-v2.0.0`

### ⚠️ major bump forced due to:

- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0

### Commits

- refactor(libdd-remote-config)!: hide Target inner properties so they
are not leaked (#2182)
- feat(remote-config)!: use the proto file from the agent (#2165)
- refactor(rc): reexport Endpoint and Tag common types (#2147)

## libdd-trace-normalization
**Next version:** `3.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-normalization-v3.0.0`

### ⚠️ major bump forced due to:

- `libdd-trace-protobuf`: ^3.0.1 → ^4.0.0

### Commits

- feat(data-pipeline)!: CSS Trace Filters (#1985)

## libdd-shared-runtime
**Next version:** `2.0.0`
**Semver bump:** `major`
**Tag:** `libdd-shared-runtime-v2.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.1.0 → ^5.1.0

### Commits

- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)
- feat(shared-runtime)!: use weak waker in trigger [APMSP-3371] (#2050)

## libdd-trace-utils
**Next version:** `9.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-utils-v9.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0

### Commits

- ci(miri): skip slow miri tests (#2188)
- chore!: update protobufs to be in sync with datadog-agent (#2180)
- feat(data-pipeline): add agentless export (#2081)
- feat(data-pipeline)!: add stdout log trace exporter (#2074)
- feat(data-pipeline): OTLP HTTP/protobuf trace export (#2115)
- feat(otlp)!: Export OTLP spans with attribute-level OTel compatibility
(#2091)
- test(trace-utils): add V05 msgpack decode microbenchmark (#2127)
- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- test(trace-utils): add VecMap microbenchmarks (#2126)
- chore(stats)!: submit p0 telemetry in stats (#2130)
- refactor(change-buffer)!: replace slot index with span_id, fix segment
isolation (#2105)
- feat(data-pipeline)!: CSS Trace Filters (#1985)
- feat(trace-exporter): add v1 span and its encoder (#2039)
- fix(trace-utils): mark decoded span maps as deduped (#2110)
- feat(trace-utils)!: change buffer implementation (#2055)
- feat(native-spans)!: change buffer foundation (#2046)
- refactor(span)!: use VecMap for `meta`, `metrics` and `meta_struct`
for v04 spans (#2043)
- test: fix timeouts on heavily contended scenarios (#2093)

## libdd-telemetry
**Next version:** `6.0.0`
**Semver bump:** `major`
**Tag:** `libdd-telemetry-v6.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-shared-runtime`: ^1.0.0 → ^2.0.0

### Commits

- ci(miri): skip slow miri tests (#2188)
- refactor(libdd-telemetry)!: avoid leaking libdd-common types in the
public API (#2152)
- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)

## libdd-trace-obfuscation
**Next version:** `5.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-obfuscation-v5.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0

### Commits

- refactor(clippy): prefer core and alloc imports (#2196)
- ci(miri): skip slow miri tests (#2188)
- fix: update anyhow for unsoundness (#2186)

## libdd-trace-stats
**Next version:** `6.0.0`
**Semver bump:** `major`
**Tag:** `libdd-trace-stats-v6.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-shared-runtime`: ^1.0.0 → ^2.0.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0

### Commits

- chore!: update protobufs to be in sync with datadog-agent (#2180)
- feat(stats)!: send telemetry for cardinality limits (#2159)
- feat(stats)!: add whole key cardinality limit (#2158)
- fix(trace-stats)!: add grpc_method to aggregation key (#2151)
- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)
- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- refactor(span)!: use VecMap for `meta`, `metrics` and `meta_struct`
for v04 spans (#2043)

## libdd-data-pipeline
**Next version:** `7.0.0`
**Semver bump:** `major`
**Tag:** `libdd-data-pipeline-v7.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-shared-runtime`: ^1.0.0 → ^2.0.0
- `libdd-telemetry`: ^5.0.1 → ^6.0.0
- `libdd-trace-protobuf`: ^3.0.2 → ^4.0.0
- `libdd-trace-stats`: ^5.0.0 → ^6.0.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0

### Commits

- feat(trace_exporter): enable telemetry in stats exporter (#2160)
- refactor(libdd-telemetry)!: avoid leaking libdd-common types in the
public API (#2152)
- feat(stats): emit canonical gRPC status name for OTLP
rpc.response.status_code (#2183)
- feat(data-pipeline): add agentless export (#2081)
- feat(stats)!: send telemetry for cardinality limits (#2159)
- feat(stats)!: add whole key cardinality limit (#2158)
- fix(trace-stats)!: add grpc_method to aggregation key (#2151)
- feat(data-pipeline)!: add stdout log trace exporter (#2074)
- feat(shared-runtime)!: SharedRuntime Borrowed & Owned mode (#2061)
- feat(data-pipeline): OTLP HTTP/protobuf trace export (#2115)
- feat(otlp)!: Export OTLP spans with attribute-level OTel compatibility
(#2091)
- feat(data-pipeline)!: export client-computed span stats as OTLP trace
metrics (#2067)
- chore(stats)!: submit p0 telemetry in stats (#2130)
- feat(data-pipeline)!: CSS Trace Filters (#1985)
- feat(shared-runtime)!: use weak waker in trigger [APMSP-3371] (#2050)
- refactor(span)!: use VecMap for `meta`, `metrics` and `meta_struct`
for v04 spans (#2043)
- feat(stats)!: add endpoint gating to client-side stats [APMSP-3361]
(#2040)

## libdd-dogstatsd-client
**Next version:** `4.0.0`
**Semver bump:** `major`
**Tag:** `libdd-dogstatsd-client-v4.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.1.0 → ^5.1.0


## libdd-sampling
**Next version:** `5.0.0`
**Semver bump:** `major`
**Tag:** `libdd-sampling-v5.0.0`

### ⚠️ major bump forced due to:

- `libdd-common`: ^4.2.0 → ^5.1.0
- `libdd-trace-utils`: ^8.0.0 → ^9.0.0


[APMSP-3371]:
https://datadoghq.atlassian.net/browse/APMSP-3371?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: iunanua <[email protected]>
taegyunkim pushed a commit that referenced this pull request Jul 8, 2026
# What does this PR do?

Removes \`grpc_method\` / \`rpc.method\` entirely from the OTLP trace metrics pipeline.

Previously \`grpc_method\` was a sidecar on \`GroupedStats\` — present in the OTLP metric payload as an explicit attribute, but not part of the aggregation key. This PR removes it from both:

- **\`FixedAggregationKey\`** — it is no longer an aggregation dimension.
- **\`OtlpExactGroup\`** — it is no longer carried in the per-cell sidecar.
- **\`build_attributes()\`** — \`rpc.method\` is no longer emitted on OTLP histogram data points.

## Rationale

For gRPC spans, \`span.resource\` already carries the full method path (e.g. \`/package.Service/Method\`). Emitting \`rpc.method\` as a separate OTLP attribute is redundant — \`span.resource\` (mapped to \`span.name\` on the data point) is the authoritative carrier of gRPC method identity and is already an aggregation dimension.

Adding \`rpc.method\` as a separate dimension would also increase cardinality for gRPC services without providing any information not already present on the metric.

Note: \`rpc.response.status_code\` (from \`grpc.status.code\`) is **kept** as a first-class aggregation dimension — it is not redundant with any existing field.

# Motivation

Correctness and cleanliness for the OTLP trace metrics feature landed in #2067.

# Risk

**Breaking changes (major semver):**

- \`FixedAggregationKey<T>\` loses its \`grpc_method: T\` field. Callers constructing the struct by name must remove the field.
- \`OtlpExactGroup\` loses its \`grpc_method\` field.
- \`build_attributes()\` loses the \`grpc_method\` parameter.

**Blast radius is limited** because:

1. The OTLP trace metrics feature (#2067) landed on \`main\` after the v36 release (June 19). No released version is affected — this only exists in unreleased code.
2. The OTLP metrics path is opt-in: the sidecar only activates it when \`set_otlp_metrics_endpoint\` is configured. No SDK enables this today.
3. Agent \`/v0.6/stats\` protobuf wire format is unchanged — \`ClientGroupedStats\` has no \`grpc_method\` field.

**Urgency: low.** No live production systems are affected. This must land before the next release that ships the OTLP metrics feature.

# How to test the change?

```
cargo nextest run -p libdd-trace-stats -p datadog-ipc -p libdd-data-pipeline
```

Co-authored-by: munir.abdinur <[email protected]>
Signed-off-by: Taegyun Kim <[email protected]>
taegyunkim pushed a commit that referenced this pull request Jul 8, 2026
## chore: release v37.0.0

Bumps the workspace version from `36.0.0` to `37.0.0` in `Cargo.toml` and regenerates `Cargo.lock`.

## Post-merge steps

1. Trigger the `create_release` job on GitLab — builds artifacts and creates a draft GitHub release.
2. Ask someone from `libdatadog-core` or `libdatadog-release` to publish the draft release.
3. Trigger the `release-proposal-dispatch` GitHub Actions workflow for per-crate crates.io publishing:
   crates: libdd-data-pipeline,libdd-trace-stats,libdd-trace-utils,libdd-ddsketch

## Notable changes since v36.0.0

- `feat(data-pipeline)`: export client-computed span stats as OTLP trace metrics (#2067)
- `feat(otel-thread-ctx)`: add self check capability (#2095)
- `fix(trace-stats)`: add `grpc_method` to aggregation key (#2151)
- `feat(data-pipeline)`: add stdout log trace exporter (#2074)
- `feat(remote-config)`: use the proto file from the agent (#2165)
- `feat(sidecar)`: expose `default_service_name` for `svc.*` process tags (#2053)
- feat(otlp)!: Export OTLP spans with attribute-level OTel compatibility (#2091)

Co-authored-by: munir.abdinur <[email protected]>
Signed-off-by: Taegyun Kim <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants