Skip to content

Commit 3389284

Browse files
committed
chore(tracing): Split tracing utils into separate module (getsentry#952)
No behavior changes - just moving stuff around.
1 parent dbd7ce8 commit 3389284

6 files changed

Lines changed: 196 additions & 172 deletions

File tree

sentry_sdk/integrations/django/__init__.py

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 9 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
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+
is_valid_sample_rate,
14+
maybe_create_breadcrumbs_from_span,
1715
)
18-
from sentry_sdk._compat import PY2
1916
from sentry_sdk._types import MYPY
2017

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

2619
if MYPY:
2720
import typing
@@ -35,45 +28,6 @@
3528

3629
from sentry_sdk._types import SamplingContext
3730

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-
7731

7832
class _SpanRecorder(object):
7933
"""Limits the number of spans recorded in a transaction."""
@@ -325,7 +279,7 @@ def from_traceparent(
325279
if traceparent.startswith("00-") and traceparent.endswith("-00"):
326280
traceparent = traceparent[3:-3]
327281

328-
match = _traceparent_header_format_re.match(str(traceparent))
282+
match = SENTRY_TRACE_REGEX.match(str(traceparent))
329283
if match is None:
330284
return None
331285

@@ -422,7 +376,7 @@ def finish(self, hub=None):
422376
except AttributeError:
423377
self.timestamp = datetime.utcnow()
424378

425-
_maybe_create_breadcrumbs_from_span(hub, self)
379+
maybe_create_breadcrumbs_from_span(hub, self)
426380
return None
427381

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

0 commit comments

Comments
 (0)