|
15 | 15 | capture_internal_exceptions, |
16 | 16 | format_attribute, |
17 | 17 | get_current_thread_meta, |
| 18 | + is_valid_sample_rate, |
18 | 19 | logger, |
19 | 20 | nanosecond_time, |
20 | 21 | should_be_treated_as_error, |
21 | 22 | ) |
22 | 23 |
|
23 | 24 | if TYPE_CHECKING: |
24 | 25 | from typing import Any, Optional, Union |
25 | | - from sentry_sdk._types import Attributes, AttributeValue |
| 26 | + from sentry_sdk._types import Attributes, AttributeValue, SamplingContext |
26 | 27 | from sentry_sdk.scope import Scope |
27 | 28 |
|
28 | 29 |
|
@@ -312,8 +313,7 @@ def trace_id(self) -> str: |
312 | 313 |
|
313 | 314 | @property |
314 | 315 | def dynamic_sampling_context(self) -> str: |
315 | | - # TODO |
316 | | - return self.segment.get_baggage().dynamic_sampling_context() |
| 316 | + return self.segment._get_baggage().dynamic_sampling_context() |
317 | 317 |
|
318 | 318 | def _update_active_thread(self) -> None: |
319 | 319 | thread_id, thread_name = get_current_thread_meta() |
@@ -351,3 +351,81 @@ def _get_baggage(self) -> "Baggage": |
351 | 351 | self._baggage = Baggage.populate_from_segment(self) |
352 | 352 |
|
353 | 353 | return self._baggage |
| 354 | + |
| 355 | + def _set_initial_sampling_decision( |
| 356 | + self, sampling_context: "SamplingContext" |
| 357 | + ) -> None: |
| 358 | + """ |
| 359 | + Sets the segment's sampling decision, according to the following |
| 360 | + precedence rules: |
| 361 | +
|
| 362 | + 1. If `traces_sampler` is defined, its decision will be used. It can |
| 363 | + choose to keep or ignore any parent sampling decision, or use the |
| 364 | + sampling context data to make its own decision or to choose a sample |
| 365 | + rate for the transaction. |
| 366 | +
|
| 367 | + 2. If `traces_sampler` is not defined, but there's a parent sampling |
| 368 | + decision, the parent sampling decision will be used. |
| 369 | +
|
| 370 | + 3. If `traces_sampler` is not defined and there's no parent sampling |
| 371 | + decision, `traces_sample_rate` will be used. |
| 372 | + """ |
| 373 | + client = sentry_sdk.get_client() |
| 374 | + |
| 375 | + # nothing to do if tracing is disabled |
| 376 | + if not has_tracing_enabled(client.options): |
| 377 | + self.sampled = False |
| 378 | + return |
| 379 | + |
| 380 | + if not self.is_segment(): |
| 381 | + return |
| 382 | + |
| 383 | + traces_sampler_defined = callable(client.options.get("traces_sampler")) |
| 384 | + |
| 385 | + # We would have bailed already if neither `traces_sampler` nor |
| 386 | + # `traces_sample_rate` were defined, so one of these should work; prefer |
| 387 | + # the hook if so |
| 388 | + if traces_sampler_defined: |
| 389 | + sample_rate = client.options["traces_sampler"](sampling_context) |
| 390 | + else: |
| 391 | + if sampling_context["parent_sampled"] is not None: |
| 392 | + sample_rate = sampling_context["parent_sampled"] |
| 393 | + else: |
| 394 | + sample_rate = client.options["traces_sample_rate"] |
| 395 | + |
| 396 | + # Since this is coming from the user (or from a function provided by the |
| 397 | + # user), who knows what we might get. (The only valid values are |
| 398 | + # booleans or numbers between 0 and 1.) |
| 399 | + if not is_valid_sample_rate(sample_rate, source="Tracing"): |
| 400 | + logger.warning( |
| 401 | + f"[Tracing] Discarding {self._name} because of invalid sample rate." |
| 402 | + ) |
| 403 | + self.sampled = False |
| 404 | + return |
| 405 | + |
| 406 | + self.sample_rate = float(sample_rate) |
| 407 | + |
| 408 | + if client.monitor: |
| 409 | + self.sample_rate /= 2**client.monitor.downsample_factor |
| 410 | + |
| 411 | + # if the function returned 0 (or false), or if `traces_sample_rate` is |
| 412 | + # 0, it's a sign the transaction should be dropped |
| 413 | + if not self.sample_rate: |
| 414 | + if traces_sampler_defined: |
| 415 | + reason = "traces_sampler returned 0 or False" |
| 416 | + else: |
| 417 | + reason = "traces_sample_rate is set to 0" |
| 418 | + |
| 419 | + logger.debug(f"[Tracing] Discarding {self._name} because {reason}") |
| 420 | + self.sampled = False |
| 421 | + return |
| 422 | + |
| 423 | + # Now we roll the dice. |
| 424 | + self.sampled = self._sample_rand < self.sample_rate |
| 425 | + |
| 426 | + if self.sampled: |
| 427 | + logger.debug(f"[Tracing] Starting {self.name}") |
| 428 | + else: |
| 429 | + logger.debug( |
| 430 | + f"[Tracing] Discarding {self.name} because it's not included in the random sample (sampling rate = {self.sample_rate})" |
| 431 | + ) |
0 commit comments