Add DD_TRACE_OTEL_SEMANTICS_ENABLED for OTel HTTP semantic conventions#11652
Add DD_TRACE_OTEL_SEMANTICS_ENABLED for OTel HTTP semantic conventions#11652link04 wants to merge 6 commits into
Conversation
Opt-in flag (off by default) that makes HTTP server and client spans emit
OpenTelemetry HTTP semantic-convention attribute names and span names instead
of the Datadog ones. When enabled, only the OTel attribute set is emitted.
- Config flag trace.otel.semantics.enabled wired through TraceInstrumentationConfig,
ConfigDefaults, Config, and metadata/supported-configurations.json.
- HttpServerDecorator / HttpClientDecorator branch on the flag to emit
http.request.method, url.path/url.scheme/url.query (server) or url.full (client),
server.address/port, client.address, network.peer.address, user_agent.original,
http.response.status_code, plus _OTHER method normalization + method_original,
error.type for error responses, and url.full credential redaction.
- Span name follows the spec: "{method}" (route appended by route-based naming for
servers); the URL path/404 is never used as the target.
- HTTP status code stays in the dedicated DDSpanContext field (so trace stats keep
their status dimension); the serializer renames the emitted key to
http.response.status_code via Metadata.httpStatusKey.
- Shared OtelHttpSemantics helper for method normalization, url redaction,
error.type, and default server.port.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0734789dc5
ℹ️ 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".
- Remove unused Tags.NETWORK_PROTOCOL_VERSION constant (deferred follow-up). - url.full redaction now redacts only the credential components actually present (user@ -> REDACTED@, user:pass@ -> REDACTED:REDACTED@) instead of always implying a password; covered by a parameterized test. - Document that url.query stays gated on the query-string toggle under OTel semantics (privacy/PII control) rather than emitting unconditionally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🟢 Java Benchmark SLOs — All performance SLOs passed
PR vs. master results
Commit: Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion. |
- Client spans no longer emit url.scheme/url.path/url.query: clients use url.full only (url.scheme is opt-in and already contained in url.full). - Honor DD_TRACE_HTTP_CLIENT_TAG_QUERY_STRING=false for url.full (strip query and fragment), matching the Datadog http.url behavior and the server-side gating. - Under OTel semantics, mark client 4xx AND 5xx as errors so the error flag and error.type stay consistent (the Datadog default only marks 4xx). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per the OTel HTTP spec, when http.request.method resolves to _OTHER the span name's method component MUST be the literal "HTTP", not the raw verb. Add OtelHttpSemantics.spanNameMethod() and use it when naming server and client spans, while the http.request.method=_OTHER tag and http.request.method_original keep the raw value. Verified against system-tests OTEL_SEMANTICS (DataDog/system-tests#7139): the previously-gated Test_HttpServerOtelSemantics::test_other_method_span_name now passes (19/19). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the repo policy (no new Spock tests): the new OTel semantic-convention logic now lives in the OtelHttpSemantics helper, covered by a JUnit5 OtelHttpSemanticsTest (method _OTHER normalization, "HTTP" span-name, url.full credential redaction, query/fragment stripping, default server.port, error.type). The Spock decorator test files are restored to their upstream state. The decorator flag-gating integration is covered end-to-end by system-tests (DataDog/system-tests#7139) and the serialized status-key rename by DDSpanContextTest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Hi! 👋 Thanks for your pull request! 🎉 To help us review it, please make sure to:
If you need help, please check our contributing guidelines. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc601e0e28
ℹ️ 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".
| // honor the query-string opt-out (privacy/PII) just like the Datadog http.url path | ||
| urlFull = OtelHttpSemantics.withoutQueryAndFragment(urlFull); | ||
| } | ||
| span.setTag(Tags.URL_FULL, urlFull); |
There was a problem hiding this comment.
Suppress legacy peer tags in OTel client mode
With DD_TRACE_OTEL_SEMANTICS_ENABLED=true, every client request with a host still runs the unconditional onURI(span, url) immediately above this branch, and UriBasedClientDecorator.onURI sets legacy peer.hostname/peer.port before this code adds server.address/server.port. That means OTel client spans are not actually replacing the Datadog peer tags, so consumers see both naming schemes for the same endpoint; make the URI peer tagging OTel-aware or skip it in the OTel path.
Useful? React with 👍 / 👎.
| if (url.port() > 0) { | ||
| span.setTag(Tags.SERVER_PORT, url.port()); |
There was a problem hiding this comment.
Pair forwarded host with forwarded port
When a proxied request supplies X-Forwarded-Host (and possibly X-Forwarded-Port) and the local request URL has a different port, this OTel branch takes server.address from the forwarded host but server.port only from url.port(). The extracted forwarded port is already available on this context, so proxied requests can be reported as server.address=public.example with the internal listener port (or no port) instead of the original client-facing port.
Useful? React with 👍 / 👎.
| writeSpanTag(buf, THREAD_NAME, metadata.getThreadName()); | ||
| if (metadata.getHttpStatusCode() != null) { | ||
| writeSpanTag(buf, HTTP_STATUS, metadata.getHttpStatusCode()); | ||
| writeSpanTag(buf, metadata.getHttpStatusKey(), metadata.getHttpStatusCode()); |
There was a problem hiding this comment.
Encode OTel HTTP status as an integer
For OTLP export with the OTel semantics flag enabled, metadata.getHttpStatusKey() becomes http.response.status_code but this call still uses the UTF8BytesString overload, so the OTLP attribute is encoded as a string value. The stable OTel HTTP attribute is an integer, and numeric filters/aggregations will miss spans that set the status through setHttpStatusCode; write the status via the long/int attribute path when using the OTel key.
Useful? React with 👍 / 👎.
| spanLinks); | ||
| } | ||
|
|
||
| public Metadata( |
There was a problem hiding this comment.
I'm sure the maintainers will have a better idea, but we might just want to add the UTF8BytesString httpStatusKey argument to the main constructor and update all its uses so we don't need to maintain two constructors
PerfectSlayer
left a comment
There was a problem hiding this comment.
📝 notes: I had a quick look and the changes do not look great design wise and performance wise. Are you sure this is ready for review?
Summary
Adds an opt-in flag
DD_TRACE_OTEL_SEMANTICS_ENABLED(trace.otel.semantics.enabled, defaultfalse) that makes HTTP server and client spans emit OpenTelemetry HTTP semantic-convention attribute names and span names instead of the Datadog ones. When enabled, only the OTel attribute set is emitted (the Datadog names are replaced, not added alongside).Scope: HTTP only. Based on the OTel HTTP spans spec.
Behavior
GET /routeGET {route}(route appended when known;GETotherwise)http.methodhttp.request.method(+_OTHER/http.request.method_originalfor unknown verbs)http.url,http.query.stringurl.path,url.scheme,url.queryhttp.urlurl.full(credentials redacted),url.schemehttp.hostname/peer.hostnameserver.address/server.porthttp.status_codehttp.response.status_codehttp.client_ip/network.client.ipclient.address/network.peer.addresshttp.useragentuser_agent.originalerror.type= status for error responsesImplementation notes
HttpServerDecorator,HttpClientDecorator), so it covers all HTTP integrations from one place.DDSpanContextfield (so APM trace stats keep their status-code dimension); only the serialized key is renamed, centralized viaMetadata.httpStatusKeyand applied by the trace mappers (v0.4/v0.5/v1), OTLP, and the file dispatcher.OtelHttpSemanticshelper for method normalization,url.fullcredential redaction,error.type, and defaultserver.port.Testing
Unit / serialization (all green on latest master):
HttpServerDecoratorTest(85),HttpClientDecoratorTest(68) — on/off attribute, span-name, method-_OTHER,error.type, redaction coverage.DDSpanContextTest(58) — status-key selection follows the flag.TraceMapperV0_4/V0_5/V1PayloadTest— serializer regression.End-to-end (real agent + bytecode instrumentation + msgpack → dd-apm-test-agent), one HTTP client call, flag off then on:
Verified mutually exclusive: with the flag on, all
http.method/http.url/http.status_codeare absent; with it off, all OTel names are absent.Known follow-ups (out of scope here)
network.protocol.version— HTTP version isn't available at the shared decorator (per-integration plumbing).OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODSoverride knob for the known-methods list.CLIENT_ERROR_STATUSESis 4xx-only;error.typeis set for 5xx but the error flag isn't).🤖 Generated with Claude Code