2025-12-01
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import uuid
|
||||
import random
|
||||
import warnings
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from enum import Enum
|
||||
|
||||
import sentry_sdk
|
||||
from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS, SPANDATA
|
||||
from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS, SPANDATA, SPANTEMPLATE
|
||||
from sentry_sdk.profiler.continuous_profiler import get_profiler_id
|
||||
from sentry_sdk.utils import (
|
||||
capture_internal_exceptions,
|
||||
get_current_thread_meta,
|
||||
is_valid_sample_rate,
|
||||
logger,
|
||||
@@ -16,6 +17,7 @@ from sentry_sdk.utils import (
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable, Mapping, MutableMapping
|
||||
from typing import Any
|
||||
@@ -28,6 +30,7 @@ if TYPE_CHECKING:
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
from typing import TypeVar
|
||||
from typing import Set
|
||||
|
||||
from typing_extensions import TypedDict, Unpack
|
||||
|
||||
@@ -126,30 +129,37 @@ if TYPE_CHECKING:
|
||||
BAGGAGE_HEADER_NAME = "baggage"
|
||||
SENTRY_TRACE_HEADER_NAME = "sentry-trace"
|
||||
|
||||
|
||||
# Transaction source
|
||||
# see https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations
|
||||
TRANSACTION_SOURCE_CUSTOM = "custom"
|
||||
TRANSACTION_SOURCE_URL = "url"
|
||||
TRANSACTION_SOURCE_ROUTE = "route"
|
||||
TRANSACTION_SOURCE_VIEW = "view"
|
||||
TRANSACTION_SOURCE_COMPONENT = "component"
|
||||
TRANSACTION_SOURCE_TASK = "task"
|
||||
class TransactionSource(str, Enum):
|
||||
COMPONENT = "component"
|
||||
CUSTOM = "custom"
|
||||
ROUTE = "route"
|
||||
TASK = "task"
|
||||
URL = "url"
|
||||
VIEW = "view"
|
||||
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
return self.value
|
||||
|
||||
|
||||
# These are typically high cardinality and the server hates them
|
||||
LOW_QUALITY_TRANSACTION_SOURCES = [
|
||||
TRANSACTION_SOURCE_URL,
|
||||
TransactionSource.URL,
|
||||
]
|
||||
|
||||
SOURCE_FOR_STYLE = {
|
||||
"endpoint": TRANSACTION_SOURCE_COMPONENT,
|
||||
"function_name": TRANSACTION_SOURCE_COMPONENT,
|
||||
"handler_name": TRANSACTION_SOURCE_COMPONENT,
|
||||
"method_and_path_pattern": TRANSACTION_SOURCE_ROUTE,
|
||||
"path": TRANSACTION_SOURCE_URL,
|
||||
"route_name": TRANSACTION_SOURCE_COMPONENT,
|
||||
"route_pattern": TRANSACTION_SOURCE_ROUTE,
|
||||
"uri_template": TRANSACTION_SOURCE_ROUTE,
|
||||
"url": TRANSACTION_SOURCE_ROUTE,
|
||||
"endpoint": TransactionSource.COMPONENT,
|
||||
"function_name": TransactionSource.COMPONENT,
|
||||
"handler_name": TransactionSource.COMPONENT,
|
||||
"method_and_path_pattern": TransactionSource.ROUTE,
|
||||
"path": TransactionSource.URL,
|
||||
"route_name": TransactionSource.COMPONENT,
|
||||
"route_pattern": TransactionSource.ROUTE,
|
||||
"uri_template": TransactionSource.ROUTE,
|
||||
"url": TransactionSource.ROUTE,
|
||||
}
|
||||
|
||||
|
||||
@@ -248,8 +258,8 @@ class Span:
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"trace_id",
|
||||
"span_id",
|
||||
"_trace_id",
|
||||
"_span_id",
|
||||
"parent_span_id",
|
||||
"same_process_as_parent",
|
||||
"sampled",
|
||||
@@ -266,10 +276,11 @@ class Span:
|
||||
"hub",
|
||||
"_context_manager_state",
|
||||
"_containing_transaction",
|
||||
"_local_aggregator",
|
||||
"scope",
|
||||
"origin",
|
||||
"name",
|
||||
"_flags",
|
||||
"_flags_capacity",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -290,8 +301,8 @@ class Span:
|
||||
name=None, # type: Optional[str]
|
||||
):
|
||||
# type: (...) -> None
|
||||
self.trace_id = trace_id or uuid.uuid4().hex
|
||||
self.span_id = span_id or uuid.uuid4().hex[16:]
|
||||
self._trace_id = trace_id
|
||||
self._span_id = span_id
|
||||
self.parent_span_id = parent_span_id
|
||||
self.same_process_as_parent = same_process_as_parent
|
||||
self.sampled = sampled
|
||||
@@ -305,6 +316,8 @@ class Span:
|
||||
self._tags = {} # type: MutableMapping[str, str]
|
||||
self._data = {} # type: Dict[str, Any]
|
||||
self._containing_transaction = containing_transaction
|
||||
self._flags = {} # type: Dict[str, bool]
|
||||
self._flags_capacity = 10
|
||||
|
||||
if hub is not None:
|
||||
warnings.warn(
|
||||
@@ -331,7 +344,6 @@ class Span:
|
||||
self.timestamp = None # type: Optional[datetime]
|
||||
|
||||
self._span_recorder = None # type: Optional[_SpanRecorder]
|
||||
self._local_aggregator = None # type: Optional[LocalAggregator]
|
||||
|
||||
self.update_active_thread()
|
||||
self.set_profiler_id(get_profiler_id())
|
||||
@@ -343,12 +355,31 @@ class Span:
|
||||
if self._span_recorder is None:
|
||||
self._span_recorder = _SpanRecorder(maxlen)
|
||||
|
||||
def _get_local_aggregator(self):
|
||||
# type: (...) -> LocalAggregator
|
||||
rv = self._local_aggregator
|
||||
if rv is None:
|
||||
rv = self._local_aggregator = LocalAggregator()
|
||||
return rv
|
||||
@property
|
||||
def trace_id(self):
|
||||
# type: () -> str
|
||||
if not self._trace_id:
|
||||
self._trace_id = uuid.uuid4().hex
|
||||
|
||||
return self._trace_id
|
||||
|
||||
@trace_id.setter
|
||||
def trace_id(self, value):
|
||||
# type: (str) -> None
|
||||
self._trace_id = value
|
||||
|
||||
@property
|
||||
def span_id(self):
|
||||
# type: () -> str
|
||||
if not self._span_id:
|
||||
self._span_id = uuid.uuid4().hex[16:]
|
||||
|
||||
return self._span_id
|
||||
|
||||
@span_id.setter
|
||||
def span_id(self, value):
|
||||
# type: (str) -> None
|
||||
self._span_id = value
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
@@ -377,12 +408,14 @@ class Span:
|
||||
def __exit__(self, ty, value, tb):
|
||||
# type: (Optional[Any], Optional[Any], Optional[Any]) -> None
|
||||
if value is not None and should_be_treated_as_error(ty, value):
|
||||
self.set_status(SPANSTATUS.INTERNAL_ERROR)
|
||||
if self.status != SPANSTATUS.ERROR:
|
||||
self.set_status(SPANSTATUS.INTERNAL_ERROR)
|
||||
|
||||
scope, old_span = self._context_manager_state
|
||||
del self._context_manager_state
|
||||
self.finish(scope)
|
||||
scope.span = old_span
|
||||
with capture_internal_exceptions():
|
||||
scope, old_span = self._context_manager_state
|
||||
del self._context_manager_state
|
||||
self.finish(scope)
|
||||
scope.span = old_span
|
||||
|
||||
@property
|
||||
def containing_transaction(self):
|
||||
@@ -468,6 +501,8 @@ class Span:
|
||||
def continue_from_headers(
|
||||
cls,
|
||||
headers, # type: Mapping[str, str]
|
||||
*,
|
||||
_sample_rand=None, # type: Optional[str]
|
||||
**kwargs, # type: Any
|
||||
):
|
||||
# type: (...) -> Transaction
|
||||
@@ -476,6 +511,8 @@ class Span:
|
||||
the ``sentry-trace`` and ``baggage`` headers).
|
||||
|
||||
:param headers: The dictionary with the HTTP headers to pull information from.
|
||||
:param _sample_rand: If provided, we override the sample_rand value from the
|
||||
incoming headers with this value. (internal use only)
|
||||
"""
|
||||
# TODO move this to the Transaction class
|
||||
if cls is Span:
|
||||
@@ -486,7 +523,9 @@ class Span:
|
||||
|
||||
# TODO-neel move away from this kwargs stuff, it's confusing and opaque
|
||||
# make more explicit
|
||||
baggage = Baggage.from_incoming_header(headers.get(BAGGAGE_HEADER_NAME))
|
||||
baggage = Baggage.from_incoming_header(
|
||||
headers.get(BAGGAGE_HEADER_NAME), _sample_rand=_sample_rand
|
||||
)
|
||||
kwargs.update({BAGGAGE_HEADER_NAME: baggage})
|
||||
|
||||
sentrytrace_kwargs = extract_sentrytrace_data(
|
||||
@@ -583,12 +622,31 @@ class Span:
|
||||
# type: (str, Any) -> None
|
||||
self._data[key] = value
|
||||
|
||||
def update_data(self, data):
|
||||
# type: (Dict[str, Any]) -> None
|
||||
self._data.update(data)
|
||||
|
||||
def set_flag(self, flag, result):
|
||||
# type: (str, bool) -> None
|
||||
if len(self._flags) < self._flags_capacity:
|
||||
self._flags[flag] = result
|
||||
|
||||
def set_status(self, value):
|
||||
# type: (str) -> None
|
||||
self.status = value
|
||||
|
||||
def set_measurement(self, name, value, unit=""):
|
||||
# type: (str, float, MeasurementUnit) -> None
|
||||
"""
|
||||
.. deprecated:: 2.28.0
|
||||
This function is deprecated and will be removed in the next major release.
|
||||
"""
|
||||
|
||||
warnings.warn(
|
||||
"`set_measurement()` is deprecated and will be removed in the next major version. Please use `set_data()` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self._measurements[name] = {"value": value, "unit": unit}
|
||||
|
||||
def set_thread(self, thread_id, thread_name):
|
||||
@@ -674,11 +732,6 @@ class Span:
|
||||
if self.status:
|
||||
self._tags["status"] = self.status
|
||||
|
||||
if self._local_aggregator is not None:
|
||||
metrics_summary = self._local_aggregator.to_json()
|
||||
if metrics_summary:
|
||||
rv["_metrics_summary"] = metrics_summary
|
||||
|
||||
if len(self._measurements) > 0:
|
||||
rv["measurements"] = self._measurements
|
||||
|
||||
@@ -686,7 +739,9 @@ class Span:
|
||||
if tags:
|
||||
rv["tags"] = tags
|
||||
|
||||
data = self._data
|
||||
data = {}
|
||||
data.update(self._flags)
|
||||
data.update(self._data)
|
||||
if data:
|
||||
rv["data"] = data
|
||||
|
||||
@@ -770,6 +825,7 @@ class Transaction(Span):
|
||||
"_profile",
|
||||
"_continuous_profile",
|
||||
"_baggage",
|
||||
"_sample_rand",
|
||||
)
|
||||
|
||||
def __init__( # type: ignore[misc]
|
||||
@@ -777,11 +833,10 @@ class Transaction(Span):
|
||||
name="", # type: str
|
||||
parent_sampled=None, # type: Optional[bool]
|
||||
baggage=None, # type: Optional[Baggage]
|
||||
source=TRANSACTION_SOURCE_CUSTOM, # type: str
|
||||
source=TransactionSource.CUSTOM, # type: str
|
||||
**kwargs, # type: Unpack[SpanKwargs]
|
||||
):
|
||||
# type: (...) -> None
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.name = name
|
||||
@@ -794,6 +849,14 @@ class Transaction(Span):
|
||||
self._continuous_profile = None # type: Optional[ContinuousProfile]
|
||||
self._baggage = baggage
|
||||
|
||||
baggage_sample_rand = (
|
||||
None if self._baggage is None else self._baggage._sample_rand()
|
||||
)
|
||||
if baggage_sample_rand is not None:
|
||||
self._sample_rand = baggage_sample_rand
|
||||
else:
|
||||
self._sample_rand = _generate_sample_rand(self.trace_id)
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
return (
|
||||
@@ -894,6 +957,12 @@ class Transaction(Span):
|
||||
|
||||
return scope_or_hub
|
||||
|
||||
def _get_log_representation(self):
|
||||
# type: () -> str
|
||||
return "{op}transaction <{name}>".format(
|
||||
op=("<" + self.op + "> " if self.op else ""), name=self.name
|
||||
)
|
||||
|
||||
def finish(
|
||||
self,
|
||||
scope=None, # type: Optional[sentry_sdk.Scope]
|
||||
@@ -922,9 +991,7 @@ class Transaction(Span):
|
||||
|
||||
# For backwards compatibility, we must handle the case where `scope`
|
||||
# or `hub` could both either be a `Scope` or a `Hub`.
|
||||
scope = self._get_scope_from_finish_args(
|
||||
scope, hub
|
||||
) # type: Optional[sentry_sdk.Scope]
|
||||
scope = self._get_scope_from_finish_args(scope, hub) # type: Optional[sentry_sdk.Scope]
|
||||
|
||||
scope = scope or self.scope or sentry_sdk.get_current_scope()
|
||||
client = sentry_sdk.get_client()
|
||||
@@ -965,6 +1032,32 @@ class Transaction(Span):
|
||||
|
||||
super().finish(scope, end_timestamp)
|
||||
|
||||
status_code = self._data.get(SPANDATA.HTTP_STATUS_CODE)
|
||||
if (
|
||||
status_code is not None
|
||||
and status_code in client.options["trace_ignore_status_codes"]
|
||||
):
|
||||
logger.debug(
|
||||
"[Tracing] Discarding {transaction_description} because the HTTP status code {status_code} is matched by trace_ignore_status_codes: {trace_ignore_status_codes}".format(
|
||||
transaction_description=self._get_log_representation(),
|
||||
status_code=self._data[SPANDATA.HTTP_STATUS_CODE],
|
||||
trace_ignore_status_codes=client.options[
|
||||
"trace_ignore_status_codes"
|
||||
],
|
||||
)
|
||||
)
|
||||
if client.transport:
|
||||
client.transport.record_lost_event(
|
||||
"event_processor", data_category="transaction"
|
||||
)
|
||||
|
||||
num_spans = len(self._span_recorder.spans) + 1
|
||||
client.transport.record_lost_event(
|
||||
"event_processor", data_category="span", quantity=num_spans
|
||||
)
|
||||
|
||||
self.sampled = False
|
||||
|
||||
if not self.sampled:
|
||||
# At this point a `sampled = None` should have already been resolved
|
||||
# to a concrete decision.
|
||||
@@ -1015,21 +1108,24 @@ class Transaction(Span):
|
||||
|
||||
event["measurements"] = self._measurements
|
||||
|
||||
# This is here since `to_json` is not invoked. This really should
|
||||
# be gone when we switch to onlyspans.
|
||||
if self._local_aggregator is not None:
|
||||
metrics_summary = self._local_aggregator.to_json()
|
||||
if metrics_summary:
|
||||
event["_metrics_summary"] = metrics_summary
|
||||
|
||||
return scope.capture_event(event)
|
||||
|
||||
def set_measurement(self, name, value, unit=""):
|
||||
# type: (str, float, MeasurementUnit) -> None
|
||||
"""
|
||||
.. deprecated:: 2.28.0
|
||||
This function is deprecated and will be removed in the next major release.
|
||||
"""
|
||||
|
||||
warnings.warn(
|
||||
"`set_measurement()` is deprecated and will be removed in the next major version. Please use `set_data()` instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self._measurements[name] = {"value": value, "unit": unit}
|
||||
|
||||
def set_context(self, key, value):
|
||||
# type: (str, Any) -> None
|
||||
# type: (str, dict[str, Any]) -> None
|
||||
"""Sets a context. Transactions can have multiple contexts
|
||||
and they should follow the format described in the "Contexts Interface"
|
||||
documentation.
|
||||
@@ -1102,9 +1198,7 @@ class Transaction(Span):
|
||||
"""
|
||||
client = sentry_sdk.get_client()
|
||||
|
||||
transaction_description = "{op}transaction <{name}>".format(
|
||||
op=("<" + self.op + "> " if self.op else ""), name=self.name
|
||||
)
|
||||
transaction_description = self._get_log_representation()
|
||||
|
||||
# nothing to do if tracing is disabled
|
||||
if not has_tracing_enabled(client.options):
|
||||
@@ -1123,8 +1217,8 @@ class Transaction(Span):
|
||||
sample_rate = (
|
||||
client.options["traces_sampler"](sampling_context)
|
||||
if callable(client.options.get("traces_sampler"))
|
||||
# default inheritance behavior
|
||||
else (
|
||||
# default inheritance behavior
|
||||
sampling_context["parent_sampled"]
|
||||
if sampling_context["parent_sampled"] is not None
|
||||
else client.options["traces_sample_rate"]
|
||||
@@ -1164,10 +1258,8 @@ class Transaction(Span):
|
||||
self.sampled = False
|
||||
return
|
||||
|
||||
# Now we roll the dice. random.random is inclusive of 0, but not of 1,
|
||||
# so strict < is safe here. In case sample_rate is a boolean, cast it
|
||||
# to a float (True becomes 1.0 and False becomes 0.0)
|
||||
self.sampled = random.random() < self.sample_rate
|
||||
# Now we roll the dice.
|
||||
self.sampled = self._sample_rand < self.sample_rate
|
||||
|
||||
if self.sampled:
|
||||
logger.debug(
|
||||
@@ -1222,6 +1314,10 @@ class NoOpSpan(Span):
|
||||
# type: (str, Any) -> None
|
||||
pass
|
||||
|
||||
def update_data(self, data):
|
||||
# type: (Dict[str, Any]) -> None
|
||||
pass
|
||||
|
||||
def set_status(self, value):
|
||||
# type: (str) -> None
|
||||
pass
|
||||
@@ -1264,7 +1360,7 @@ class NoOpSpan(Span):
|
||||
pass
|
||||
|
||||
def set_context(self, key, value):
|
||||
# type: (str, Any) -> None
|
||||
# type: (str, dict[str, Any]) -> None
|
||||
pass
|
||||
|
||||
def init_span_recorder(self, maxlen):
|
||||
@@ -1279,43 +1375,103 @@ class NoOpSpan(Span):
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@overload
|
||||
def trace(func=None):
|
||||
# type: (None) -> Callable[[Callable[P, R]], Callable[P, R]]
|
||||
def trace(
|
||||
func=None, *, op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT
|
||||
):
|
||||
# type: (None, Optional[str], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Callable[[Callable[P, R]], Callable[P, R]]
|
||||
# Handles: @trace() and @trace(op="custom")
|
||||
pass
|
||||
|
||||
@overload
|
||||
def trace(func):
|
||||
# type: (Callable[P, R]) -> Callable[P, R]
|
||||
# Handles: @trace
|
||||
pass
|
||||
|
||||
|
||||
def trace(func=None):
|
||||
# type: (Optional[Callable[P, R]]) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]]
|
||||
def trace(
|
||||
func=None, *, op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT
|
||||
):
|
||||
# type: (Optional[Callable[P, R]], Optional[str], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]]
|
||||
"""
|
||||
Decorator to start a child span under the existing current transaction.
|
||||
If there is no current transaction, then nothing will be traced.
|
||||
Decorator to start a child span around a function call.
|
||||
|
||||
.. code-block::
|
||||
:caption: Usage
|
||||
This decorator automatically creates a new span when the decorated function
|
||||
is called, and finishes the span when the function returns or raises an exception.
|
||||
|
||||
:param func: The function to trace. When used as a decorator without parentheses,
|
||||
this is the function being decorated. When used with parameters (e.g.,
|
||||
``@trace(op="custom")``, this should be None.
|
||||
:type func: Callable or None
|
||||
|
||||
:param op: The operation name for the span. This is a high-level description
|
||||
of what the span represents (e.g., "http.client", "db.query").
|
||||
You can use predefined constants from :py:class:`sentry_sdk.consts.OP`
|
||||
or provide your own string. If not provided, a default operation will
|
||||
be assigned based on the template.
|
||||
:type op: str or None
|
||||
|
||||
:param name: The human-readable name/description for the span. If not provided,
|
||||
defaults to the function name. This provides more specific details about
|
||||
what the span represents (e.g., "GET /api/users", "process_user_data").
|
||||
:type name: str or None
|
||||
|
||||
:param attributes: A dictionary of key-value pairs to add as attributes to the span.
|
||||
Attribute values must be strings, integers, floats, or booleans. These
|
||||
attributes provide additional context about the span's execution.
|
||||
:type attributes: dict[str, Any] or None
|
||||
|
||||
:param template: The type of span to create. This determines what kind of
|
||||
span instrumentation and data collection will be applied. Use predefined
|
||||
constants from :py:class:`sentry_sdk.consts.SPANTEMPLATE`.
|
||||
The default is `SPANTEMPLATE.DEFAULT` which is the right choice for most
|
||||
use cases.
|
||||
:type template: :py:class:`sentry_sdk.consts.SPANTEMPLATE`
|
||||
|
||||
:returns: When used as ``@trace``, returns the decorated function. When used as
|
||||
``@trace(...)`` with parameters, returns a decorator function.
|
||||
:rtype: Callable or decorator function
|
||||
|
||||
Example::
|
||||
|
||||
import sentry_sdk
|
||||
from sentry_sdk.consts import OP, SPANTEMPLATE
|
||||
|
||||
# Simple usage with default values
|
||||
@sentry_sdk.trace
|
||||
def my_function():
|
||||
...
|
||||
def process_data():
|
||||
# Function implementation
|
||||
pass
|
||||
|
||||
@sentry_sdk.trace
|
||||
async def my_async_function():
|
||||
...
|
||||
# With custom parameters
|
||||
@sentry_sdk.trace(
|
||||
op=OP.DB_QUERY,
|
||||
name="Get user data",
|
||||
attributes={"postgres": True}
|
||||
)
|
||||
def make_db_query(sql):
|
||||
# Function implementation
|
||||
pass
|
||||
|
||||
# With a custom template
|
||||
@sentry_sdk.trace(template=SPANTEMPLATE.AI_TOOL)
|
||||
def calculate_interest_rate(amount, rate, years):
|
||||
# Function implementation
|
||||
pass
|
||||
"""
|
||||
from sentry_sdk.tracing_utils import start_child_span_decorator
|
||||
from sentry_sdk.tracing_utils import create_span_decorator
|
||||
|
||||
decorator = create_span_decorator(
|
||||
op=op,
|
||||
name=name,
|
||||
attributes=attributes,
|
||||
template=template,
|
||||
)
|
||||
|
||||
# This patterns allows usage of both @sentry_traced and @sentry_traced(...)
|
||||
# See https://stackoverflow.com/questions/52126071/decorator-with-arguments-avoid-parenthesis-when-no-arguments/52126278
|
||||
if func:
|
||||
return start_child_span_decorator(func)
|
||||
return decorator(func)
|
||||
else:
|
||||
return start_child_span_decorator
|
||||
return decorator
|
||||
|
||||
|
||||
# Circular imports
|
||||
@@ -1324,11 +1480,7 @@ from sentry_sdk.tracing_utils import (
|
||||
Baggage,
|
||||
EnvironHeaders,
|
||||
extract_sentrytrace_data,
|
||||
_generate_sample_rand,
|
||||
has_tracing_enabled,
|
||||
maybe_create_breadcrumbs_from_span,
|
||||
)
|
||||
|
||||
with warnings.catch_warnings():
|
||||
# The code in this file which uses `LocalAggregator` is only called from the deprecated `metrics` module.
|
||||
warnings.simplefilter("ignore", DeprecationWarning)
|
||||
from sentry_sdk.metrics import LocalAggregator
|
||||
|
||||
Reference in New Issue
Block a user