Skip to content

feat(otel): otel sampling support in distributed tracing - #4177

Open
vpellan wants to merge 1 commit into
masterfrom
vpellan/otel-threshold-random-value
Open

feat(otel): otel sampling support in distributed tracing#4177
vpellan wants to merge 1 commit into
masterfrom
vpellan/otel-threshold-random-value

Conversation

@vpellan

@vpellan vpellan commented Sep 9, 2026

Copy link
Copy Markdown

Description

Add support for OTel sampling in distributed tracing, by reading and emitting ot.th and ot.rv tags. These tags will be forwarded to services using OTel SDKs, that should forward them to the backend.

Distributed tracing will work between DD-instrumented services and OTel-instrumented services, downstream OTel services will be able to forward the th field it to the collector and backend, which will calculate metrics correctly

Locally passes DataDog/system-tests#7518

Reviewer checklist

  • Test coverage seems ok.
  • Appropriate labels assigned.

@vpellan
vpellan requested review from a team as code owners September 9, 2026 14:51
@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Sep 9, 2026

Copy link
Copy Markdown

Pipelines  Tests

Unblock PR with BitsAI

⚠️ Warnings

Your PR has failed checks. Please review the issues below and take necessary action before merging.

🚦 102 Pipeline jobs failed

DataDog/apm-reliability/dd-trace-php | ASAN test_c: [7.4, arm64] — ❄️ 2 known flaky tests

View more details · View in GitLab

↳ ❄️ Known flaky: tmp/build_extension/tests/ext/integrations/curl/distributed_tracing_curl.phpt (Distributed tracing headers propagate with curl_exec()) from PHP.tmp.build_extension.tests.ext.integrations.curl

↳ ❄️ Known flaky: tmp/build_extension/tests/ext/integrations/curl/distributed_tracing_curl_invalid_tags.phpt (Distributed tracing headers contain properly escaped values) from PHP.tmp.build_extension.tests.ext.integrations.curl

DataDog/apm-reliability/dd-trace-php | ASAN test_c: [8.5, arm64] — ❄️ 2 known flaky tests

View more details · View in GitLab

↳ ❄️ Known flaky: tmp/build_extension/tests/ext/integrations/curl/distributed_tracing_curl.phpt (Distributed tracing headers propagate with curl_exec()) from php.tmp.build_extension.tests.ext.integrations.curl

↳ ❄️ Known flaky: tmp/build_extension/tests/ext/integrations/curl/distributed_tracing_curl_invalid_tags.phpt (Distributed tracing headers contain properly escaped values) from php.tmp.build_extension.tests.ext.integrations.curl

DataDog/apm-reliability/dd-trace-php | appsec integration tests: [test8.3-release] — ❄️ 1 known flaky test

View more details · View in GitLab

↳ ❄️ Known flaky: appsec is disabled and sidecar is not launched() from com.datadog.appsec.php.integration.SidecarFeaturesDisabledTests

View all 102 failed jobs.

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: f7329a3 | Docs | View more details | Give us feedback!

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T15:05:34.839614Z f7329a3 PR opened
🔒 Security Review Completed 2026-09-09T15:01:51.128033Z f7329a3 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: f7329a3d7e

ℹ️ 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 tracer/otel_sampling.c
Comment on lines +182 to +186
} else if (decision == DDTRACE_OTEL_SAMPLING_DECISION_PROBABILITY &&
!fields.random_value && !fields.threshold && sample_rate > 0) {
ddtrace_otel_generate_fields(&fields, generated_random_value,
generated_threshold, trace_id,
sampling_priority, sample_rate);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Rebuild ot fields after a local probability decision

When an inbound trace was dropped and a local target_span:any sampling rule makes a new probability decision, any valid inbound rv or th prevents regeneration here. The outbound traceparent can therefore say sampled while the preserved random value remains below the preserved threshold, or a partial member can omit the local threshold entirely, causing downstream OpenTelemetry services to calculate the wrong effective sampling probability. Preserve these fields only for INHERITED; a local probability decision needs fields reconciled with its own rate and final priority.

Useful? React with 👍 / 👎.

Comment thread tracer/otel_sampling.c
Comment on lines +334 to +336
if (members.otel_member) {
ddtrace_otel_append_member(&limited, members.otel_member,
members.otel_member_len, &member_count);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reserve space for both owned tracestate members

With a large dd member—possible because propagated Datadog tags default to a 512-byte allowance—the first append can consume nearly the entire tracestate limit and the ot append then returns false. Its failure is ignored, and the later loop deliberately skips that ot member, so the emitted header silently loses the new sampling threshold exactly on traces carrying substantial propagated tags. The limiting pass should reserve capacity for both owned members or deliberately trim/evict lower-priority data.

Useful? React with 👍 / 👎.

Comment on lines +468 to +471
// W3C Trace Context permits at most 32 list-members. Owned members
// are removed or normalized after this pass, then protected when
// the outbound header is rebuilt.
if (last_comma && ++commas == 32) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reapply tracestate limits when constructing span links

Allowing all 32 extracted members is safe only on the outbound-header path, which runs the limiter again after rebuilding the dd member. DDTrace\SpanLink::fromHeaders() and the restart behavior instead pass this result to ddtrace_build_span_link_from_result(), where ddtrace_format_tracestate() can prepend a dd member for an extracted Datadog origin or tags without another limiting pass. An otherwise valid 32-member or 512-byte tracecontext input can therefore produce an over-limit traceState on the span link; retain the previous reservation or normalize the rebuilt link tracestate too.

Useful? React with 👍 / 👎.

Comment on lines +392 to +395
span->otel_sampling_rate = sample_rate;
span->otel_sampling_decision = mechanism == DD_MECHANISM_MANUAL || mechanism == DD_MECHANISM_ASM || limited || sample_rate <= 0
? DDTRACE_OTEL_SAMPLING_DECISION_NON_PROBABILITY
: DDTRACE_OTEL_SAMPLING_DECISION_PROBABILITY;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat limiter-approved traces as non-probability decisions

When DD_TRACE_SAMPLE_RATE enables the trace rate limiter and the limiter approves the current candidate, limited is false, so this records a pure probability decision and emits a threshold based only on sample_rate. At throughput above DD_TRACE_RATE_LIMIT, however, admission also depends on the token-bucket limiter: for example, approved traces at sample rate 1 emit th:0 even though many equivalent traces are rejected by the limiter. Downstream OpenTelemetry metrics then treat the surviving spans as unsampled and undercount traffic; any decision for which the active limiter was consulted must omit the probability threshold (or encode the combined effective probability), not only decisions the limiter rejected.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant