feat: Publish OTel process context - #324
Conversation
|
🎯 Code Coverage (details) 🔗 Commit SHA: fe98617 | Docs | Datadog PR Page | Give us feedback! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2db7a3588b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
BenchmarksBenchmark execution time: 2026-08-10 06:57:44 Comparing candidate commit fe98617 in PR branch Found 4 performance improvements and 1 performance regressions! Performance is the same for 2 metrics, 1 unstable metrics.
|
Before this change, Tracer had a user-declared destructor that called
otel_process_ctx_drop_current() directly. That caused two problems:
1. Declaring ~Tracer() suppresses the implicit move constructor and move
assignment, while leaving copy operations in place. Tracer became
accidentally copyable and non-movable -- the exact wrong shape for a
class that owns a process-wide singleton. Callers like nginx-datadog
that return Tracer by value silently bound to the copy ctor, and the
source's destructor then dropped the global context out from under
the live copy.
2. otel_process_ctx_publish / otel_process_ctx_drop_current are
documented as not thread-safe. Concurrent construction or
destruction of Tracers across threads would race on the global
published_state.
This change introduces OtelCtxGuard, a small RAII type that owns the
published OpenTelemetry process context, and a
publish_otel_process_ctx()
factory that returns one. Tracer now holds the guard via
std::unique_ptr<OtelCtxGuard>, which gives us:
- Move-only Tracer. Move ops are implicitly defaulted (the previous
explicit ~Tracer() body is gone) and copy ops are implicitly deleted
through the unique_ptr member. A failed or skipped publish is
represented as a null guard, so the destructor is correctly a no-op
in that case.
- Symmetric publish/drop encapsulation. Both directions through the
C API go through OtelCtxGuard, and tracer.cpp no longer references
otel_process_ctx_publish / otel_process_ctx_drop_current directly.
- Thread safety. A mutex internal to the guard serializes publish and
drop, so concurrent Tracer construction/destruction across threads
is safe.
Multi-instance behavior is "last writer wins": the C API holds at most
one published context per process, and any guard destruction drops
whatever is current. This matches the common case (one Tracer per
process) and is documented at the top of otel_process_ctx_guard.h.
This includes a for-tests-only msgpack decoder dependency.
When multiple tracers are alive (as is expected from our envoy integration), we now correctly handle that -- the process context remains published until the last tracer is destroyed, and we also correctly handle the differing runtime-ids.
Also `get_otel_context_state()` to `get_otel_ctx_state()` to match.
This avoids holding the lock while logging.
|
I had to push force to fix the missing commit signatures. Going to fix that merge conflict next... |
…el-process-ctx-signed # Conflicts: # include/datadog/tracer.h # src/datadog/tracer.cpp # src/datadog/version.cpp
Description
This PR adds support for publishing the OpenTelemetry Process Context (aka OTEP 4719 -- https://github.com/open-telemetry/opentelemetry-specification/blob/main/oteps/profiles/4719-process-ctx.md).
This mechanism is very similar to Datadog's existing "process discovery"/"tracer-info" mechanism, so the actual change in
trace.cppis very minimal (this is by design :p ).This data will be read by OTel-compliant tools, of which the eBPF Profiler (which Datadog will also support as part of our Continuous Profiler product) is the first one.
Motivation
We're adding support for OTEP 4719 to all Datadog SDKs, so dd-trace-cpp is actually one of the last ones to adopt this.
See https://docs.google.com/document/d/1IwjjVJzEChcFPcnVV2N5Kkjg-4_Q4v4Q3ojpxntbdvY/edit?pli=1&tab=t.s7dfru3a9hnl (Datadog-only link) for an up-to-date list of all SDKs.
Additional Notes
Jira ticket: PROF-14789
The
otel_process_ctx.cppandotel_process_ctx.hcome from the reference implementation we're maintaining upstream with OTel in https://github.com/open-telemetry/sig-profiling/tree/main/process-context/c-and-cpp.They are already in use in dd-trace-java as well.
Review-wise, my suggestion for those would be to focus on correctness -- ideally we want to be able to quickly update to the latest version when needed, which is made easier if we have a minimal delta on upstream.
(I'm the one maintaining this upstream so I can propagate fixes/changes as well so we don't have to diverge).
How to test the change?
This change includes test coverage. Also, I've tested this in practice using the
otel_process_ctx_dump.shscript from the https://github.com/open-telemetry/sig-profiling/ repo.Here's how that looks: