sentry_sdk.scope
import os import sys import warnings from copy import copy, deepcopy from collections import deque from contextlib import contextmanager from enum import Enum from datetime import datetime, timezone from functools import wraps from itertools import chain import sentry_sdk from sentry_sdk._types import AnnotatedValue from sentry_sdk.attachments import Attachment from sentry_sdk.consts import ( DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES, INSTRUMENTER, SPANDATA, ) from sentry_sdk.feature_flags import FlagBuffer, DEFAULT_FLAG_CAPACITY from sentry_sdk.profiler.continuous_profiler import ( get_profiler_id, try_autostart_continuous_profiler, try_profile_lifecycle_trace_start, ) from sentry_sdk.profiler.transaction_profiler import Profile from sentry_sdk.session import Session from sentry_sdk.tracing_utils import ( Baggage, has_tracing_enabled, has_span_streaming_enabled, is_ignored_span, _make_sampling_decision, PropagationContext, ) from sentry_sdk.traces import _DEFAULT_PARENT_SPAN, StreamedSpan, NoOpStreamedSpan from sentry_sdk.tracing import ( BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME, NoOpSpan, Span, Transaction, ) from sentry_sdk.utils import ( capture_internal_exception, capture_internal_exceptions, ContextVar, datetime_from_isoformat, disable_capture_event, event_from_exception, exc_info_from_error, format_attribute, logger, has_logs_enabled, has_metrics_enabled, ) from typing import TYPE_CHECKING, cast if TYPE_CHECKING: from collections.abc import Mapping from typing import Any from typing import Callable from typing import Deque from typing import Dict from typing import Generator from typing import Iterator from typing import List from typing import Optional from typing import ParamSpec from typing import Tuple from typing import TypeVar from typing import Union from typing_extensions import Unpack from sentry_sdk._types import ( Attributes, AttributeValue, Breadcrumb, BreadcrumbHint, ErrorProcessor, Event, EventProcessor, ExcInfo, Hint, Log, LogLevelStr, Metric, SamplingContext, Type, ) from sentry_sdk.tracing import TransactionKwargs import sentry_sdk P = ParamSpec("P") R = TypeVar("R") F = TypeVar("F", bound=Callable[..., Any]) T = TypeVar("T") # Holds data that will be added to **all** events sent by this process. # In case this is a http server (think web framework) with multiple users # the data will be added to events of all users. # Typically this is used for process wide data such as the release. _global_scope: "Optional[Scope]" = None # Holds data for the active request. # This is used to isolate data for different requests or users. # The isolation scope is usually created by integrations, but may also # be created manually _isolation_scope = ContextVar("isolation_scope", default=None) # Holds data for the active span. # This can be used to manually add additional data to a span. _current_scope = ContextVar("current_scope", default=None) global_event_processors: "List[EventProcessor]" = [] # A function returning a (trace_id, span_id) tuple # from an external tracing source (such as otel) _external_propagation_context_fn: "Optional[Callable[[], Optional[Tuple[str, str]]]]" = None class ScopeType(Enum): CURRENT = "current" ISOLATION = "isolation" GLOBAL = "global" MERGED = "merged" class _ScopeManager: def __init__(self, hub: "Optional[Any]" = None) -> None: self._old_scopes: "List[Scope]" = [] def __enter__(self) -> "Scope": isolation_scope = Scope.get_isolation_scope() self._old_scopes.append(isolation_scope) forked_scope = isolation_scope.fork() _isolation_scope.set(forked_scope) return forked_scope def __exit__(self, exc_type: "Any", exc_value: "Any", tb: "Any") -> None: old_scope = self._old_scopes.pop() _isolation_scope.set(old_scope) def add_global_event_processor(processor: "EventProcessor") -> None: global_event_processors.append(processor) def register_external_propagation_context( fn: "Callable[[], Optional[Tuple[str, str]]]", ) -> None: global _external_propagation_context_fn _external_propagation_context_fn = fn def remove_external_propagation_context() -> None: global _external_propagation_context_fn _external_propagation_context_fn = None def get_external_propagation_context() -> "Optional[Tuple[str, str]]": return ( _external_propagation_context_fn() if _external_propagation_context_fn else None ) def has_external_propagation_context() -> bool: return _external_propagation_context_fn is not None def _attr_setter(fn: "Any") -> "Any": return property(fset=fn, doc=fn.__doc__) def _disable_capture(fn: "F") -> "F": @wraps(fn) def wrapper(self: "Any", *args: "Dict[str, Any]", **kwargs: "Any") -> "Any": if not self._should_capture: return try: self._should_capture = False return fn(self, *args, **kwargs) finally: self._should_capture = True return wrapper # type: ignore class Scope: """The scope holds extra information that should be sent with all events that belong to it. """ # NOTE: Even though it should not happen, the scope needs to not crash when # accessed by multiple threads. It's fine if it's full of races, but those # races should never make the user application crash. # # The same needs to hold for any accesses of the scope the SDK makes. __slots__ = ( "_level", "_name", "_fingerprint", # note that for legacy reasons, _transaction is the transaction *name*, # not a Transaction object (the object is stored in _span) "_transaction", "_transaction_info", "_user", "_tags", "_contexts", "_extras", "_breadcrumbs", "_n_breadcrumbs_truncated", "_gen_ai_original_message_count", "_gen_ai_conversation_id", "_event_processors", "_error_processors", "_should_capture", "_span", "_session", "_attachments", "_force_auto_session_tracking", "_profile", "_propagation_context", "client", "_type", "_last_event_id", "_flags", "_attributes", ) def __init__( self, ty: "Optional[ScopeType]" = None, client: "Optional[sentry_sdk.Client]" = None, ) -> None: self._type = ty self._event_processors: "List[EventProcessor]" = [] self._error_processors: "List[ErrorProcessor]" = [] self._name: "Optional[str]" = None self._propagation_context: "Optional[PropagationContext]" = None self._n_breadcrumbs_truncated: int = 0 self._gen_ai_original_message_count: "Dict[str, int]" = {} self.client: "sentry_sdk.client.BaseClient" = NonRecordingClient() if client is not None: self.set_client(client) self.clear() incoming_trace_information = self._load_trace_data_from_env() self.generate_propagation_context(incoming_data=incoming_trace_information) def __copy__(self) -> "Scope": """ Returns a copy of this scope. This also creates a copy of all referenced data structures. """ rv: "Scope" = object.__new__(self.__class__) rv._type = self._type rv.client = self.client rv._level = self._level rv._name = self._name rv._fingerprint = self._fingerprint rv._transaction = self._transaction rv._transaction_info = self._transaction_info.copy() rv._user = self._user rv._tags = self._tags.copy() rv._contexts = self._contexts.copy() rv._extras = self._extras.copy() rv._breadcrumbs = copy(self._breadcrumbs) rv._n_breadcrumbs_truncated = self._n_breadcrumbs_truncated rv._gen_ai_original_message_count = self._gen_ai_original_message_count.copy() rv._event_processors = self._event_processors.copy() rv._error_processors = self._error_processors.copy() rv._propagation_context = self._propagation_context rv._should_capture = self._should_capture rv._span = self._span rv._session = self._session rv._force_auto_session_tracking = self._force_auto_session_tracking rv._attachments = self._attachments.copy() rv._profile = self._profile rv._last_event_id = self._last_event_id rv._flags = deepcopy(self._flags) rv._attributes = self._attributes.copy() rv._gen_ai_conversation_id = self._gen_ai_conversation_id return rv @classmethod def get_current_scope(cls) -> "Scope": """ .. versionadded:: 2.0.0 Returns the current scope. """ current_scope = _current_scope.get() if current_scope is None: current_scope = Scope(ty=ScopeType.CURRENT) _current_scope.set(current_scope) return current_scope