|
1 |
| -import re |
2 | 1 | import uuid
|
3 |
| -import contextlib |
4 |
| -import math |
5 | 2 | import random
|
6 | 3 | import time
|
7 | 4 |
|
8 | 5 | from datetime import datetime, timedelta
|
9 |
| -from numbers import Real |
10 | 6 |
|
11 | 7 | import sentry_sdk
|
12 | 8 |
|
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, |
17 | 16 | )
|
18 |
| -from sentry_sdk._compat import PY2 |
19 | 17 | from sentry_sdk._types import MYPY
|
20 | 18 |
|
21 |
| -if PY2: |
22 |
| - from collections import Mapping |
23 |
| -else: |
24 |
| - from collections.abc import Mapping |
25 | 19 |
|
26 | 20 | if MYPY:
|
27 | 21 | import typing
|
|
35 | 29 |
|
36 | 30 | from sentry_sdk._types import SamplingContext
|
37 | 31 |
|
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 |
| - |
77 | 32 |
|
78 | 33 | class _SpanRecorder(object):
|
79 | 34 | """Limits the number of spans recorded in a transaction."""
|
@@ -325,7 +280,7 @@ def from_traceparent(
|
325 | 280 | if traceparent.startswith("00-") and traceparent.endswith("-00"):
|
326 | 281 | traceparent = traceparent[3:-3]
|
327 | 282 |
|
328 |
| - match = _traceparent_header_format_re.match(str(traceparent)) |
| 283 | + match = SENTRY_TRACE_REGEX.match(str(traceparent)) |
329 | 284 | if match is None:
|
330 | 285 | return None
|
331 | 286 |
|
@@ -422,7 +377,7 @@ def finish(self, hub=None):
|
422 | 377 | except AttributeError:
|
423 | 378 | self.timestamp = datetime.utcnow()
|
424 | 379 |
|
425 |
| - _maybe_create_breadcrumbs_from_span(hub, self) |
| 380 | + maybe_create_breadcrumbs_from_span(hub, self) |
426 | 381 | return None
|
427 | 382 |
|
428 | 383 | def to_json(self):
|
@@ -617,7 +572,7 @@ def _set_initial_sampling_decision(self, sampling_context):
|
617 | 572 | # Since this is coming from the user (or from a function provided by the
|
618 | 573 | # user), who knows what we might get. (The only valid values are
|
619 | 574 | # 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): |
621 | 576 | logger.warning(
|
622 | 577 | "[Tracing] Discarding {transaction_description} because of invalid sample rate.".format(
|
623 | 578 | transaction_description=transaction_description,
|
@@ -660,127 +615,3 @@ def _set_initial_sampling_decision(self, sampling_context):
|
660 | 615 | sample_rate=float(sample_rate),
|
661 | 616 | )
|
662 | 617 | )
|
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