feat(tracer): Implement OTLP Traces Export#4600
Conversation
… to determine otlp mode
…nfig registry to reflect this
…ut ddspan -> otlpspan conversion
… span_to_otlp_test.go
… service name differs from global service name
…me (#4576) ### What does this PR do? Moves the `strconv.ParseInt` call for `_dd.p.dm` (decision maker) out of the v1 payload flush path and into the write path. A `dm uint32` field is added to the `trace` struct. It is kept in sync with `propagatingTags[keyDecisionMaker]` at all mutation sites (`setPropagatingTagLocked`, `unsetPropagatingTagLocked`, `replacePropagatingTags`). A shared `unsetPropagatingTagLocked` helper is introduced so the delete-and-clear logic is not duplicated between `unsetPropagatingTag` and `setSamplingPriorityLockedWithForce`. `payloadV1.push` now reads the pre-computed value via `trace.decisionMaker()` instead of fetching the string from the propagating tags map and parsing it on every flush. ### Motivation `payloadV1.push` is called once per finished trace chunk. On every call it was acquiring a read lock, doing a map lookup, and then running `strconv.ParseInt` — all to read a value that was set at most once or twice during the lifetime of the trace. Moving the parse to write-time eliminates the per-flush map lookup and parse, replacing them with a single scalar read behind an `RLock`. ### Reviewer's Checklist - [x] Changed code has unit tests for its functionality at or near 100% coverage. - [x] New code is free of linting errors. You can check this by running `make lint` locally. - [x] New code doesn't break existing tests. You can check this by running `make test` locally. - [x] Add an appropriate team label so this PR gets put in the right place for the release notes. - [x] All generated files are up to date. You can check this by running `make generate` locally. Co-authored-by: dario.castane <[email protected]>
…ED` once, use a global `bool` (#4548) Stop checking environment variable `DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED` on span start. Checks it once, use a global `bool`. It'd be better to load it on `newConfig` and make the value available, but this one happens in `newSpanContext`, which modification will cause a lot of changes in multiple files, including tests. Complete #1905 work using the current benchmarking platform. - [x] Changed code has unit tests for its functionality at or near 100% coverage. - [x] There is a benchmark for any new code, or changes to existing code. - [x] New code is free of linting errors. You can check this by running `make lint` locally. - [x] New code doesn't break existing tests. You can check this by running `make test` locally. - [x] Add an appropriate team label so this PR gets put in the right place for the release notes. Unsure? Have a question? Request a review! Co-authored-by: kakkoyun <[email protected]> Co-authored-by: kemal.akkoyun <[email protected]>
Signed-off-by: Moe Zein <[email protected]>
Co-authored-by: Benjamin De Bernardi <[email protected]>
| // resolveTraceTransport returns the trace URL and headers for the transport | ||
| // based on whether OTLP export mode is active. | ||
| // resolveTraceTransport returns the trace URL and headers for the Datadog | ||
| // agent transport. In OTLP export mode the ddTransport is not used for trace |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8d6156e0db
ℹ️ 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".
| return &otlptrace.Span{ | ||
| TraceId: convertTraceID(s.context.traceID.Upper(), s.context.traceID.Lower()), | ||
| SpanId: convertSpanID(s.spanID), | ||
| ParentSpanId: convertParentSpanID(s.parentID), | ||
| Name: s.resource, | ||
| Kind: convertSpanKind(getSpanKind(s)), | ||
| StartTimeUnixNano: uint64(s.start), | ||
| EndTimeUnixNano: uint64(s.start + s.duration), | ||
| Attributes: convertSpanAttributes(s, defaultServiceName), | ||
| Events: convertEvents(s), | ||
| Links: convertSpanLinks(s.spanLinks), | ||
| Status: convertSpanStatus(s), | ||
| TraceState: convertTraceState(s.context), | ||
| } |
There was a problem hiding this comment.
I think we could make this more efficient by wrapping *tracer.Span with otlptrace.Span and having getters returning each expected field in the converted format.
This can be done in the future.
There was a problem hiding this comment.
Every field converted here is a required field in the protobuf output; there are no unused conversions. So as I see it, deferring the conversion would just change when the latency is introduced, not how much latency we introduce. Unless I am missing something?
There was a problem hiding this comment.
I was thinking on the heap allocation that happens here. If we had a wrapper type that stores a pointer to the tracer.Span we could probably make this function inlineable, as we wouldn't need to run all these conversion functions here.
I'm also not sure if these conversion functions are inlining here.
In any case, looking how this is used, I see it's not possible to optimize that much.
kakkoyun
left a comment
There was a problem hiding this comment.
LGTM.
Thanks for enduring all the reviews. It's an critical piece and a bi g PR. I hope we haven't missed anything. We can always iterate.
| scope *otlpcommon.InstrumentationScope | ||
| spans []*otlptrace.Span // +checklocks:mu | ||
| buffSize int // +checklocks:mu | ||
| baseSize int |
There was a problem hiding this comment.
nit: Lack of this annotation should not be green on the CI, I need to check this.
73db441
into
main
What does this PR do?
Implements the OTLP trace export pipeline (See: RFC) for dd-trace-go: when
OTEL_TRACES_EXPORTER=otlpis set, the tracer converts Datadog spans into OTLP protobuf format and sends them directly to an OpenTelemetry Collector instead of the Datadog agent.This adds three new components:
The PR also renames the existing transport interface and config.transport field to ddTransport to make it clear that it handles Datadog-specific traffic (msgpack traces, stats), and ensures ddTransport always points at the Datadog agent even when OTLP mode is active.
Motivation
We are adding native OTLP export support to dd-trace-go so users can send traces directly to any OTLP-compatible collector. A prior PR added the configuration layer that resolves
OTEL_TRACES_EXPORTER,OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, andOTEL_EXPORTER_OTLP_TRACES_HEADERS. This PR builds the runtime pipeline on top of that configuration.IMPORTANT NOTE!
The existing
agentTraceWriterandddTransportwas too tightly coupled to Datadog conventions — msgpack encoding, agent sampling rate feedback, stats collection — to extend it for the otlp path, so a separate writer and transport were introduced instead. TheotlpTraceWriterowns its ownotlpTransportdirectly (rather than reaching throughconfig.ddTransport), keeping OTLP concerns isolated from the Datadog agent path. A follow-up PR may removeddTransportfrom the config struct entirely, instead passing the transport directly to each writer and consumer at construction time. This would make all writer implementations structurally consistent — each owning its transport as a field — and eliminate the implicit coupling through config.An additional PR to disable stats collection in otlp mode is in progress here.
Reviewer's Checklist
make lintlocally.make testlocally.make generatelocally.make fix-moduleslocally.Unsure? Have a question? Request a review!