Skip to content

Commit 0d2097b

Browse files
committed
dsc, sampling
1 parent 97cf291 commit 0d2097b

File tree

2 files changed

+84
-8
lines changed

2 files changed

+84
-8
lines changed

sentry_sdk/_span_batcher.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
if TYPE_CHECKING:
1212
from typing import Any, Callable, Optional
13-
from sentry_sdk.trace import SpanStatus, StreamedSpan
13+
from sentry_sdk._tracing import SpanStatus, StreamedSpan
1414

1515

1616
class SpanBatcher(Batcher["Span"]):
@@ -87,8 +87,7 @@ def _to_transport_format(item: "StreamedSpan") -> "Any":
8787

8888
return res
8989

90-
def _flush(self):
91-
# type: (...) -> Optional[Envelope]
90+
def _flush(self) -> Optional[Envelope]:
9291
from sentry_sdk.utils import format_timestamp
9392

9493
with self._lock:
@@ -97,8 +96,7 @@ def _flush(self):
9796

9897
for trace_id, spans in self._span_buffer.items():
9998
if spans:
100-
trace_context = spans[0].get_trace_context()
101-
dsc = trace_context.get("dynamic_sampling_context")
99+
dsc = spans[0].dynamic_sampling_context()
102100
# XXX[span-first]: empty dsc?
103101

104102
envelope = Envelope(

sentry_sdk/_tracing.py

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
capture_internal_exceptions,
1616
format_attribute,
1717
get_current_thread_meta,
18+
is_valid_sample_rate,
1819
logger,
1920
nanosecond_time,
2021
should_be_treated_as_error,
2122
)
2223

2324
if TYPE_CHECKING:
2425
from typing import Any, Optional, Union
25-
from sentry_sdk._types import Attributes, AttributeValue
26+
from sentry_sdk._types import Attributes, AttributeValue, SamplingContext
2627
from sentry_sdk.scope import Scope
2728

2829

@@ -312,8 +313,7 @@ def trace_id(self) -> str:
312313

313314
@property
314315
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()
317317

318318
def _update_active_thread(self) -> None:
319319
thread_id, thread_name = get_current_thread_meta()
@@ -351,3 +351,81 @@ def _get_baggage(self) -> "Baggage":
351351
self._baggage = Baggage.populate_from_segment(self)
352352

353353
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

Comments
 (0)