From 092e49e58f9ff6634bea6278d02171f844eb4d1c Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 21:59:53 -0400 Subject: [PATCH 1/4] Fix _dd.p.ksr formatting to use 6 significant digits without trailing zeros Replace std::to_string() (which uses sprintf "%f" producing 6 trailing decimal places) with snprintf "%.6g" which produces up to 6 significant digits with no trailing zeros. This matches the behavior of Python's f"{rate:.6g}" and Go's strconv.FormatFloat(rate, 'g', 6, 64). Examples: 1.0 -> "1", 0.5 -> "0.5", 0.0 -> "0" Co-Authored-By: Claude Opus 4.6 --- src/datadog/trace_segment.cpp | 9 ++++++++- test/test_span.cpp | 16 ++++++++-------- test/test_trace_segment.cpp | 14 ++++++++++---- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/datadog/trace_segment.cpp b/src/datadog/trace_segment.cpp index 61450863..f0ed2fbf 100644 --- a/src/datadog/trace_segment.cpp +++ b/src/datadog/trace_segment.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,12 @@ namespace datadog { namespace tracing { namespace { +std::string format_rate(double rate) { + char buf[32]; + std::snprintf(buf, sizeof(buf), "%.6g", rate); + return std::string(buf); +} + struct Cache { static int process_id; @@ -323,7 +330,7 @@ void TraceSegment::make_sampling_decision_if_null() { trace_tags_.emplace_back( tags::internal::ksr, - std::to_string(*sampling_decision_->configured_rate)); + format_rate(*sampling_decision_->configured_rate)); } void TraceSegment::update_decision_maker_trace_tag() { diff --git a/test/test_span.cpp b/test/test_span.cpp index bce0d27d..65a79c30 100644 --- a/test/test_span.cpp +++ b/test/test_span.cpp @@ -784,8 +784,8 @@ TEST_SPAN("injecting W3C tracestate header") { {"x-datadog-parent-id", "1"}, {"x-datadog-origin", "France"}, }, - // The "s:-1" and "t.ksr:0.000000" comes from the 0% sample rate. - "dd=s:-1;p:$parent_id;o:France;t.ksr:0.000000"}, + // The "s:-1" and "t.ksr:0" comes from the 0% sample rate. + "dd=s:-1;p:$parent_id;o:France;t.ksr:0"}, {__LINE__, "trace tags", @@ -794,8 +794,8 @@ TEST_SPAN("injecting W3C tracestate header") { {"x-datadog-parent-id", "1"}, {"x-datadog-tags", "_dd.p.foo=x,_dd.p.bar=y,ignored=wrong_prefix"}, }, - // The "s:-1" and "t.ksr:0.000000" comes from the 0% sample rate. - "dd=s:-1;p:$parent_id;t.foo:x;t.bar:y;t.ksr:0.000000"}, + // The "s:-1" and "t.ksr:0" comes from the 0% sample rate. + "dd=s:-1;p:$parent_id;t.foo:x;t.bar:y;t.ksr:0"}, {__LINE__, "extra fields", @@ -825,7 +825,7 @@ TEST_SPAN("injecting W3C tracestate header") { }, // The "s:-1" comes from the 0% sample rate. "dd=s:-1;p:$parent_id;o:France_ is a country~nation_ so is " - "______.;t.ksr:0.000000", + "______.;t.ksr:0", }, {__LINE__, @@ -836,7 +836,7 @@ TEST_SPAN("injecting W3C tracestate header") { {"x-datadog-tags", "_dd.p.a;d台北x =foo,_dd.p.ok=bar"}, }, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;p:$parent_id;t.a_d______x_:foo;t.ok:bar;t.ksr:0.000000"}, + "dd=s:-1;p:$parent_id;t.a_d______x_:foo;t.ok:bar;t.ksr:0"}, {__LINE__, "replace invalid characters in trace tag value", @@ -847,7 +847,7 @@ TEST_SPAN("injecting W3C tracestate header") { }, // The "s:-1" comes from the 0% sample rate. "dd=s:-1;p:$parent_id;t.wacky:hello fr_d_ how are " - "_________?;t.ksr:0.000000"}, + "_________?;t.ksr:0"}, {__LINE__, "replace equal signs with tildes in trace tag value", @@ -857,7 +857,7 @@ TEST_SPAN("injecting W3C tracestate header") { {"x-datadog-tags", "_dd.p.base64_thingy=d2Fra2EhIHdhaw=="}, }, // The "s:-1" comes from the 0% sample rate. - "dd=s:-1;p:$parent_id;t.base64_thingy:d2Fra2EhIHdhaw~~;t.ksr:0.000000"}, + "dd=s:-1;p:$parent_id;t.base64_thingy:d2Fra2EhIHdhaw~~;t.ksr:0"}, {__LINE__, "oversized origin truncates it and subsequent fields", diff --git a/test/test_trace_segment.cpp b/test/test_trace_segment.cpp index 94d167e6..59c478f0 100644 --- a/test/test_trace_segment.cpp +++ b/test/test_trace_segment.cpp @@ -7,7 +7,9 @@ #include #include +#include #include +#include #include #include "matchers.h" @@ -325,7 +327,7 @@ TEST_CASE("TraceSegment finalization of spans") { REQUIRE(collector->span_count() == 1); const auto& span = collector->first_span(); REQUIRE(span.numeric_tags.at(tags::internal::agent_sample_rate) == 1.0); - REQUIRE(span.tags.at(tags::internal::ksr) == "1.000000"); + REQUIRE(span.tags.at(tags::internal::ksr) == "1"); } SECTION( @@ -348,7 +350,7 @@ TEST_CASE("TraceSegment finalization of spans") { { REQUIRE(collector_response->span_count() == 1); const auto& span = collector_response->first_span(); - CHECK(span.tags.at(tags::internal::ksr) == "1.000000"); + CHECK(span.tags.at(tags::internal::ksr) == "1"); } collector_response->chunks.clear(); @@ -361,7 +363,7 @@ TEST_CASE("TraceSegment finalization of spans") { REQUIRE(collector_response->span_count() == 1); const auto& span = collector_response->first_span(); CHECK(span.numeric_tags.at(tags::internal::agent_sample_rate) == 1.0); - CHECK(span.tags.at(tags::internal::ksr) == "1.000000"); + CHECK(span.tags.at(tags::internal::ksr) == "1"); } } @@ -392,7 +394,11 @@ TEST_CASE("TraceSegment finalization of spans") { const auto& span = collector->first_span(); REQUIRE(span.numeric_tags.at(tags::internal::rule_sample_rate) == sample_rate); - CHECK(span.tags.at(tags::internal::ksr) == std::to_string(sample_rate)); + { + char buf[32]; + std::snprintf(buf, sizeof(buf), "%.6g", sample_rate); + CHECK(span.tags.at(tags::internal::ksr) == std::string(buf)); + } if (sample_rate == 1.0) { REQUIRE(span.numeric_tags.at( tags::internal::rule_limiter_sample_rate) == 1.0); From 28fee0bf4cd58f29d445444ed255d405f2bfb5cd Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 22:12:51 -0400 Subject: [PATCH 2/4] Fix clang-format violation in trace_segment.cpp Reformat the emplace_back call to match clang-format's expected style (arguments on one line with alignment) to fix the verify CI job. Co-Authored-By: Claude Sonnet 4.6 --- src/datadog/trace_segment.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/datadog/trace_segment.cpp b/src/datadog/trace_segment.cpp index f0ed2fbf..a40a4a68 100644 --- a/src/datadog/trace_segment.cpp +++ b/src/datadog/trace_segment.cpp @@ -328,9 +328,8 @@ void TraceSegment::make_sampling_decision_if_null() { update_decision_maker_trace_tag(); - trace_tags_.emplace_back( - tags::internal::ksr, - format_rate(*sampling_decision_->configured_rate)); + trace_tags_.emplace_back(tags::internal::ksr, + format_rate(*sampling_decision_->configured_rate)); } void TraceSegment::update_decision_maker_trace_tag() { From ef4d91bc77eac03f4c502e3e7fd9c26d0f892875 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 22:47:53 -0400 Subject: [PATCH 3/4] Do not set _dd.p.ksr when using the DEFAULT sampling mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ksr trace tag should only be set when the sampling decision comes from an explicit source (agent rate, rule, or remote rule). When the DEFAULT mechanism is used — meaning no agent configuration has been received yet — the rate is a hardcoded 100% and ksr is meaningless. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/datadog/trace_segment.cpp | 10 ++++++++-- test/test_trace_segment.cpp | 10 +++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/datadog/trace_segment.cpp b/src/datadog/trace_segment.cpp index a40a4a68..5ade7ad3 100644 --- a/src/datadog/trace_segment.cpp +++ b/src/datadog/trace_segment.cpp @@ -328,8 +328,14 @@ void TraceSegment::make_sampling_decision_if_null() { update_decision_maker_trace_tag(); - trace_tags_.emplace_back(tags::internal::ksr, - format_rate(*sampling_decision_->configured_rate)); + // Only set ksr when the sampling mechanism is explicit (agent rate, rule, or + // remote rule). The DEFAULT mechanism means we haven't received any + // configuration from the agent yet, so ksr would be meaningless. + if (sampling_decision_->mechanism && + *sampling_decision_->mechanism != int(SamplingMechanism::DEFAULT)) { + trace_tags_.emplace_back(tags::internal::ksr, + format_rate(*sampling_decision_->configured_rate)); + } } void TraceSegment::update_decision_maker_trace_tag() { diff --git a/test/test_trace_segment.cpp b/test/test_trace_segment.cpp index 59c478f0..8da35261 100644 --- a/test/test_trace_segment.cpp +++ b/test/test_trace_segment.cpp @@ -312,7 +312,9 @@ TEST_CASE("TraceSegment finalization of spans") { REQUIRE_THAT(span.tags, ContainsSubset(filtered)); // "_dd.p.dm" will be added, because we made a sampling decision. REQUIRE(span.tags.count("_dd.p.dm") == 1); - REQUIRE(span.tags.count("_dd.p.ksr") == 1); + // "_dd.p.ksr" is NOT set because this uses the DEFAULT mechanism (no + // agent configuration received yet). + REQUIRE(span.tags.count("_dd.p.ksr") == 0); } SECTION("rate tags") { @@ -327,7 +329,8 @@ TEST_CASE("TraceSegment finalization of spans") { REQUIRE(collector->span_count() == 1); const auto& span = collector->first_span(); REQUIRE(span.numeric_tags.at(tags::internal::agent_sample_rate) == 1.0); - REQUIRE(span.tags.at(tags::internal::ksr) == "1"); + // ksr is NOT set for the DEFAULT mechanism. + REQUIRE(span.tags.count(tags::internal::ksr) == 0); } SECTION( @@ -350,7 +353,8 @@ TEST_CASE("TraceSegment finalization of spans") { { REQUIRE(collector_response->span_count() == 1); const auto& span = collector_response->first_span(); - CHECK(span.tags.at(tags::internal::ksr) == "1"); + // ksr is NOT set for the DEFAULT mechanism. + CHECK(span.tags.count(tags::internal::ksr) == 0); } collector_response->chunks.clear(); From 913ad91e6b6a3598448b7a948ceced0bb0601d7d Mon Sep 17 00:00:00 2001 From: bm1549 Date: Wed, 11 Mar 2026 11:02:04 -0400 Subject: [PATCH 4/4] Replace snprintf with std::to_chars for _dd.p.ksr formatting Address PR feedback: remove format_rate function and use std::to_chars with std::chars_format::general inline instead of snprintf. Co-Authored-By: Claude Opus 4.6 --- src/datadog/trace_segment.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/datadog/trace_segment.cpp b/src/datadog/trace_segment.cpp index 5ade7ad3..07ce49cf 100644 --- a/src/datadog/trace_segment.cpp +++ b/src/datadog/trace_segment.cpp @@ -11,9 +11,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -35,12 +35,6 @@ namespace datadog { namespace tracing { namespace { -std::string format_rate(double rate) { - char buf[32]; - std::snprintf(buf, sizeof(buf), "%.6g", rate); - return std::string(buf); -} - struct Cache { static int process_id; @@ -333,8 +327,12 @@ void TraceSegment::make_sampling_decision_if_null() { // configuration from the agent yet, so ksr would be meaningless. if (sampling_decision_->mechanism && *sampling_decision_->mechanism != int(SamplingMechanism::DEFAULT)) { - trace_tags_.emplace_back(tags::internal::ksr, - format_rate(*sampling_decision_->configured_rate)); + std::array buf; + const auto [ptr, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), + *sampling_decision_->configured_rate, + std::chars_format::general, 6); + assert(ec == std::errc()); + trace_tags_.emplace_back(tags::internal::ksr, std::string(buf.data(), ptr)); } }