Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3c6394e
feat(tracing): add OpenTelemetry tracestate sampling
MilanGarnier Aug 14, 2026
9c814e8
chore(tracing): remove redundant includes
MilanGarnier Aug 17, 2026
cb6e08f
refactor(propagation): simplify tracestate parsing
MilanGarnier Aug 17, 2026
5a53772
fix(propagation): retain matching OpenTelemetry tracestate
MilanGarnier Aug 17, 2026
0e2c402
refactor: use explicit types
MilanGarnier Aug 18, 2026
f489cbf
refactor(propagation): split tracestate parsing
MilanGarnier Aug 18, 2026
a368c3b
refactor(sampling): move probability mechanism helper
MilanGarnier Aug 19, 2026
62fdc87
refactor(sampling): clarify probability sampling result
MilanGarnier Aug 19, 2026
21e3a87
refactor(propagation): rename hex digit predicate
MilanGarnier Aug 19, 2026
2deb5ae
refactor(propagation): consolidate tracestate helpers
MilanGarnier Aug 20, 2026
4b9032b
refactor(propagation): remove unused item append result
MilanGarnier Aug 20, 2026
772c4c1
refactor(propagation): name OpenTelemetry value limits
MilanGarnier Aug 20, 2026
0e64820
fix(propagation): reject oversized OpenTelemetry tracestate
MilanGarnier Aug 20, 2026
ccccc52
test(propagation): cover duplicate OpenTelemetry state
MilanGarnier Aug 20, 2026
3f4e4fc
refactor(tracing): use W3C link context alias
MilanGarnier Aug 20, 2026
6eb3ba3
refactor(propagation): defer inherited tracestate view
MilanGarnier Aug 20, 2026
0cea919
refactor(propagation): name dropped probability sampling condition
MilanGarnier Aug 24, 2026
6e53618
fix(tracing): lock local root tag lookup
MilanGarnier Aug 25, 2026
1097548
fix(propagation): preserve ot tracestate member order
MilanGarnier Aug 25, 2026
43d047f
Merge branch 'main' into milan.garnier/otel-tracestate-ot-th
MilanGarnier Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ cc_library(
"include/datadog/logger.h",
"include/datadog/null_collector.h",
"include/datadog/optional.h",
"include/datadog/otel_tracestate.h",
"include/datadog/propagation_behavior_extract.h",
"include/datadog/propagation_style.h",
"include/datadog/rate.h",
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ target_sources(dd-trace-cpp-objects
include/datadog/logger.h
include/datadog/null_collector.h
include/datadog/optional.h
include/datadog/otel_tracestate.h
include/datadog/propagation_behavior_extract.h
include/datadog/propagation_style.h
include/datadog/rate.h
Expand Down
16 changes: 16 additions & 0 deletions include/datadog/otel_tracestate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <string>

namespace datadog {
namespace tracing {

struct OtelTraceState {
// Raw value of the `ot` tracestate member.
std::string value;
// Number of other-vendor members to emit before `ot`.
std::size_t position;
};

} // namespace tracing
} // namespace datadog
3 changes: 3 additions & 0 deletions include/datadog/sampling_decision.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ struct SamplingDecision {
// The per-second maximum allowed number of "keeps" configured for the limiter
// consulted in this decision, if any.
Optional<double> limiter_max_per_second;
// Whether the sample rate alone, before the rate limiter, would keep this
// trace.
Optional<bool> was_probability_sampled;
// The provenance of this decision.
Origin origin;
};
Expand Down
3 changes: 3 additions & 0 deletions include/datadog/trace_segment.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <vector>

#include "optional.h"
#include "otel_tracestate.h"
#include "propagation_style.h"
#include "runtime_id.h"
#include "sampling_decision.h"
Expand Down Expand Up @@ -77,6 +78,7 @@ class TraceSegment {
std::vector<std::unique_ptr<SpanData>> spans_;
std::size_t num_finished_spans_;
Optional<SamplingDecision> sampling_decision_;
const Optional<OtelTraceState> otel_w3c_tracestate_;
const Optional<std::string> additional_w3c_tracestate_;
const Optional<std::string> additional_datadog_w3c_tracestate_;

Expand All @@ -99,6 +101,7 @@ class TraceSegment {
Optional<std::string> origin, std::size_t tags_header_max_size,
std::vector<std::pair<std::string, std::string>> trace_tags,
Optional<SamplingDecision> sampling_decision,
Optional<OtelTraceState> otel_w3c_tracestate,
Optional<std::string> additional_w3c_tracestate,
Optional<std::string> additional_datadog_w3c_tracestate,
std::unique_ptr<SpanData> local_root,
Expand Down
9 changes: 6 additions & 3 deletions src/datadog/extracted_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// extracted from trace context. It's an implementation detail of this library.

#include <datadog/optional.h>
#include <datadog/otel_tracestate.h>
#include <datadog/propagation_style.h>
#include <datadog/trace_id.h>

Expand All @@ -26,11 +27,13 @@ struct ExtractedData {
// refering to the latest datadog parent ID.
Optional<std::string> datadog_w3c_parent_id;
// If this `ExtractedData` was created on account of `PropagationStyle::W3C`,
// then `additional_w3c_tracestate` contains the parts of the "tracestate"
// header that are not the "dd" (Datadog) entry. If there are no other parts,
// then `additional_w3c_tracestate` is null.
// then `additional_w3c_tracestate` contains the entries of the "tracestate"
// header other than the "dd" (Datadog) and "ot" (OpenTelemetry) entries.
// If there are no such entries, then `additional_w3c_tracestate` is null.
// `additional_w3c_tracestate` is used for the `W3C` injection style.
Optional<std::string> additional_w3c_tracestate;
// The retained OpenTelemetry `ot` tracestate member, if present.
Optional<OtelTraceState> otel_w3c_tracestate;
// If this `ExtractedData` was created on account of `PropagationStyle::W3C`,
// and if the "tracestate" header contained a "dd" (Datadog) entry, then
// `additional_datadog_w3c_tracestate` contains fields from within the "dd"
Expand Down
1 change: 1 addition & 0 deletions src/datadog/extraction_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ ExtractedData merge(

if (w3c != contexts.end() && w3c->second.trace_id == result.trace_id) {
result.additional_w3c_tracestate = w3c->second.additional_w3c_tracestate;
result.otel_w3c_tracestate = w3c->second.otel_w3c_tracestate;
result.additional_datadog_w3c_tracestate =
w3c->second.additional_datadog_w3c_tracestate;
result.headers_examined.insert(result.headers_examined.end(),
Expand Down
17 changes: 17 additions & 0 deletions src/datadog/sampling_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,30 @@
// `TraceSampler` and `SpanSampler`.

#include <datadog/rate.h>
#include <datadog/sampling_mechanism.h>

#include <cstdint>
#include <limits>

namespace datadog {
namespace tracing {

inline bool is_probability_mechanism(int mechanism) {
switch (static_cast<SamplingMechanism>(mechanism)) {
case SamplingMechanism::DEFAULT:
case SamplingMechanism::AGENT_RATE:
case SamplingMechanism::REMOTE_RATE_AUTO:
case SamplingMechanism::RULE:
case SamplingMechanism::REMOTE_RATE_USER_DEFINED:
case SamplingMechanism::REMOTE_RATE_EMERGENCY:
case SamplingMechanism::REMOTE_RULE:
case SamplingMechanism::REMOTE_ADAPTIVE_RULE:
return true;
default:
return false;
}
}

// Return a hash value for the specified `value`. `value` is one of the
// following:
//
Expand Down
9 changes: 6 additions & 3 deletions src/datadog/trace_sampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ SamplingDecision TraceSampler::decide(const SpanData& span) {
decision.mechanism = int(rule.mechanism);
decision.limiter_max_per_second = limiter_max_per_second_;
decision.configured_rate = rule.rate;
const std::uint64_t threshold = max_id_from_rate(rule.rate);
if (knuth_hash(span.trace_id.low) <= threshold) {
const std::uint64_t threshold = max_id_from_rate(*decision.configured_rate);
decision.was_probability_sampled =
knuth_hash(span.trace_id.low) <= threshold;
if (*decision.was_probability_sampled) {
if (rule.bypass_limiter) {
decision.priority = int(SamplingPriority::USER_KEEP);
return decision;
Expand Down Expand Up @@ -91,7 +93,8 @@ SamplingDecision TraceSampler::decide(const SpanData& span) {
}

const std::uint64_t threshold = max_id_from_rate(*decision.configured_rate);
if (knuth_hash(span.trace_id.low) <= threshold) {
decision.was_probability_sampled = knuth_hash(span.trace_id.low) <= threshold;
if (*decision.was_probability_sampled) {
decision.priority = int(SamplingPriority::AUTO_KEEP);
} else {
decision.priority = int(SamplingPriority::AUTO_DROP);
Expand Down
Loading
Loading