Skip to content

feat(data-pipeline)!: OTLP gRPC trace export - #2171

Draft
bm1549 wants to merge 1 commit into
brian.marks/otlp-grpc-transportfrom
brian.marks/otlp-grpc-trace-export
Draft

feat(data-pipeline)!: OTLP gRPC trace export#2171
bm1549 wants to merge 1 commit into
brian.marks/otlp-grpc-transportfrom
brian.marks/otlp-grpc-trace-export

Conversation

@bm1549

@bm1549 bm1549 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds OTLP gRPC as a trace-export transport, selectable via OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc. OtlpProtocol gains a Grpc variant; TraceExporter dispatches through a new OtlpExportMode (Http | Grpc) to either the existing HTTP path or the gRPC transport, with bounded exponential retry on transient IO matching the HTTP path. The OTLP resource info is built once at construction. gRPC is native-only; wasm32 rejects it at build. The FFI accepts grpc, so SDKs get gRPC export through libdatadog.

Stacked on #2273 (the fork-safe gRPC transport primitive). Review/merge that first; this PR's diff is only the export-integration layer on top of it.

Motivation

libdatadog's OTLP trace export was HTTP-only. Java and .NET do gRPC OTLP by hand; PHP, Python, Ruby, and Rust export through libdatadog FFI, so adding gRPC here unblocks all four and lets SDKs honor OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc instead of coercing it to HTTP.

Additional Notes

How to test the change?

  • cargo nextest run -p libdd-data-pipeline -p libdd-data-pipeline-ffi covers unit + FFI tests, including a public-API end-to-end gRPC export test (TraceExporterBuildersend → in-process h2 gRPC server that decodes the ExportTraceServiceRequest), and builder tests for grpc-endpoint success, no-endpoint agent fallback, and https rejection.
  • cargo clippy -p libdd-data-pipeline -p libdd-data-pipeline-ffi --all-targets -- -D warnings, cargo +nightly fmt --check, and cargo check -p libdd-data-pipeline --target wasm32-unknown-unknown --no-default-features (gRPC is wasm-gated) pass.

@bm1549 bm1549 added the AI Generated PR largely written by AI tools label Jun 26, 2026
@datadog-official

datadog-official Bot commented Jun 26, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 84.24%
Overall Coverage: 74.79% (+0.01%)

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

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

📚 Documentation Check Results

⚠️ 2559 documentation warning(s) found

📦 libdd-data-pipeline-ffi - 1335 warning(s)

📦 libdd-data-pipeline - 1224 warning(s)


Updated: 2026-07-24 20:46:31 UTC | Commit: 3807f28 | missing-docs job results

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🔒 Cargo Deny Results

⚠️ 6 issue(s) found, showing only errors (advisories, bans, sources)

📦 libdd-data-pipeline-ffi - 3 error(s)

Show output
error[vulnerability]: Invalid pointer dereference in `fmt::Pointer` impl for `Atomic` and `Shared` when the underlying pointer is invalid
   ┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:78:1
   │
78 │ crossbeam-epoch 0.9.18 registry+https://github.com/rust-lang/crates.io-index
   │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
   │
   ├ ID: RUSTSEC-2026-0204
   ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0204
   ├ Affected versions of `fmt::Display` dereference the underlying pointer. This causes a invalid pointer dereference e.g., when a pointer created with `Atomic::null` or `Shared::null`. `fmt::Debug` impls and pre-0.9 `fmt::Display` impls, which do not dereference pointers, are not affected by this issue.
   ├ Announcement: https://github.com/crossbeam-rs/crossbeam/pull/1276
   ├ Solution: Upgrade to >=0.9.20 (try `cargo update -p crossbeam-epoch`)
   ├ crossbeam-epoch v0.9.18
     └── crossbeam-deque v0.8.5
         └── rayon-core v1.12.1
             └── rayon v1.10.0
                 └── criterion v0.5.1
                     ├── libdd-common v5.1.0
                     │   ├── libdd-capabilities-impl v3.0.0
                     │   │   ├── libdd-data-pipeline v7.0.0
                     │   │   │   └── libdd-data-pipeline-ffi v37.0.0
                     │   │   ├── libdd-data-pipeline-ffi v37.0.0 (*)
                     │   │   ├── libdd-shared-runtime v2.0.0
                     │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   ├── libdd-data-pipeline-ffi v37.0.0 (*)
                     │   │   │   ├── libdd-dogstatsd-client v4.0.0
                     │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   │   └── libdd-trace-stats v6.0.0
                     │   │   │   │       └── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   ├── libdd-telemetry v6.0.0
                     │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   │   └── libdd-trace-stats v6.0.0 (*)
                     │   │   │   └── libdd-trace-stats v6.0.0 (*)
                     │   │   ├── (dev) libdd-telemetry v6.0.0 (*)
                     │   │   ├── libdd-trace-stats v6.0.0 (*)
                     │   │   └── libdd-trace-utils v9.0.0
                     │   │       ├── libdd-data-pipeline v7.0.0 (*)
                     │   │       ├── (dev) libdd-data-pipeline-ffi v37.0.0 (*)
                     │   │       ├── libdd-trace-obfuscation v5.0.0
                     │   │       │   └── libdd-trace-stats v6.0.0 (*)
                     │   │       ├── libdd-trace-stats v6.0.0 (*)
                     │   │       └── (dev) libdd-trace-utils v9.0.0 (*)
                     │   ├── libdd-common-ffi v37.0.0
                     │   │   └── libdd-data-pipeline-ffi v37.0.0 (*)
                     │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   ├── libdd-dogstatsd-client v4.0.0 (*)
                     │   ├── libdd-shared-runtime v2.0.0 (*)
                     │   ├── libdd-telemetry v6.0.0 (*)
                     │   ├── libdd-trace-obfuscation v5.0.0 (*)
                     │   ├── libdd-trace-stats v6.0.0 (*)
                     │   └── libdd-trace-utils v9.0.0 (*)
                     ├── (dev) libdd-data-pipeline v7.0.0 (*)
                     ├── (dev) libdd-ddsketch v1.1.0
                     │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   ├── libdd-telemetry v6.0.0 (*)
                     │   └── libdd-trace-stats v6.0.0 (*)
                     ├── (dev) libdd-trace-normalization v3.0.0
                     │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   └── libdd-trace-utils v9.0.0 (*)
                     ├── (dev) libdd-trace-obfuscation v5.0.0 (*)
                     ├── (dev) libdd-trace-stats v6.0.0 (*)
                     └── (dev) libdd-trace-utils v9.0.0 (*)

error[unsound]: Rand is unsound with a custom logger using `rand::rng()`
    ┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:226:1
    │
226 │ rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index
    │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unsound advisory detected
    │
    ├ ID: RUSTSEC-2026-0097
    ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0097
    ├ It has been reported (by [@lopopolo](https://github.com/lopopolo)) that the `rand` library is [unsound](https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library) (i.e. that safe code using the public API can cause Undefined Behaviour) when all the following conditions are met:
      
      - The `log` and `thread_rng` features are enabled
      - A [custom logger](https://docs.rs/log/latest/log/#implementing-a-logger) is defined
      - The custom logger accesses `rand::rng()` (previously `rand::thread_rng()`) and calls any `TryRng` (previously `RngCore`) methods on `ThreadRng`
      - The `ThreadRng` (attempts to) reseed while called from the custom logger (this happens every 64 kB of generated data)
      - Trace-level logging is enabled or warn-level logging is enabled and the random source (the `getrandom` crate) is unable to provide a new seed
      
      `TryRng` (previously `RngCore`) methods for `ThreadRng` use `unsafe` code to cast `*mut BlockRng<ReseedingCore>` to `&mut BlockRng<ReseedingCore>`. When all the above conditions are met this results in an aliased mutable reference, violating the Stacked Borrows rules. Miri is able to detect this violation in sample code. Since construction of [aliased mutable references is Undefined Behaviour](https://doc.rust-lang.org/stable/nomicon/references.html), the behaviour of optimized builds is hard to predict.
    ├ Announcement: https://github.com/rust-random/rand/pull/1763
    ├ Solution: Upgrade to >=0.10.1 OR <0.10.0, >=0.9.3 OR <0.9.0, >=0.8.6 (try `cargo update -p rand`)
    ├ rand v0.8.5
      ├── libdd-common v5.1.0
      │   ├── libdd-capabilities-impl v3.0.0
      │   │   ├── libdd-data-pipeline v7.0.0
      │   │   │   └── libdd-data-pipeline-ffi v37.0.0
      │   │   ├── libdd-data-pipeline-ffi v37.0.0 (*)
      │   │   ├── libdd-shared-runtime v2.0.0
      │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
      │   │   │   ├── libdd-data-pipeline-ffi v37.0.0 (*)
      │   │   │   ├── libdd-dogstatsd-client v4.0.0
      │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
      │   │   │   │   └── libdd-trace-stats v6.0.0
      │   │   │   │       └── libdd-data-pipeline v7.0.0 (*)
      │   │   │   ├── libdd-telemetry v6.0.0
      │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
      │   │   │   │   └── libdd-trace-stats v6.0.0 (*)
      │   │   │   └── libdd-trace-stats v6.0.0 (*)
      │   │   ├── (dev) libdd-telemetry v6.0.0 (*)
      │   │   ├── libdd-trace-stats v6.0.0 (*)
      │   │   └── libdd-trace-utils v9.0.0
      │   │       ├── libdd-data-pipeline v7.0.0 (*)
      │   │       ├── (dev) libdd-data-pipeline-ffi v37.0.0 (*)
      │   │       ├── libdd-trace-obfuscation v5.0.0
      │   │       │   └── libdd-trace-stats v6.0.0 (*)
      │   │       ├── libdd-trace-stats v6.0.0 (*)
      │   │       └── (dev) libdd-trace-utils v9.0.0 (*)
      │   ├── libdd-common-ffi v37.0.0
      │   │   └── libdd-data-pipeline-ffi v37.0.0 (*)
      │   ├── libdd-data-pipeline v7.0.0 (*)
      │   ├── libdd-dogstatsd-client v4.0.0 (*)
      │   ├── libdd-shared-runtime v2.0.0 (*)
      │   ├── libdd-telemetry v6.0.0 (*)
      │   ├── libdd-trace-obfuscation v5.0.0 (*)
      │   ├── libdd-trace-stats v6.0.0 (*)
      │   └── libdd-trace-utils v9.0.0 (*)
      ├── (dev) libdd-data-pipeline v7.0.0 (*)
      ├── (dev) libdd-ddsketch v1.1.0
      │   ├── libdd-data-pipeline v7.0.0 (*)
      │   ├── libdd-telemetry v6.0.0 (*)
      │   └── libdd-trace-stats v6.0.0 (*)
      ├── (dev) libdd-trace-normalization v3.0.0
      │   ├── libdd-data-pipeline v7.0.0 (*)
      │   └── libdd-trace-utils v9.0.0 (*)
      ├── (dev) libdd-trace-stats v6.0.0 (*)
      ├── libdd-trace-utils v9.0.0 (*)
      └── proptest v1.5.0
          └── (dev) libdd-tinybytes v1.1.1
              ├── libdd-data-pipeline v7.0.0 (*)
              ├── libdd-data-pipeline-ffi v37.0.0 (*)
              ├── (dev) libdd-tinybytes v1.1.1 (*)
              └── libdd-trace-utils v9.0.0 (*)

error[vulnerability]: Denial of Service via Stack Exhaustion
    ┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:297:1
    │
297 │ time 0.3.41 registry+https://github.com/rust-lang/crates.io-index
    │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
    │
    ├ ID: RUSTSEC-2026-0009
    ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0009
    ├ ## Impact
      
      When user-provided input is provided to any type that parses with the RFC 2822 format, a denial of
      service attack via stack exhaustion is possible. The attack relies on formally deprecated and
      rarely-used features that are part of the RFC 2822 format used in a malicious manner. Ordinary,
      non-malicious input will never encounter this scenario.
      
      ## Patches
      
      A limit to the depth of recursion was added in v0.3.47. From this version, an error will be returned
      rather than exhausting the stack.
      
      ## Workarounds
      
      Limiting the length of user input is the simplest way to avoid stack exhaustion, as the amount of
      the stack consumed would be at most a factor of the length of the input.
    ├ Announcement: https://github.com/time-rs/time/blob/main/CHANGELOG.md#0347-2026-02-05
    ├ Solution: Upgrade to >=0.3.47 (try `cargo update -p time`)
    ├ time v0.3.41
      └── tracing-appender v0.2.3
          └── libdd-log v1.0.0
              └── (dev) libdd-data-pipeline v7.0.0
                  └── libdd-data-pipeline-ffi v37.0.0

advisories FAILED, bans ok, sources ok

📦 libdd-data-pipeline - 3 error(s)

Show output
error[vulnerability]: Invalid pointer dereference in `fmt::Pointer` impl for `Atomic` and `Shared` when the underlying pointer is invalid
   ┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:75:1
   │
75 │ crossbeam-epoch 0.9.18 registry+https://github.com/rust-lang/crates.io-index
   │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
   │
   ├ ID: RUSTSEC-2026-0204
   ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0204
   ├ Affected versions of `fmt::Display` dereference the underlying pointer. This causes a invalid pointer dereference e.g., when a pointer created with `Atomic::null` or `Shared::null`. `fmt::Debug` impls and pre-0.9 `fmt::Display` impls, which do not dereference pointers, are not affected by this issue.
   ├ Announcement: https://github.com/crossbeam-rs/crossbeam/pull/1276
   ├ Solution: Upgrade to >=0.9.20 (try `cargo update -p crossbeam-epoch`)
   ├ crossbeam-epoch v0.9.18
     └── crossbeam-deque v0.8.5
         └── rayon-core v1.12.1
             └── rayon v1.10.0
                 └── criterion v0.5.1
                     ├── libdd-common v5.1.0
                     │   ├── libdd-capabilities-impl v3.0.0
                     │   │   ├── libdd-data-pipeline v7.0.0
                     │   │   ├── libdd-shared-runtime v2.0.0
                     │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   ├── libdd-dogstatsd-client v4.0.0
                     │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   │   └── libdd-trace-stats v6.0.0
                     │   │   │   │       └── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   ├── libdd-telemetry v6.0.0
                     │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   │   │   │   └── libdd-trace-stats v6.0.0 (*)
                     │   │   │   └── libdd-trace-stats v6.0.0 (*)
                     │   │   ├── (dev) libdd-telemetry v6.0.0 (*)
                     │   │   ├── libdd-trace-stats v6.0.0 (*)
                     │   │   └── libdd-trace-utils v9.0.0
                     │   │       ├── libdd-data-pipeline v7.0.0 (*)
                     │   │       ├── libdd-trace-obfuscation v5.0.0
                     │   │       │   └── libdd-trace-stats v6.0.0 (*)
                     │   │       ├── libdd-trace-stats v6.0.0 (*)
                     │   │       └── (dev) libdd-trace-utils v9.0.0 (*)
                     │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   ├── libdd-dogstatsd-client v4.0.0 (*)
                     │   ├── libdd-shared-runtime v2.0.0 (*)
                     │   ├── libdd-telemetry v6.0.0 (*)
                     │   ├── libdd-trace-obfuscation v5.0.0 (*)
                     │   ├── libdd-trace-stats v6.0.0 (*)
                     │   └── libdd-trace-utils v9.0.0 (*)
                     ├── (dev) libdd-data-pipeline v7.0.0 (*)
                     ├── (dev) libdd-ddsketch v1.1.0
                     │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   ├── libdd-telemetry v6.0.0 (*)
                     │   └── libdd-trace-stats v6.0.0 (*)
                     ├── (dev) libdd-trace-normalization v3.0.0
                     │   ├── libdd-data-pipeline v7.0.0 (*)
                     │   └── libdd-trace-utils v9.0.0 (*)
                     ├── (dev) libdd-trace-obfuscation v5.0.0 (*)
                     ├── (dev) libdd-trace-stats v6.0.0 (*)
                     └── (dev) libdd-trace-utils v9.0.0 (*)

error[unsound]: Rand is unsound with a custom logger using `rand::rng()`
    ┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:218:1
    │
218 │ rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index
    │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unsound advisory detected
    │
    ├ ID: RUSTSEC-2026-0097
    ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0097
    ├ It has been reported (by [@lopopolo](https://github.com/lopopolo)) that the `rand` library is [unsound](https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library) (i.e. that safe code using the public API can cause Undefined Behaviour) when all the following conditions are met:
      
      - The `log` and `thread_rng` features are enabled
      - A [custom logger](https://docs.rs/log/latest/log/#implementing-a-logger) is defined
      - The custom logger accesses `rand::rng()` (previously `rand::thread_rng()`) and calls any `TryRng` (previously `RngCore`) methods on `ThreadRng`
      - The `ThreadRng` (attempts to) reseed while called from the custom logger (this happens every 64 kB of generated data)
      - Trace-level logging is enabled or warn-level logging is enabled and the random source (the `getrandom` crate) is unable to provide a new seed
      
      `TryRng` (previously `RngCore`) methods for `ThreadRng` use `unsafe` code to cast `*mut BlockRng<ReseedingCore>` to `&mut BlockRng<ReseedingCore>`. When all the above conditions are met this results in an aliased mutable reference, violating the Stacked Borrows rules. Miri is able to detect this violation in sample code. Since construction of [aliased mutable references is Undefined Behaviour](https://doc.rust-lang.org/stable/nomicon/references.html), the behaviour of optimized builds is hard to predict.
    ├ Announcement: https://github.com/rust-random/rand/pull/1763
    ├ Solution: Upgrade to >=0.10.1 OR <0.10.0, >=0.9.3 OR <0.9.0, >=0.8.6 (try `cargo update -p rand`)
    ├ rand v0.8.5
      ├── libdd-common v5.1.0
      │   ├── libdd-capabilities-impl v3.0.0
      │   │   ├── libdd-data-pipeline v7.0.0
      │   │   ├── libdd-shared-runtime v2.0.0
      │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
      │   │   │   ├── libdd-dogstatsd-client v4.0.0
      │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
      │   │   │   │   └── libdd-trace-stats v6.0.0
      │   │   │   │       └── libdd-data-pipeline v7.0.0 (*)
      │   │   │   ├── libdd-telemetry v6.0.0
      │   │   │   │   ├── libdd-data-pipeline v7.0.0 (*)
      │   │   │   │   └── libdd-trace-stats v6.0.0 (*)
      │   │   │   └── libdd-trace-stats v6.0.0 (*)
      │   │   ├── (dev) libdd-telemetry v6.0.0 (*)
      │   │   ├── libdd-trace-stats v6.0.0 (*)
      │   │   └── libdd-trace-utils v9.0.0
      │   │       ├── libdd-data-pipeline v7.0.0 (*)
      │   │       ├── libdd-trace-obfuscation v5.0.0
      │   │       │   └── libdd-trace-stats v6.0.0 (*)
      │   │       ├── libdd-trace-stats v6.0.0 (*)
      │   │       └── (dev) libdd-trace-utils v9.0.0 (*)
      │   ├── libdd-data-pipeline v7.0.0 (*)
      │   ├── libdd-dogstatsd-client v4.0.0 (*)
      │   ├── libdd-shared-runtime v2.0.0 (*)
      │   ├── libdd-telemetry v6.0.0 (*)
      │   ├── libdd-trace-obfuscation v5.0.0 (*)
      │   ├── libdd-trace-stats v6.0.0 (*)
      │   └── libdd-trace-utils v9.0.0 (*)
      ├── (dev) libdd-data-pipeline v7.0.0 (*)
      ├── (dev) libdd-ddsketch v1.1.0
      │   ├── libdd-data-pipeline v7.0.0 (*)
      │   ├── libdd-telemetry v6.0.0 (*)
      │   └── libdd-trace-stats v6.0.0 (*)
      ├── (dev) libdd-trace-normalization v3.0.0
      │   ├── libdd-data-pipeline v7.0.0 (*)
      │   └── libdd-trace-utils v9.0.0 (*)
      ├── (dev) libdd-trace-stats v6.0.0 (*)
      ├── libdd-trace-utils v9.0.0 (*)
      └── proptest v1.5.0
          └── (dev) libdd-tinybytes v1.1.1
              ├── libdd-data-pipeline v7.0.0 (*)
              ├── (dev) libdd-tinybytes v1.1.1 (*)
              └── libdd-trace-utils v9.0.0 (*)

error[vulnerability]: Denial of Service via Stack Exhaustion
    ┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:288:1
    │
288 │ time 0.3.41 registry+https://github.com/rust-lang/crates.io-index
    │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
    │
    ├ ID: RUSTSEC-2026-0009
    ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0009
    ├ ## Impact
      
      When user-provided input is provided to any type that parses with the RFC 2822 format, a denial of
      service attack via stack exhaustion is possible. The attack relies on formally deprecated and
      rarely-used features that are part of the RFC 2822 format used in a malicious manner. Ordinary,
      non-malicious input will never encounter this scenario.
      
      ## Patches
      
      A limit to the depth of recursion was added in v0.3.47. From this version, an error will be returned
      rather than exhausting the stack.
      
      ## Workarounds
      
      Limiting the length of user input is the simplest way to avoid stack exhaustion, as the amount of
      the stack consumed would be at most a factor of the length of the input.
    ├ Announcement: https://github.com/time-rs/time/blob/main/CHANGELOG.md#0347-2026-02-05
    ├ Solution: Upgrade to >=0.3.47 (try `cargo update -p time`)
    ├ time v0.3.41
      └── tracing-appender v0.2.3
          └── libdd-log v1.0.0
              └── (dev) libdd-data-pipeline v7.0.0

advisories FAILED, bans ok, sources ok

Updated: 2026-07-24 20:47:48 UTC | Commit: 3807f28 | dependency-check job results

bm1549 added a commit that referenced this pull request Jun 27, 2026
The in-process exporter<->h2-server round trip is too timing-sensitive for
heavily contended CI runners: the client `send` has hit its request timeout on
macos-15 (GitHub) and the alpine/arm release-build matrix (GitLab) across
multiple runs, despite passing locally (20/20) and on less-loaded runners, and
despite the live backend verification in PR #2171 succeeding.

Mark it #[ignore] so it no longer runs in the default CI suites (it can't flake
the PR or burn release-build runner time), while keeping it runnable on demand
via `--run-ignored all`. The gRPC export path stays covered by this crate's unit
tests (ProstCodec, grpc_status_to_error, attach_metadata, build_grpc_channel,
builder dispatch, protocol parse) and by the live backend verification.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@dd-octo-sts

dd-octo-sts Bot commented Jun 27, 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.a 88.33 MB 92.68 MB +4.92% (+4.34 MB) ⚠️
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 8.13 MB 8.70 MB +6.98% (+582.33 KB) 🚨
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 99.62 MB 104.08 MB +4.48% (+4.46 MB) ⚠️
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.94 MB 11.73 MB +7.20% (+807.23 KB) 🚨
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 26.65 MB 28.39 MB +6.54% (+1.74 MB) 🚨
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 89.18 KB 89.18 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 191.80 MB 200.30 MB +4.43% (+8.50 MB) ⚠️
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 1.04 GB 1.11 GB +6.91% (+74.02 MB) 🚨
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 8.68 MB 9.37 MB +7.97% (+709.00 KB) 🚨
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 89.18 KB 89.18 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 25.40 MB 26.58 MB +4.67% (+1.18 MB) ⚠️
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 50.65 MB 53.81 MB +6.22% (+3.15 MB) 🚨
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 23.21 MB 24.78 MB +6.72% (+1.56 MB) 🚨
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 90.58 KB 90.58 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 196.50 MB 205.41 MB +4.53% (+8.91 MB) ⚠️
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 1.03 GB 1.10 GB +6.78% (+71.86 MB) 🚨
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 6.70 MB 7.23 MB +8.05% (+552.50 KB) 🚨
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 90.58 KB 90.58 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 27.32 MB 28.62 MB +4.77% (+1.30 MB) ⚠️
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 48.23 MB 50.85 MB +5.44% (+2.62 MB) 🚨
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 78.78 MB 82.70 MB +4.98% (+3.92 MB) ⚠️
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 9.07 MB 9.72 MB +7.12% (+662.34 KB) 🚨
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 94.28 MB 98.31 MB +4.27% (+4.03 MB) ⚠️
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 11.04 MB 11.84 MB +7.21% (+816.39 KB) 🚨

@bm1549
bm1549 marked this pull request as ready for review June 29, 2026 17:17
@bm1549
bm1549 requested review from a team as code owners June 29, 2026 17:17
@bm1549
bm1549 requested review from vpellan and removed request for a team June 29, 2026 17:17

@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: 9dcddce271

ℹ️ 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-data-pipeline/src/otlp/grpc_exporter.rs Outdated
Comment thread libdd-data-pipeline/src/otlp/grpc_exporter.rs Outdated
bm1549 added a commit that referenced this pull request Jul 10, 2026
The in-process exporter<->h2-server round trip is too timing-sensitive for
heavily contended CI runners: the client `send` has hit its request timeout on
macos-15 (GitHub) and the alpine/arm release-build matrix (GitLab) across
multiple runs, despite passing locally (20/20) and on less-loaded runners, and
despite the live backend verification in PR #2171 succeeding.

Mark it #[ignore] so it no longer runs in the default CI suites (it can't flake
the PR or burn release-build runner time), while keeping it runnable on demand
via `--run-ignored all`. The gRPC export path stays covered by this crate's unit
tests (ProstCodec, grpc_status_to_error, attach_metadata, build_grpc_channel,
builder dispatch, protocol parse) and by the live backend verification.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@bm1549
bm1549 force-pushed the brian.marks/otlp-grpc-trace-export branch from 9dcddce to 093ed21 Compare July 10, 2026 22:35
@pr-commenter

pr-commenter Bot commented Jul 10, 2026

Copy link
Copy Markdown

Benchmarks

Comparison

Benchmark execution time: 2026-07-24 21:16:28

Comparing candidate commit 812e43b in PR branch brian.marks/otlp-grpc-trace-export with baseline commit b7fb78d in branch brian.marks/otlp-grpc-transport.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 142 metrics, 0 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Benchmark execution time: 2026-07-24 21:31:43

Comparing candidate commit 812e43b in PR branch brian.marks/otlp-grpc-trace-export with baseline commit b7fb78d in branch brian.marks/otlp-grpc-transport.

Found 4 performance improvements and 4 performance regressions! Performance is the same for 169 metrics, 10 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:datadog_sample_span/complex_rule_partial_match/wall_time

  • 🟥 execution_time [+15.833ns; +16.138ns] or [+6.730%; +6.860%]

scenario:glob_matcher/ascii_wildcard_backtrack_match/wall_time

  • 🟩 execution_time [-3.286ns; -3.271ns] or [-7.952%; -7.914%]

scenario:glob_matcher/ascii_wildcard_heavy_backtrack/wall_time

  • 🟩 execution_time [-3.664ns; -3.634ns] or [-8.159%; -8.092%]

scenario:glob_matcher/ascii_wildcard_question_match/wall_time

  • 🟩 execution_time [-3.895ns; -3.846ns] or [-9.427%; -9.309%]

scenario:glob_matcher/ascii_wildcard_star_match/wall_time

  • 🟩 execution_time [-4.794ns; -4.772ns] or [-11.460%; -11.407%]

scenario:receiver_entry_point/report/2644

  • 🟥 execution_time [+149.050µs; +157.765µs] or [+4.054%; +4.291%]

scenario:trace_buffer/4_senders/no_delay

  • 🟥 execution_time [+190.801µs; +220.755µs] or [+8.334%; +9.643%]
  • 🟥 throughput [-140584.439op/s; -120841.376op/s] or [-8.928%; -7.674%]

Candidate

Omitted due to size.

Baseline

Omitted due to size.

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

First, apologies for waiting so long before giving you a first review. We'll avoid that kind of delay in the future.

As far as the PR goes, I have left a bunch of comments but there's no big technical blocker on my side. However I can't approve right away because the size impact is quite important. We don't have binary size quality gates (yet), but downstream consumers of libdatadog (tracers) do, which can lead to an unfortunate situation where this would get merged but tracers would be unable to update to future releases.

A first naive question: could/should this be feature-gated? I assume you'd want all tracers to support otlp out of the box, so the answers might be no, bu asking just in case.

A good test for the impact of the binary size increase: would you mind trying to create a PR against dd-trace-py pointing to this PR's libdatadog ref, and see if it triggers the size gates for e.g. serverless?

framed
}

pub fn grpc_export_benches(c: &mut Criterion) {

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.

What are we benchmarking in this function? I'm asking because we had slow benchmark jobs recently (and I've been one of the usual offender :whistling:). I would say we should only add benchmark if:

  • we bench internal code, and not external libraries (it can be useful to bench end-to-end since we do rely a lot on external libraries, but I don't think this is the right place for those benches)
  • we bench performance-sensitive code / code on the hot path
  • we are interested in the evolution of the performance of this code. Like, we want to avoid having regression here, or we want to optimize it in the future and thus would like to have a baseline.

Do you think those applies here?

Comment thread libdd-data-pipeline/src/otlp/config.rs Outdated
Comment on lines +11 to +18
/// All three OTel-standard protocol strings parse successfully; the selection
/// controls which send path the exporter uses:
/// - `http/json` and `http/protobuf` → OTLP over HTTP/1.1 via
/// [`HttpClientCapability`](libdd_capabilities::HttpClientCapability).
/// - `grpc` → OTLP over HTTP/2 via a tonic [`Channel`](tonic::transport::Channel).
///
/// Plaintext gRPC (`http://` scheme, port 4317) is supported. TLS gRPC
/// (`https://` scheme) is not yet implemented — use a TLS-terminating sidecar.

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: I find this comment confused and not very useful. I think the first line and each variant's description is enough.

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.

done in 812e43b

Comment thread libdd-data-pipeline/src/otlp/config.rs Outdated
Comment on lines +26 to +27
/// gRPC over HTTP/2. Protobuf-encoded body with 5-byte gRPC framing.
/// Default port is 4317. Only plaintext (`http://`) is supported.

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.

(Unless any of what comes after that in non-standard, I don't think it's relevant here)

Suggested change
/// gRPC over HTTP/2. Protobuf-encoded body with 5-byte gRPC framing.
/// Default port is 4317. Only plaintext (`http://`) is supported.
/// gRPC over HTTP/2

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.

done in 812e43b

Comment thread libdd-data-pipeline/src/otlp/config.rs Outdated
match self {
OtlpProtocol::HttpJson => libdd_common::header::APPLICATION_JSON,
OtlpProtocol::HttpProtobuf => libdd_common::header::APPLICATION_PROTOBUF,
OtlpProtocol::Grpc => unreachable!("gRPC path does not call content_type()"),

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.

I would avoid unreachable here, because it makes a hypothesis on every possible caller, past and future. Basically we don't have enough information to prove it's unreachable; if someone somedays adds code that call protocol.content_type() without filtering Grpc out, this will panic.

Suggestion: either return some default value that vaguely makes sense, or if there is not, make this function return an optional. The caller can then take the responsibility of unwrap(), but at least they have to make that choice explicitly.

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.

done in 812e43b

Comment thread libdd-data-pipeline/src/otlp/config.rs Outdated
OtlpProtocol::HttpProtobuf => {
Ok(libdd_trace_utils::otlp_encoder::encode_otlp_protobuf(req))
}
OtlpProtocol::Grpc => unreachable!("gRPC path does not call encode()"),

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.

Same remark.

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.

done in 812e43b

Comment on lines +68 to +70
///
/// Only one variant is active per instance; mutual exclusivity is enforced at
/// build time.

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.

Suggested change
///
/// Only one variant is active per instance; mutual exclusivity is enforced at
/// build time.

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.

OtlpExportMode reworked in the rebuild; it's a one-line doc now.

r.client_computed_stats = self.otlp_stats_enabled;
r
};
let resource_info = self.build_otlp_resource_info();

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.

Not really related to this PR, but should we cache the resource info in the struct instead of creating it and cloning strings on every request? Unless the metadata can change? Or maybe map_trace_to_otlp should take LanguageMetadata instead. Or make ResourceInfo borrowed. But it does sounds wasteful to clone 7ish strings on each send (that are probably not changing often or at all), just to pass a reference to them to map_traces_to_otlp after.

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.

Did it as a nice bonus in 812e43b but let me know if you'd prefer I split this out into another PR

Comment on lines 879 to 894
if self.otlp.is_some() {
libdd_trace_utils::span::trace_utils::drop_chunks(&mut traces);
if traces.is_empty() {
return Ok(AgentResponse::Unchanged);
}
return self.send_otlp_traces_inner(traces, config).await;
}
match &self.otlp {
Some(OtlpExportMode::Http(config)) => {
return self.send_otlp_traces_inner(traces, config).await;
}
#[cfg(not(target_arch = "wasm32"))]
Some(OtlpExportMode::Grpc(transport)) => {
return self.send_otlp_grpc_inner(traces, transport).await;
}
None => {}
}

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.

Suggested change
if self.otlp.is_some() {
libdd_trace_utils::span::trace_utils::drop_chunks(&mut traces);
if traces.is_empty() {
return Ok(AgentResponse::Unchanged);
}
return self.send_otlp_traces_inner(traces, config).await;
}
match &self.otlp {
Some(OtlpExportMode::Http(config)) => {
return self.send_otlp_traces_inner(traces, config).await;
}
#[cfg(not(target_arch = "wasm32"))]
Some(OtlpExportMode::Grpc(transport)) => {
return self.send_otlp_grpc_inner(traces, transport).await;
}
None => {}
}
if let Some(otlp) = &self.otlp {
libdd_trace_utils::span::trace_utils::drop_chunks(&mut traces);
if traces.is_empty() {
return Ok(AgentResponse::Unchanged);
}
match otlp {
OtlpExportMode::Http(config) => {
return self.send_otlp_traces_inner(traces, config).await;
}
#[cfg(not(target_arch = "wasm32"))]
OtlpExportMode::Grpc(transport) => {
return self.send_otlp_grpc_inner(traces, transport).await;
}
}
}

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.

done in 812e43b

Comment thread libdd-data-pipeline/Cargo.toml Outdated
Comment on lines +52 to +54
# tonic's transport (hyper/tokio/socket2) does not build for wasm32, so the gRPC
# OTLP transport — and prost, which only the gRPC exporter uses — are gated off
# wasm targets along with the code that uses them.

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.

Suggested change
# tonic's transport (hyper/tokio/socket2) does not build for wasm32, so the gRPC
# OTLP transport — and prost, which only the gRPC exporter uses — are gated off
# wasm targets along with the code that uses them.
# tonic's transport does not build for wasm32. Prost is only used by the gRPC
# exporter, so it's gated as well.

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.

Deps section was reworked for the new transport in #2273; that comment's shorter now.

#[cfg(test)]
mod grpc_export_tests {
use bytes::Bytes;
use h2::server;

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.

Any reason for using a h2 server instead of the usual http mock server we've been using elsewhere in tests?

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.

httpmock here is HTTP/1.1-only (no http2 feature) and has no trailer support, so it can't serve gRPC - the unary status rides in HTTP/2 trailers. That's why the test hand-rolls an h2 server.

@yannham

yannham commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

So, we've had a discussion with @VianneyRuhlmann about the whole worker situation. We believe we can side-step the "task in the background that needs to be restared" entirely by plugging in our own GrpcService implementation. It seems tonic provides a complicated default implementation through Channel with this background tower Service that reads through a channel and whatnot. But it's customizable and we can provide our own implementation of GrpcService instead, which seems to be rather simple (basically send some request). Doing so there's no background task anymore, we can get rid of the wait channel, and of the worker as well. It might even be possible to remove the "transport" feature entirely and save some binary size, though this has to be seen.

Wires OTLP gRPC trace export into TraceExporter on top of the fork-safe
gRPC transport, selectable via OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc.

OtlpProtocol gains a Grpc variant; the exporter dispatches through a new
OtlpExportMode (Http | Grpc) to either the existing HTTP path or the gRPC
transport (send_otlp_traces_grpc), with bounded exponential retry on
transient IO matching the HTTP path. The OTLP resource info is built once
at construction and shared by both paths. gRPC is native-only; wasm32
rejects it at build time. Includes a public-API end-to-end gRPC export test.

BREAKING CHANGE: adds the Grpc variant to the exhaustive public OtlpProtocol
enum, so exhaustive matches on it must add an arm. libdatadog consumers pin
by version and pick this up on the next release.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bm1549
bm1549 force-pushed the brian.marks/otlp-grpc-trace-export branch from 093ed21 to 812e43b Compare July 24, 2026 20:44
@bm1549
bm1549 changed the base branch from main to brian.marks/otlp-grpc-transport July 24, 2026 20:44
@bm1549
bm1549 marked this pull request as draft July 24, 2026 20:44
@bm1549 bm1549 changed the title feat(data-pipeline): OTLP gRPC trace export feat(data-pipeline)!: OTLP gRPC trace export Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated PR largely written by AI tools data-pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants