Skip to content

Commit 8776800

Browse files
committed
chore(tracing): Split tracing utils into separate module (getsentry#952)
No behavior changes - just moving stuff around. # Conflicts: # sentry_sdk/tracing.py
1 parent 832263b commit 8776800

File tree

6 files changed

+211
-185
lines changed

6 files changed

+211
-185
lines changed

sentry_sdk/integrations/django/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from sentry_sdk.hub import Hub, _should_send_default_pii
1010
from sentry_sdk.scope import add_global_event_processor
1111
from sentry_sdk.serializer import add_global_repr_processor
12-
from sentry_sdk.tracing import record_sql_queries
12+
from sentry_sdk.tracing_utils import record_sql_queries
1313
from sentry_sdk.utils import (
1414
HAS_REAL_CONTEXTVARS,
1515
CONTEXTVARS_ERROR_MESSAGE,

sentry_sdk/integrations/sqlalchemy.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from sentry_sdk._types import MYPY
44
from sentry_sdk.hub import Hub
55
from sentry_sdk.integrations import Integration, DidNotEnable
6-
from sentry_sdk.tracing import record_sql_queries
6+
from sentry_sdk.tracing_utils import record_sql_queries
77

88
try:
99
from sqlalchemy.engine import Engine # type: ignore

sentry_sdk/integrations/stdlib.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from sentry_sdk.hub import Hub
77
from sentry_sdk.integrations import Integration
88
from sentry_sdk.scope import add_global_event_processor
9-
from sentry_sdk.tracing import EnvironHeaders
9+
from sentry_sdk.tracing_utils import EnvironHeaders
1010
from sentry_sdk.utils import capture_internal_exceptions, safe_repr
1111

1212
from sentry_sdk._types import MYPY

sentry_sdk/tracing.py

+10-179
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
1-
import re
21
import uuid
3-
import contextlib
4-
import math
52
import random
63
import time
74

85
from datetime import datetime, timedelta
9-
from numbers import Real
106

117
import sentry_sdk
128

13-
from sentry_sdk.utils import (
14-
capture_internal_exceptions,
15-
logger,
16-
to_string,
9+
from sentry_sdk.utils import logger
10+
from sentry_sdk.tracing_utils import (
11+
SENTRY_TRACE_REGEX,
12+
EnvironHeaders,
13+
has_tracing_enabled,
14+
is_valid_sample_rate,
15+
maybe_create_breadcrumbs_from_span,
1716
)
18-
from sentry_sdk._compat import PY2
1917
from sentry_sdk._types import MYPY
2018

21-
if PY2:
22-
from collections import Mapping
23-
else:
24-
from collections.abc import Mapping
2519

2620
if MYPY:
2721
import typing
@@ -35,45 +29,6 @@
3529

3630
from sentry_sdk._types import SamplingContext
3731

38-
_traceparent_header_format_re = re.compile(
39-
"^[ \t]*" # whitespace
40-
"([0-9a-f]{32})?" # trace_id
41-
"-?([0-9a-f]{16})?" # span_id
42-
"-?([01])?" # sampled
43-
"[ \t]*$" # whitespace
44-
)
45-
46-
47-
class EnvironHeaders(Mapping): # type: ignore
48-
def __init__(
49-
self,
50-
environ, # type: typing.Mapping[str, str]
51-
prefix="HTTP_", # type: str
52-
):
53-
# type: (...) -> None
54-
self.environ = environ
55-
self.prefix = prefix
56-
57-
def __getitem__(self, key):
58-
# type: (str) -> Optional[Any]
59-
return self.environ[self.prefix + key.replace("-", "_").upper()]
60-
61-
def __len__(self):
62-
# type: () -> int
63-
return sum(1 for _ in iter(self))
64-
65-
def __iter__(self):
66-
# type: () -> Generator[str, None, None]
67-
for k in self.environ:
68-
if not isinstance(k, str):
69-
continue
70-
71-
k = k.replace("-", "_").upper()
72-
if not k.startswith(self.prefix):
73-
continue
74-
75-
yield k[len(self.prefix) :]
76-
7732

7833
class _SpanRecorder(object):
7934
"""Limits the number of spans recorded in a transaction."""
@@ -325,7 +280,7 @@ def from_traceparent(
325280
if traceparent.startswith("00-") and traceparent.endswith("-00"):
326281
traceparent = traceparent[3:-3]
327282

328-
match = _traceparent_header_format_re.match(str(traceparent))
283+
match = SENTRY_TRACE_REGEX.match(str(traceparent))
329284
if match is None:
330285
return None
331286

@@ -422,7 +377,7 @@ def finish(self, hub=None):
422377
except AttributeError:
423378
self.timestamp = datetime.utcnow()
424379

425-
_maybe_create_breadcrumbs_from_span(hub, self)
380+
maybe_create_breadcrumbs_from_span(hub, self)
426381
return None
427382

428383
def to_json(self):
@@ -617,7 +572,7 @@ def _set_initial_sampling_decision(self, sampling_context):
617572
# Since this is coming from the user (or from a function provided by the
618573
# user), who knows what we might get. (The only valid values are
619574
# booleans or numbers between 0 and 1.)
620-
if not _is_valid_sample_rate(sample_rate):
575+
if not is_valid_sample_rate(sample_rate):
621576
logger.warning(
622577
"[Tracing] Discarding {transaction_description} because of invalid sample rate.".format(
623578
transaction_description=transaction_description,
@@ -660,127 +615,3 @@ def _set_initial_sampling_decision(self, sampling_context):
660615
sample_rate=float(sample_rate),
661616
)
662617
)
663-
664-
665-
def has_tracing_enabled(options):
666-
# type: (Dict[str, Any]) -> bool
667-
"""
668-
Returns True if either traces_sample_rate or traces_sampler is
669-
defined, False otherwise.
670-
"""
671-
672-
return bool(
673-
options.get("traces_sample_rate") is not None
674-
or options.get("traces_sampler") is not None
675-
)
676-
677-
678-
def _is_valid_sample_rate(rate):
679-
# type: (Any) -> bool
680-
"""
681-
Checks the given sample rate to make sure it is valid type and value (a
682-
boolean or a number between 0 and 1, inclusive).
683-
"""
684-
685-
# both booleans and NaN are instances of Real, so a) checking for Real
686-
# checks for the possibility of a boolean also, and b) we have to check
687-
# separately for NaN
688-
if not isinstance(rate, Real) or math.isnan(rate):
689-
logger.warning(
690-
"[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(
691-
rate=rate, type=type(rate)
692-
)
693-
)
694-
return False
695-
696-
# in case rate is a boolean, it will get cast to 1 if it's True and 0 if it's False
697-
rate = float(rate)
698-
if rate < 0 or rate > 1:
699-
logger.warning(
700-
"[Tracing] Given sample rate is invalid. Sample rate must be between 0 and 1. Got {rate}.".format(
701-
rate=rate
702-
)
703-
)
704-
return False
705-
706-
return True
707-
708-
709-
def _format_sql(cursor, sql):
710-
# type: (Any, str) -> Optional[str]
711-
712-
real_sql = None
713-
714-
# If we're using psycopg2, it could be that we're
715-
# looking at a query that uses Composed objects. Use psycopg2's mogrify
716-
# function to format the query. We lose per-parameter trimming but gain
717-
# accuracy in formatting.
718-
try:
719-
if hasattr(cursor, "mogrify"):
720-
real_sql = cursor.mogrify(sql)
721-
if isinstance(real_sql, bytes):
722-
real_sql = real_sql.decode(cursor.connection.encoding)
723-
except Exception:
724-
real_sql = None
725-
726-
return real_sql or to_string(sql)
727-
728-
729-
@contextlib.contextmanager
730-
def record_sql_queries(
731-
hub, # type: sentry_sdk.Hub
732-
cursor, # type: Any
733-
query, # type: Any
734-
params_list, # type: Any
735-
paramstyle, # type: Optional[str]
736-
executemany, # type: bool
737-
):
738-
# type: (...) -> Generator[Span, None, None]
739-
740-
# TODO: Bring back capturing of params by default
741-
if hub.client and hub.client.options["_experiments"].get(
742-
"record_sql_params", False
743-
):
744-
if not params_list or params_list == [None]:
745-
params_list = None
746-
747-
if paramstyle == "pyformat":
748-
paramstyle = "format"
749-
else:
750-
params_list = None
751-
paramstyle = None
752-
753-
query = _format_sql(cursor, query)
754-
755-
data = {}
756-
if params_list is not None:
757-
data["db.params"] = params_list
758-
if paramstyle is not None:
759-
data["db.paramstyle"] = paramstyle
760-
if executemany:
761-
data["db.executemany"] = True
762-
763-
with capture_internal_exceptions():
764-
hub.add_breadcrumb(message=query, category="query", data=data)
765-
766-
with hub.start_span(op="db", description=query) as span:
767-
for k, v in data.items():
768-
span.set_data(k, v)
769-
yield span
770-
771-
772-
def _maybe_create_breadcrumbs_from_span(hub, span):
773-
# type: (sentry_sdk.Hub, Span) -> None
774-
if span.op == "redis":
775-
hub.add_breadcrumb(
776-
message=span.description, type="redis", category="redis", data=span._tags
777-
)
778-
elif span.op == "http":
779-
hub.add_breadcrumb(type="http", category="httplib", data=span._data)
780-
elif span.op == "subprocess":
781-
hub.add_breadcrumb(
782-
type="subprocess",
783-
category="subprocess",
784-
message=span.description,
785-
data=span._data,
786-
)

0 commit comments

Comments
 (0)