Skip to content

Commit f77a9ec

Browse files
feat(tracing): Propagate sample_rand to transaction's baggage
`continue_trace` now propagates incoming `sample_rand` values to the transaction's baggage. Also, in the case where `sample_rand` is missing from the incoming trace and needs to be backfilled, this change introduces a mechanism for the backfilled value from the scope's propagation context to be propagated to the transaction's baggage. The transaction still does not use the `sample_rand` for making sampling decisions; this PR only enables propagation. A future PR will add support for reading the incoming/backfilled `sample_rand` and for using this value to make sampling decisions. Ref #3998
1 parent 2b3f4f7 commit f77a9ec

File tree

4 files changed

+82
-3
lines changed

4 files changed

+82
-3
lines changed

sentry_sdk/scope.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
logger,
4444
)
4545

46+
import typing
4647
from typing import TYPE_CHECKING
4748

4849
if TYPE_CHECKING:
@@ -1146,8 +1147,20 @@ def continue_trace(
11461147
"""
11471148
self.generate_propagation_context(environ_or_headers)
11481149

1150+
# When we generate the propagation context, the sample_rand value is set
1151+
# if missing or invalid (we use the original value if it's valid).
1152+
# We want the transaction to use the same sample_rand value. Due to duplicated
1153+
# propagation logic in the transaction, we pass it in to avoid recomputing it
1154+
# in the transaction.
1155+
# TYPE SAFETY: self.generate_propagation_context() ensures that self._propagation_context
1156+
# is not None.
1157+
sample_rand = typing.cast(
1158+
PropagationContext, self._propagation_context
1159+
)._sample_rand()
1160+
11491161
transaction = Transaction.continue_from_headers(
11501162
normalize_incoming_data(environ_or_headers),
1163+
_sample_rand=sample_rand,
11511164
op=op,
11521165
origin=origin,
11531166
name=name,

sentry_sdk/tracing.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@ def continue_from_environ(
477477
def continue_from_headers(
478478
cls,
479479
headers, # type: Mapping[str, str]
480+
*,
481+
_sample_rand=None, # type: Optional[str]
480482
**kwargs, # type: Any
481483
):
482484
# type: (...) -> Transaction
@@ -485,6 +487,8 @@ def continue_from_headers(
485487
the ``sentry-trace`` and ``baggage`` headers).
486488
487489
:param headers: The dictionary with the HTTP headers to pull information from.
490+
:param _sample_rand: If provided, we override the sample_rand value from the
491+
incoming headers with this value. (internal use only)
488492
"""
489493
# TODO move this to the Transaction class
490494
if cls is Span:
@@ -495,7 +499,9 @@ def continue_from_headers(
495499

496500
# TODO-neel move away from this kwargs stuff, it's confusing and opaque
497501
# make more explicit
498-
baggage = Baggage.from_incoming_header(headers.get(BAGGAGE_HEADER_NAME))
502+
baggage = Baggage.from_incoming_header(
503+
headers.get(BAGGAGE_HEADER_NAME), _sample_rand=_sample_rand
504+
)
499505
kwargs.update({BAGGAGE_HEADER_NAME: baggage})
500506

501507
sentrytrace_kwargs = extract_sentrytrace_data(

sentry_sdk/tracing_utils.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,14 @@ def _fill_sample_rand(self):
531531
f"{sample_rand:.6f}" # noqa: E231
532532
)
533533

534+
def _sample_rand(self):
535+
# type: () -> Optional[str]
536+
"""Convenience method to get the sample_rand value from the dynamic_sampling_context."""
537+
if self.dynamic_sampling_context is None:
538+
return None
539+
540+
return self.dynamic_sampling_context.get("sample_rand")
541+
534542

535543
class Baggage:
536544
"""
@@ -553,8 +561,13 @@ def __init__(
553561
self.mutable = mutable
554562

555563
@classmethod
556-
def from_incoming_header(cls, header):
557-
# type: (Optional[str]) -> Baggage
564+
def from_incoming_header(
565+
cls,
566+
header, # type: Optional[str]
567+
*,
568+
_sample_rand=None, # type: Optional[str]
569+
):
570+
# type: (...) -> Baggage
558571
"""
559572
freeze if incoming header already has sentry baggage
560573
"""
@@ -577,6 +590,10 @@ def from_incoming_header(cls, header):
577590
else:
578591
third_party_items += ("," if third_party_items else "") + item
579592

593+
if _sample_rand is not None:
594+
sentry_items["sample_rand"] = str(_sample_rand)
595+
mutable = False
596+
580597
return Baggage(sentry_items, third_party_items, mutable)
581598

582599
@classmethod
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
These tests exist to verify that Scope.continue_trace() correctly propagates the
3+
sample_rand value onto the transaction's baggage.
4+
5+
We check both the case where there is an incoming sample_rand, as well as the case
6+
where we need to compute it because it is missing.
7+
"""
8+
9+
from unittest import mock
10+
from unittest.mock import Mock
11+
12+
import sentry_sdk
13+
14+
15+
def test_continue_trace_with_sample_rand():
16+
"""
17+
Test that an incoming sample_rand is propagated onto the transaction's baggage.
18+
"""
19+
headers = {
20+
"sentry-trace": "00000000000000000000000000000000-0000000000000000-0",
21+
"baggage": "sentry-sample_rand=0.1,sentry-sample_rate=0.5",
22+
}
23+
24+
transaction = sentry_sdk.continue_trace(headers)
25+
assert transaction.get_baggage().sentry_items["sample_rand"] == "0.1"
26+
27+
28+
def test_continue_trace_missing_sample_rand():
29+
"""
30+
Test that a missing sample_rand is filled in onto the transaction's baggage.
31+
"""
32+
33+
headers = {
34+
"sentry-trace": "00000000000000000000000000000000-0000000000000000",
35+
"baggage": "sentry-placeholder=asdf",
36+
}
37+
38+
mock_uniform = Mock(return_value=0.5)
39+
40+
with mock.patch("sentry_sdk.tracing_utils.Random.uniform", mock_uniform):
41+
transaction = sentry_sdk.continue_trace(headers)
42+
43+
assert transaction.get_baggage().sentry_items["sample_rand"] == "0.500000"

0 commit comments

Comments
 (0)