11import uuid
2- import random
32import warnings
43from datetime import datetime , timedelta , timezone
4+ from random import Random
55
66import sentry_sdk
77from sentry_sdk .consts import INSTRUMENTER , SPANSTATUS , SPANDATA
@@ -761,6 +761,7 @@ class Transaction(Span):
761761 "parent_sampled" ,
762762 # used to create baggage value for head SDKs in dynamic sampling
763763 "sample_rate" ,
764+ "_sample_rand" ,
764765 "_measurements" ,
765766 "_contexts" ,
766767 "_profile" ,
@@ -1152,10 +1153,9 @@ def _set_initial_sampling_decision(self, sampling_context):
11521153 self .sampled = False
11531154 return
11541155
1155- # Now we roll the dice. random.random is inclusive of 0, but not of 1,
1156- # so strict < is safe here. In case sample_rate is a boolean, cast it
1157- # to a float (True becomes 1.0 and False becomes 0.0)
1158- self .sampled = random .random () < self .sample_rate
1156+ # Now we "roll the dice" by using the pre-computed sample_rand value.
1157+ # The sample_rand is in the range [0.0, 1.0).
1158+ self .sampled = self .sample_rand () < self .sample_rate
11591159
11601160 if self .sampled :
11611161 logger .debug (
@@ -1171,6 +1171,45 @@ def _set_initial_sampling_decision(self, sampling_context):
11711171 )
11721172 )
11731173
1174+ def sample_rand (self ):
1175+ # type: () -> float
1176+ """Generate a sample_rand value, or obtain it from the baggage.
1177+
1178+ The sample_rand value is used to determine if a trace is sampled. We use the sample_rand
1179+ value from the incoming baggage header, if available. Otherwise, we generate a new one
1180+ according to the [specs](https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value).
1181+
1182+ The first time this function is called, we generate the sample_rand value. Future calls
1183+ will return the same value, since we cache the sample_rand on the transaction.
1184+ """
1185+ cached_sample_rand = getattr (self , "_sample_rand" , None )
1186+
1187+ if cached_sample_rand is not None :
1188+ return cached_sample_rand
1189+
1190+ incoming_sample_rand = self ._incoming_sample_rand ()
1191+ if incoming_sample_rand is not None :
1192+ return incoming_sample_rand
1193+
1194+ return self ._generate_sample_rand ()
1195+
1196+ def _generate_sample_rand (self ):
1197+ # type: () -> float
1198+ """Generate a sample_rand value for this transaction.
1199+
1200+ Per the [specs](https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value),
1201+ the `sample_rand` value is a pseudo-random number in the range [0.0, 1.0), which we generate
1202+ deterministically based on the transaction's trace ID.
1203+ """
1204+ return Random (self .trace_id ).random ()
1205+
1206+ def _incoming_sample_rand (self ):
1207+ # type: () -> Optional[float]
1208+ """Returns the sample_rand value from the incoming baggage header, if available."""
1209+ if self ._baggage is not None :
1210+ return self ._baggage .sample_rand ()
1211+ return None
1212+
11741213
11751214class NoOpSpan (Span ):
11761215 def __repr__ (self ):
0 commit comments