Skip to content

feat(otlp): support http/protobuf OTLP trace export#18609

Draft
bm1549 wants to merge 7 commits into
mainfrom
brian.marks/otlp-http-protobuf-export
Draft

feat(otlp): support http/protobuf OTLP trace export#18609
bm1549 wants to merge 7 commits into
mainfrom
brian.marks/otlp-http-protobuf-export

Conversation

@bm1549

@bm1549 bm1549 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

Adds http/protobuf as a second OTLP trace-export encoding alongside the existing http/json path, and makes it the default. The encoding is selected from the OTel-standard OTEL_EXPORTER_OTLP_TRACES_PROTOCOL value and passed to libdatadog via the new set_otlp_protocol builder setter.

  • http/protobuf (the default) and http/json are supported over HTTP.
  • Any other value (e.g. the OTel-default grpc) is coerced to http/protobuf.

This PR also bumps libdatadog from v35.0.0 to the merged OTLP commit (4e8e6cc8c, DataDog/libdatadog#2115). That bump pulls in the datadog-remote-configlibdd-remote-config rename and the redesigned remote-config parsing API, so tracer_flare.rs is ported to parse per-product via RemoteConfigContent (AgentConfigFile/AgentTaskFile).

DataDog/libdatadog#2115 is merged. The rev is pinned to the merge commit (4e8e6cc8c) because no libdatadog release includes it yet; repoint to a release tag once one ships.

Testing

  • cargo check --features stats,profiling,crashtracker,ffe (the production feature set) passes against the merged libdatadog rev.
  • Backend verification (local DD Agent to datadoghq.com): emitted protobuf-only OTLP traces from a fresh service and confirmed ingestion. The wire was application/x-protobuf (HTTP 200), and spans landed with correct service/resource names and a preserved 128-bit trace id. http/json regression also confirmed (application/json wire, HTTP 200).

Risks

  • OTLP trace export now defaults to http/protobuf; set OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/json for the previous JSON wire format. Only the encoding changes; the endpoint (/v1/traces on port 4318) serves both.
  • The behavioral change is gated on _otlp_endpoint being set; non-OTLP export is unaffected.
  • The libdatadog v35→merged bump is the larger surface: it touches the renamed crate and the remote-config parse API used by tracer flare. The native extension compiles clean against the merged rev.

Additional Notes

The protocol coercion happens on the Python side (writer.py), so the native set_otlp_protocol only ever receives http/json or http/protobuf. This matches libdatadog, which rejects grpc at the parse boundary.

bm1549 and others added 2 commits June 12, 2026 17:15
Bumps the libdatadog git rev from v35.0.0 to 664f16fb (the OTLP
HTTP/protobuf branch, libdatadog#2115). Renames the renamed crate
datadog-remote-config -> libdd-remote-config and ports tracer_flare.rs
to the redesigned RemoteConfigContent parse API (per-product parsing
via AgentConfigFile/AgentTaskFile instead of the removed
RemoteConfigData enum). Regenerates Cargo.lock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wires the new libdatadog set_otlp_protocol builder setter so OTLP trace
export honors OTEL_EXPORTER_OTLP_TRACES_PROTOCOL. http/json (default)
and http/protobuf are supported over HTTP; any other value (e.g. the
OTel grpc default) falls back to http/json, preserving prior behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bm1549 bm1549 added the AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos label Jun 12, 2026
@datadog-official

datadog-official Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Pipelines  Tests

Fix all issues with BitsAI

⚠️ Warnings

🚦 12 Pipeline jobs failed

System Tests | tracer-release / End-to-end #7 / flask-poc 7   View in Datadog   GitHub Actions

🧪 6 Tests failed

tests.appsec.api_security.test_endpoints.Test_LLM_Endpoint.test_openai_async_chat_completions_create[flask-poc] from system_tests_suite   View in Datadog
KeyError: &#39;appsec.events.llm.call.provider&#39;

self = &lt;tests.appsec.api_security.test_endpoints.Test_LLM_Endpoint object at 0x7f18e9285bb0&gt;

    def test_openai_async_chat_completions_create(self):
&gt;       assert_llm_span(self.request, self.MODEL)

tests/appsec/api_security/test_endpoints.py:66: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

...
tests.appsec.api_security.test_endpoints.Test_LLM_Endpoint.test_openai_async_completions_create[flask-poc] from system_tests_suite   View in Datadog
KeyError: &#39;appsec.events.llm.call.provider&#39;

self = &lt;tests.appsec.api_security.test_endpoints.Test_LLM_Endpoint object at 0x7f18e9285010&gt;

    def test_openai_async_completions_create(self):
&gt;       assert_llm_span(self.request, self.MODEL)

tests/appsec/api_security/test_endpoints.py:72: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

...
View all 6 test failures

DataDog/apm-reliability/dd-trace-py | build linux serverless: [arm64, cp315-cp315, v113741357-d2b8243-manylinux2014_aarch64, 1]   View in Datadog   GitLab

DataDog/apm-reliability/dd-trace-py | build linux: [arm64, cp315-cp315, v113741357-d2b8243-manylinux2014_aarch64]   View in Datadog   GitLab

View all 12 failed jobs.

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

🔄 Datadog auto-retried 1 job - 1 passed on retry View in Datadog

Useful? React with 👍 / 👎

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

@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codeowners resolved as

tests/opentelemetry/test_otlp_trace.py                                  @DataDog/apm-sdk-capabilities-python @DataDog/apm-core-python

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.

This is to fix a breaking change in libdatadog

@pr-commenter

pr-commenter Bot commented Jun 12, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-06-24 16:47:08

Comparing candidate commit 132f024 in PR branch brian.marks/otlp-http-protobuf-export with baseline commit 33e2657 in branch main.

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

scenario:iast_aspects-re_match_noaspect

  • 🟥 execution_time [+28.685µs; +37.324µs] or [+8.597%; +11.186%]

scenario:iastaspects-ljust_aspect

  • 🟥 execution_time [+75.280µs; +87.980µs] or [+12.651%; +14.785%]

scenario:iastaspects-stringio_noaspect

  • 🟥 execution_time [+36.753µs; +43.163µs] or [+9.804%; +11.514%]

scenario:iastaspectsospath-ospathbasename_aspect

  • 🟥 execution_time [+89.487µs; +98.578µs] or [+20.167%; +22.216%]

scenario:span-start

  • 🟥 execution_time [+1.405ms; +1.550ms] or [+9.165%; +10.111%]

scenario:telemetryaddmetric-1-count-metric-1-times

  • 🟥 execution_time [+149.489ns; +178.213ns] or [+7.212%; +8.598%]

gh-worker-dd-mergequeue-cf854d Bot pushed a commit to DataDog/libdatadog that referenced this pull request Jun 23, 2026
# What does this PR do?

Adds OTLP HTTP/protobuf as a trace-export encoding alongside HTTP/JSON, selectable via the OTel-standard `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` (`http/json` default, `http/protobuf`). The generated `prost` OTLP types are the single intermediate representation: the mapper builds them directly from native spans, protobuf is `prost::encode_to_vec`, and JSON is a serde serializer over the same types.

# Motivation

libdatadog's OTLP trace export only spoke HTTP/JSON. SDKs that honor `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` need `http/protobuf` to match the OTel default and to talk to collectors that expect protobuf.

# Additional Notes

- Prost is the single IR: no parallel hand-rolled JSON model, no string round-trip. `encode_otlp_protobuf` is `encode_to_vec`; `encode_otlp_json` is a serde serializer (`json_serializer.rs`) emitting OTLP-spec JSON (hex ids, base64 bytes, int64-as-string, lowerCamelCase, proto3 defaults omitted).
- `OtlpProtocol` is `{HttpJson, HttpProtobuf}`; `grpc` is rejected at the parse boundary (`FromStr`) rather than carried as an unsupported variant. `content_type()`/`encode()` live on the type.
- Integrates with the OTLP metrics exporter already on main: the shared low-level sender takes the content-type per request, so traces use the configured protocol while metrics stay JSON.
- Carries through `otel_trace_semantics_enabled` (OTel attribute compatibility, #2091): when set, the prost mapper omits the DD-specific span attributes and promotes the error message to the OTLP `Status`.
- Span-link W3C trace flags are carried through to OTLP `Link.flags`.
- Benchmarks for the encoder hot paths, plus allocation tuning (pre-sized Vecs, allocation-free id/timestamp/int serialization).

# How to test the change?

- `cargo test -p libdd-trace-utils -p libdd-data-pipeline -p libdd-data-pipeline-ffi`, `cargo test --doc`, clippy, fmt, and `cargo ffi-test` pass. A parity test asserts the JSON and protobuf encodings carry the same span from the one prost IR; a protobuf round-trip test asserts the encoding is lossless.
- End-to-end through dd-trace-py (DataDog/dd-trace-py#18609): emitted protobuf-only OTLP traces through a local Agent to the backend. Wire was `application/x-protobuf` (HTTP 200), spans ingested with correct service/resource and a preserved 128-bit trace id.
- Benchmarks: `cargo bench -p libdd-trace-utils --bench main -- otlp/`.

BREAKING CHANGE: removes the previously public `libdd_trace_utils::otlp_encoder::json_types` module (the hand-rolled OTLP JSON model). OTLP encoding now builds prost-generated types as the single IR. libdatadog consumers pin by version, so they pick this up on the next release.


Co-authored-by: brian.marks <brian.marks@datadoghq.com>
bm1549 and others added 3 commits June 24, 2026 09:47
)

DataDog/libdatadog#2115 merged to main as 4e8e6cc8c. Repoint the native
extension's libdatadog git rev from the pre-merge branch SHA to that
permanent commit, and regenerate Cargo.lock (libdd-* 35.0.0 -> 36.0.0
plus the transitive deps the merged tree pulls in).

Add the AssignmentReason::Default -> Reason::Default arm in ffe.rs: the
merged libdatadog FFE feature added that enum variant, so the
From<AssignmentReason> for Reason match is otherwise non-exhaustive.

Add a reno release note for the http/protobuf OTLP trace export feature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…otocol stub

- Default OTLP trace export to http/protobuf: coerce any non-HTTP protocol
  (e.g. the OTel-default grpc) to it instead of http/json.
- Declare set_otlp_protocol in the _native type stub so mypy resolves the
  writer.py call (unblocks the prechecks typing lint).
- Drop the repeated "grpc unsupported / fallback" comments, keeping one
  explanation at the coercion site.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The merged libdatadog FFE remaps per-flag config parse failures (bad regex,
variant type mismatch) from ConfigurationParseError to the new
FlagConfigurationInvalid variant. dd-trace-py's From<EvaluationError> match
didn't handle it, so it fell through to ErrorCode::General and the
feature_flag.evaluations metric reported error.type=general instead of
parse_error, failing the FEATURE_FLAGGING_AND_EXPERIMENTATION system tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bm1549 bm1549 force-pushed the brian.marks/otlp-http-protobuf-export branch from d485fb5 to a53e451 Compare June 24, 2026 14:42
bm1549 and others added 2 commits June 24, 2026 11:23
test_otlp_traces_sent_via_http now sets OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=
http/json explicitly (it validates the JSON encoding, which is no longer the
default). Add test_otlp_traces_sent_via_http_protobuf_default asserting the
new default emits application/x-protobuf and decodes to the expected span.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The opentelemetry riot venv doesn't ship opentelemetry-proto, so decoding the
body via ExportTraceServiceRequest raised ModuleNotFoundError. Validate the
protobuf default decode-free instead: protobuf content type, the OTLP
resource_spans field-1 tag (0x0a), and the span name bytes in the payload.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant