|
7 | 7 | import uuid |
8 | 8 | from collections.abc import Mapping |
9 | 9 | from datetime import datetime, timedelta, timezone |
10 | | -from decimal import ROUND_DOWN, Context, Decimal |
| 10 | +from decimal import ROUND_DOWN, Context, Decimal, InvalidOperation |
11 | 11 | from functools import wraps |
12 | 12 | from random import Random |
13 | 13 | from urllib.parse import quote, unquote |
@@ -393,6 +393,9 @@ def from_incoming_data(cls, incoming_data): |
393 | 393 | propagation_context = PropagationContext() |
394 | 394 | propagation_context.update(sentrytrace_data) |
395 | 395 |
|
| 396 | + if propagation_context is not None: |
| 397 | + propagation_context._fill_sample_rand() |
| 398 | + |
396 | 399 | return propagation_context |
397 | 400 |
|
398 | 401 | @property |
@@ -434,6 +437,70 @@ def update(self, other_dict): |
434 | 437 | except AttributeError: |
435 | 438 | pass |
436 | 439 |
|
| 440 | + def _fill_sample_rand(self): |
| 441 | + # type: () -> None |
| 442 | + """ |
| 443 | + Ensure that there is a valid sample_rand value in the baggage. |
| 444 | +
|
| 445 | + If there is a valid sample_rand value in the baggage, we keep it. |
| 446 | + Otherwise, we generate a sample_rand value according to the following: |
| 447 | +
|
| 448 | + - If we have a parent_sampled value and a sample_rate in the DSC, we compute |
| 449 | + a sample_rand value randomly in the range: |
| 450 | + - [0, sample_rate) if parent_sampled is True, |
| 451 | + - or, in the range [sample_rate, 1) if parent_sampled is False. |
| 452 | +
|
| 453 | + - If either parent_sampled or sample_rate is missing, we generate a random |
| 454 | + value in the range [0, 1). |
| 455 | +
|
| 456 | + The sample_rand is deterministically generated from the trace_id, if present. |
| 457 | +
|
| 458 | + This function does nothing if there is no dynamic_sampling_context. |
| 459 | + """ |
| 460 | + if self.dynamic_sampling_context is None: |
| 461 | + return |
| 462 | + |
| 463 | + try: |
| 464 | + sample_rand = Decimal(self.baggage.sentry_items.get("sample_rand")) |
| 465 | + except (TypeError, ValueError, InvalidOperation): |
| 466 | + sample_rand = None |
| 467 | + |
| 468 | + if sample_rand is not None and 0 <= sample_rand < 1: |
| 469 | + # sample_rand is present and valid, so don't overwrite it |
| 470 | + return |
| 471 | + |
| 472 | + # Get the sample rate and compute the transformation that will map the random value |
| 473 | + # to the desired range: [0, 1), [0, sample_rate), or [sample_rate, 1). |
| 474 | + try: |
| 475 | + sample_rate = float(self.baggage.sentry_items.get("sample_rate")) |
| 476 | + except (TypeError, ValueError): |
| 477 | + sample_rate = None |
| 478 | + |
| 479 | + lower, upper = _sample_rand_range(self.parent_sampled, sample_rate) |
| 480 | + |
| 481 | + try: |
| 482 | + sample_rand = _generate_sample_rand(self.trace_id, interval=(lower, upper)) |
| 483 | + except ValueError: |
| 484 | + # ValueError is raised if the interval is invalid, i.e. lower >= upper. |
| 485 | + # lower >= upper might happen if the incoming trace's sampled flag |
| 486 | + # and sample_rate are inconsistent, e.g. sample_rate=0.0 but sampled=True. |
| 487 | + # We cannot generate a sensible sample_rand value in this case. |
| 488 | + logger.debug( |
| 489 | + f"Could not backfill sample_rand, since parent_sampled={self.parent_sampled} " |
| 490 | + f"and sample_rate={sample_rate}." |
| 491 | + ) |
| 492 | + return |
| 493 | + |
| 494 | + self.baggage.sentry_items["sample_rand"] = f"{sample_rand:.6f}" # noqa: E231 |
| 495 | + |
| 496 | + def _sample_rand(self): |
| 497 | + # type: () -> Optional[str] |
| 498 | + """Convenience method to get the sample_rand value from the dynamic_sampling_context.""" |
| 499 | + if self.baggage is None: |
| 500 | + return None |
| 501 | + |
| 502 | + return self.baggage.sentry_items.get("sample_rand") |
| 503 | + |
437 | 504 | def __repr__(self): |
438 | 505 | # type: (...) -> str |
439 | 506 | return "<PropagationContext _trace_id={} _span_id={} parent_span_id={} parent_sampled={} baggage={} dynamic_sampling_context={}>".format( |
|
0 commit comments