Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 14 additions & 2 deletions ddtrace/internal/sampling.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import math
from typing import Any
from typing import Optional
from typing import TypedDict
Expand Down Expand Up @@ -46,6 +47,17 @@ class PriorityCategory(object):

KNUTH_SAMPLE_RATE_KEY = "_dd.p.ksr"


def _format_ksr(rate: float) -> str:
"""Format a sampling rate for _dd.p.ksr: up to 6 decimal digits, trailing zeros stripped.

Uses explicit rounding via math.floor(x + 0.5) to avoid Python's banker's rounding
which would round 0.0000005 down to 0 instead of up to 0.000001.
"""
rounded = math.floor(rate * 1e6 + 0.5) / 1e6
return f"{rounded:.6f}".rstrip("0").rstrip(".")


SpanSamplingRules = TypedDict(
"SpanSamplingRules",
{
Expand Down Expand Up @@ -243,10 +255,10 @@ def _set_sampling_tags(span: Span, sampled: bool, sample_rate: float, mechanism:
SamplingMechanism.REMOTE_DYNAMIC_TRACE_SAMPLING_RULE,
):
span._set_attribute(_SAMPLING_RULE_DECISION, sample_rate)
span._set_attribute(KNUTH_SAMPLE_RATE_KEY, f"{sample_rate:.6g}")
span._set_attribute(KNUTH_SAMPLE_RATE_KEY, _format_ksr(sample_rate))
elif mechanism == SamplingMechanism.AGENT_RATE_BY_SERVICE:
span._set_attribute(_SAMPLING_AGENT_DECISION, sample_rate)
span._set_attribute(KNUTH_SAMPLE_RATE_KEY, f"{sample_rate:.6g}")
span._set_attribute(KNUTH_SAMPLE_RATE_KEY, _format_ksr(sample_rate))
# Set the sampling priority
priorities = SAMPLING_MECHANISM_TO_PRIORITIES[mechanism]
priority_index = _KEEP_PRIORITY_INDEX if sampled else _REJECT_PRIORITY_INDEX
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
tracing: Fix ``_dd.p.ksr`` span tag formatting for very small sampling rates.
Previously, rates below 0.001 could be output in scientific notation (e.g.
``1e-06``). Now always uses decimal notation with up to 6 decimal digits.
29 changes: 29 additions & 0 deletions tests/tracer/test_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -889,3 +889,32 @@ def span():
def test_trace_tag(span, sampling_mechanism, expected):
span._set_sampling_decision_maker(sampling_mechanism)
assert span.context._meta["_dd.p.dm"] == expected


@pytest.mark.parametrize(
"sample_rate,expected_ksr",
[
(1.0, "1"),
(0.5, "0.5"),
(0.000001, "0.000001"),
(0.0000001, "0"),
(0.0000005, "0.000001"),
(0.7654321, "0.765432"),
],
ids=[
"rate_1_strips_trailing_zeros",
"simple_rate",
"six_decimal_precision_boundary",
"below_precision_rounds_to_zero",
"rounds_up_to_one_millionth",
"truncation_at_six_decimals",
],
)
def test_ksr_formatting(span, sample_rate, expected_ksr):
"""_dd.p.ksr is formatted with up to 6 decimal digits, trailing zeros stripped, no scientific notation."""
from ddtrace.internal.sampling import KNUTH_SAMPLE_RATE_KEY
from ddtrace.internal.sampling import SamplingMechanism
from ddtrace.internal.sampling import _set_sampling_tags

_set_sampling_tags(span, True, sample_rate, SamplingMechanism.LOCAL_USER_TRACE_SAMPLING_RULE)
assert span._meta.get(KNUTH_SAMPLE_RATE_KEY) == expected_ksr
Loading