Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import re
import uuid
import contextlib
import math
import time

from datetime import datetime, timedelta
from numbers import Real

import sentry_sdk

Expand Down Expand Up @@ -407,8 +409,8 @@ def finish(self, hub=None):
_maybe_create_breadcrumbs_from_span(hub, self)
return None

def to_json(self, client):
# type: (Optional[sentry_sdk.Client]) -> Dict[str, Any]
def to_json(self):
# type: () -> Dict[str, Any]
rv = {
"trace_id": self.trace_id,
"span_id": self.span_id,
Expand Down Expand Up @@ -517,7 +519,7 @@ def finish(self, hub=None):
return None

finished_spans = [
span.to_json(client)
span.to_json()
for span in self._span_recorder.spans
if span is not self and span.timestamp is not None
]
Expand All @@ -534,6 +536,47 @@ def finish(self, hub=None):
}
)

def to_json(self):
# type: () -> Dict[str, Any]
rv = super(Transaction, self).to_json()

rv["name"] = self.name
rv["sampled"] = self.sampled
rv["parent_sampled"] = self.parent_sampled

return rv


def _is_valid_sample_rate(rate):
# type: (Any) -> bool
"""
Checks the given sample rate to make sure it is valid type and value (a
boolean or a number between 0 and 1, inclusive).
"""

# both booleans and NaN are instances of Real, so a) checking for Real
# checks for the possibility of a boolean also, and b) we have to check
# separately for NaN
if not isinstance(rate, Real) or math.isnan(rate):
logger.warning(
"[Tracing] Given sample rate is invalid. Sample rate must be a boolean or a number between 0 and 1. Got {rate} of type {type}.".format(
rate=rate, type=type(rate)
)
)
return False

# in case rate is a boolean, it will get cast to 1 if it's True and 0 if it's False
rate = float(rate)
if rate < 0 or rate > 1:
logger.warning(
"[Tracing] Given sample rate is invalid. Sample rate must be between 0 and 1. Got {rate}.".format(
rate=rate
)
)
return False

return True


def _format_sql(cursor, sql):
# type: (Any, str) -> Optional[str]
Expand Down
10 changes: 10 additions & 0 deletions sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,3 +968,13 @@ def run(self):
integer_configured_timeout
)
)


def has_tracing_enabled(options):
# type: (Dict[str, Any]) -> bool
"""
Returns True if either traces_sample_rate or traces_sampler is
non-zero/defined, False otherwise.
"""

return bool(options.get("traces_sample_rate") or options.get("traces_sampler"))
41 changes: 41 additions & 0 deletions tests/tracing/test_sampling.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import pytest

from sentry_sdk import start_span, start_transaction
from sentry_sdk.tracing import _is_valid_sample_rate
from sentry_sdk.utils import logger

try:
from unittest import mock # python 3.3 and above
except ImportError:
import mock # python < 3.3


def test_sampling_decided_only_for_transactions(sentry_init, capture_events):
Expand Down Expand Up @@ -32,3 +41,35 @@ def test_no_double_sampling(sentry_init, capture_events):
pass

assert len(events) == 1


@pytest.mark.parametrize(
"rate",
[0.0, 0.1231, 1.0, True, False],
)
def test_accepts_valid_sample_rate(rate):
with mock.patch.object(logger, "warning", mock.Mock()):
result = _is_valid_sample_rate(rate)
assert logger.warning.called is False
assert result is True


@pytest.mark.parametrize(
"rate",
[
"dogs are great", # wrong type
(0, 1), # wrong type
{"Maisey": "Charllie"}, # wrong type
[True, True], # wrong type
{0.2012}, # wrong type
float("NaN"), # wrong type
None, # wrong type
-1.121, # wrong value
1.231, # wrong value
],
)
def test_warns_on_invalid_sample_rate(rate, StringContaining): # noqa: N803
with mock.patch.object(logger, "warning", mock.Mock()):
result = _is_valid_sample_rate(rate)
logger.warning.assert_any_call(StringContaining("Given sample rate is invalid"))
assert result is False