Skip to content

Commit

Permalink
feat(tracing): Store sample_rand in DSC
Browse files Browse the repository at this point in the history
Closes #3998
  • Loading branch information
szokeasaurusrex committed Feb 3, 2025
1 parent 91bf322 commit f50c9af
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 5 deletions.
49 changes: 44 additions & 5 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import uuid
import random
import warnings
from datetime import datetime, timedelta, timezone
from random import Random

import sentry_sdk
from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS, SPANDATA
Expand Down Expand Up @@ -761,6 +761,7 @@ class Transaction(Span):
"parent_sampled",
# used to create baggage value for head SDKs in dynamic sampling
"sample_rate",
"_sample_rand",
"_measurements",
"_contexts",
"_profile",
Expand Down Expand Up @@ -1152,10 +1153,9 @@ def _set_initial_sampling_decision(self, sampling_context):
self.sampled = False
return

# Now we roll the dice. random.random is inclusive of 0, but not of 1,
# so strict < is safe here. In case sample_rate is a boolean, cast it
# to a float (True becomes 1.0 and False becomes 0.0)
self.sampled = random.random() < self.sample_rate
# Now we "roll the dice" by using the pre-computed sample_rand value.
# The sample_rand is in the range [0.0, 1.0).
self.sampled = self.sample_rand() < self.sample_rate

if self.sampled:
logger.debug(
Expand All @@ -1171,6 +1171,45 @@ def _set_initial_sampling_decision(self, sampling_context):
)
)

def sample_rand(self):
# type: () -> float
"""Generate a sample_rand value, or obtain it from the baggage.
The sample_rand value is used to determine if a trace is sampled. We use the sample_rand
value from the incoming baggage header, if available. Otherwise, we generate a new one
according to the [specs](https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value).
The first time this function is called, we generate the sample_rand value. Future calls
will return the same value, since we cache the sample_rand on the transaction.
"""
cached_sample_rand = getattr(self, "_sample_rand", None)

if cached_sample_rand is not None:
return cached_sample_rand

incoming_sample_rand = self._incoming_sample_rand()
if incoming_sample_rand is not None:
return incoming_sample_rand

return self._generate_sample_rand()

def _generate_sample_rand(self):
# type: () -> float
"""Generate a sample_rand value for this transaction.
Per the [specs](https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value),
the `sample_rand` value is a pseudo-random number in the range [0.0, 1.0), which we generate
deterministically based on the transaction's trace ID.
"""
return Random(self.trace_id).random()

def _incoming_sample_rand(self):
# type: () -> Optional[float]
"""Returns the sample_rand value from the incoming baggage header, if available."""
if self._baggage is not None:
return self._baggage.sample_rand()
return None


class NoOpSpan(Span):
def __repr__(self):
Expand Down
19 changes: 19 additions & 0 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ def populate_from_transaction(cls, transaction):
options = client.options or {}

sentry_items["trace_id"] = transaction.trace_id
sentry_items["sample_rand"] = str(transaction.sample_rand())

if options.get("environment"):
sentry_items["environment"] = options["environment"]
Expand Down Expand Up @@ -624,6 +625,24 @@ def strip_sentry_baggage(header):
)
)

def sample_rand(self):
# type: () -> Optional[float]
"""Gets the sample_rand value from the baggage, if available.
This function validates the `sample_rand` before returning it. A valid `sample_rand` is
a float in the range [0.0, 1.0). If the `sample_rand` is missing or invalid, we return
`None` instead of the invalid/missing value.
"""
try:
sample_rand = float(self.sentry_items["sample_rand"])
except (KeyError, ValueError):
return None

if sample_rand < 0.0 or sample_rand >= 1.0:
return None

return sample_rand


def should_propagate_trace(client, url):
# type: (sentry_sdk.client.BaseClient, str) -> bool
Expand Down

0 comments on commit f50c9af

Please sign in to comment.