diff --git a/sentry_sdk/debug.py b/sentry_sdk/debug.py
index fe8ae50cea..257fa6e75c 100644
--- a/sentry_sdk/debug.py
+++ b/sentry_sdk/debug.py
@@ -2,6 +2,7 @@
import logging
from sentry_sdk import utils
+from sentry_sdk.api import get_client
from sentry_sdk.hub import Hub
from sentry_sdk.utils import logger
from sentry_sdk.client import _client_init_debug
@@ -13,10 +14,8 @@ def filter(self, record):
# type: (LogRecord) -> bool
if _client_init_debug.get(False):
return True
- hub = Hub.current
- if hub is not None and hub.client is not None:
- return hub.client.options["debug"]
- return False
+
+ return get_client().options["debug"]
def init_debug_support():
diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py
index 45afb56cc9..90a87c8890 100644
--- a/sentry_sdk/hub.py
+++ b/sentry_sdk/hub.py
@@ -6,7 +6,7 @@
from sentry_sdk._compat import with_metaclass
from sentry_sdk.consts import INSTRUMENTER
from sentry_sdk.scope import Scope
-from sentry_sdk.client import Client
+from sentry_sdk.client import Client, NoopClient
from sentry_sdk.tracing import (
NoOpSpan,
Span,
@@ -266,21 +266,19 @@ def get_integration(
If the return value is not `None` the hub is guaranteed to have a
client attached.
"""
- client = self.client
- if client is not None:
- return client.get_integration(name_or_class)
+ Scope.get_client().get_integration(name_or_class)
@property
def client(self):
- # type: () -> Optional[Client]
+ # type: () -> Optional[NoopClient]
"""Returns the current client on the hub."""
- return self._stack[-1][0]
+ return Scope.get_client()
@property
def scope(self):
# type: () -> Scope
"""Returns the current scope on the hub."""
- return self._stack[-1][1]
+ return Scope.get_current_scope()
def last_event_id(self):
# type: () -> Optional[str]
@@ -292,8 +290,7 @@ def bind_client(
):
# type: (...) -> None
"""Binds a new client to the hub."""
- top = self._stack[-1]
- self._stack[-1] = (new, top[1])
+ Scope.get_global_scope().set_client(new)
def capture_event(self, event, hint=None, scope=None, **scope_kwargs):
# type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str]
@@ -313,12 +310,8 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs):
For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`.
The `scope` and `scope_kwargs` parameters are mutually exclusive.
"""
- client, top_scope = self._stack[-1]
- if client is None:
- return None
-
- last_event_id = top_scope.capture_event(
- event, hint, client=client, scope=scope, **scope_kwargs
+ last_event_id = Scope.get_current_scope().capture_event(
+ event, hint, scope=scope, **scope_kwargs
)
is_transaction = event.get("type") == "transaction"
@@ -347,12 +340,8 @@ def capture_message(self, message, level=None, scope=None, **scope_kwargs):
:returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`).
"""
- client, top_scope = self._stack[-1]
- if client is None:
- return None
-
- last_event_id = top_scope.capture_message(
- message, level=level, client=client, scope=scope, **scope_kwargs
+ last_event_id = Scope.get_current_scope().capture_message(
+ message, level=level, scope=scope, **scope_kwargs
)
if last_event_id is not None:
@@ -377,12 +366,8 @@ def capture_exception(self, error=None, scope=None, **scope_kwargs):
:returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`).
"""
- client, top_scope = self._stack[-1]
- if client is None:
- return None
-
- last_event_id = top_scope.capture_exception(
- error, client=client, scope=scope, **scope_kwargs
+ last_event_id = Scope.get_current_scope().capture_exception(
+ error, scope=scope, **scope_kwargs
)
if last_event_id is not None:
@@ -414,14 +399,7 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
:param hint: An optional value that can be used by `before_breadcrumb`
to customize the breadcrumbs that are emitted.
"""
- client, scope = self._stack[-1]
- if client is None:
- logger.info("Dropped breadcrumb because no client bound")
- return
-
- kwargs["client"] = client
-
- scope.add_breadcrumb(crumb, hint, **kwargs)
+ Scope.get_isolation_scope().add_breadcrumb(crumb, hint, **kwargs)
def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
# type: (Optional[Span], str, Any) -> Span
@@ -440,12 +418,12 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`.
"""
- client, scope = self._stack[-1]
-
+ # TODO: fix hub
kwargs["hub"] = self
- kwargs["client"] = client
- return scope.start_span(span=span, instrumenter=instrumenter, **kwargs)
+ return Scope.get_isolation_scope().start_span(
+ span=span, instrumenter=instrumenter, **kwargs
+ )
def start_transaction(
self, transaction=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs
@@ -475,12 +453,10 @@ def start_transaction(
For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`.
"""
- client, scope = self._stack[-1]
-
+ # TODO: fix hub
kwargs["hub"] = self
- kwargs["client"] = client
- return scope.start_transaction(
+ return Scope.get_isolation_scope().start_transaction(
transaction=transaction, instrumenter=instrumenter, **kwargs
)
@@ -489,9 +465,7 @@ def continue_trace(self, environ_or_headers, op=None, name=None, source=None):
"""
Sets the propagation context from environment or headers and returns a transaction.
"""
- scope = self._stack[-1][1]
-
- return scope.continue_trace(
+ return Scope.get_isolation_scope().continue_trace(
environ_or_headers=environ_or_headers, op=op, name=name, source=source
)
@@ -580,25 +554,19 @@ def configure_scope( # noqa
:returns: If no callback is provided, returns a context manager that returns the scope.
"""
-
- client, scope = self._stack[-1]
+ scope = Scope.get_isolation_scope()
if continue_trace:
- scope.generate_propagation_context()
+ Scope.get_isolation_scope().generate_propagation_context()
if callback is not None:
- if client is not None:
- callback(scope)
-
- return None
+ # TODO: used to return None when client is None. Check if this changes behavior.
+ callback(scope)
@contextmanager
def inner():
# type: () -> Generator[Scope, None, None]
- if client is not None:
- yield scope
- else:
- yield Scope()
+ yield scope
return inner()
@@ -607,17 +575,14 @@ def start_session(
):
# type: (...) -> None
"""Starts a new session."""
- client, scope = self._stack[-1]
- scope.start_session(
- client=client,
+ Scope.get_isolation_scope().start_session(
session_mode=session_mode,
)
def end_session(self):
# type: (...) -> None
"""Ends the current session if there is one."""
- client, scope = self._stack[-1]
- scope.end_session(client=client)
+ Scope.get_isolation_scope().end_session()
def stop_auto_session_tracking(self):
# type: (...) -> None
@@ -626,8 +591,7 @@ def stop_auto_session_tracking(self):
This temporarily session tracking for the current scope when called.
To resume session tracking call `resume_auto_session_tracking`.
"""
- client, scope = self._stack[-1]
- scope.stop_auto_session_tracking(client=client)
+ Scope.get_isolation_scope().stop_auto_session_tracking()
def resume_auto_session_tracking(self):
# type: (...) -> None
@@ -635,8 +599,7 @@ def resume_auto_session_tracking(self):
disabled earlier. This requires that generally automatic session
tracking is enabled.
"""
- scope = self._stack[-1][1]
- scope.resume_auto_session_tracking()
+ Scope.get_isolation_scope().resume_auto_session_tracking()
def flush(
self,
@@ -647,25 +610,21 @@ def flush(
"""
Alias for :py:meth:`sentry_sdk.Client.flush`
"""
- client, scope = self._stack[-1]
- if client is not None:
- return client.flush(timeout=timeout, callback=callback)
+ return Scope.get_client().flush(timeout=timeout, callback=callback)
def get_traceparent(self):
# type: () -> Optional[str]
"""
Returns the traceparent either from the active span or from the scope.
"""
- client, scope = self._stack[-1]
- return scope.get_traceparent(client=client)
+ return Scope.get_current_scope().get_traceparent()
def get_baggage(self):
# type: () -> Optional[str]
"""
Returns Baggage either from the active span or from the scope.
"""
- client, scope = self._stack[-1]
- baggage = scope.get_baggage(client=client)
+ baggage = Scope.get_current_scope().get_baggage()
if baggage is not None:
return baggage.serialize()
@@ -679,9 +638,9 @@ def iter_trace_propagation_headers(self, span=None):
from the span representing the request, if available, or the current
span on the scope if not.
"""
- client, scope = self._stack[-1]
-
- return scope.iter_trace_propagation_headers(span=span, client=client)
+ return Scope.get_current_scope().iter_trace_propagation_headers(
+ span=span,
+ )
def trace_propagation_meta(self, span=None):
# type: (Optional[Span]) -> str
@@ -694,8 +653,9 @@ def trace_propagation_meta(self, span=None):
"The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future."
)
- client, scope = self._stack[-1]
- return scope.trace_propagation_meta(span=span, client=client)
+ return Scope.get_current_scope().trace_propagation_meta(
+ span=span,
+ )
GLOBAL_HUB = Hub()
diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py
index b109a82342..ebd255fad7 100644
--- a/sentry_sdk/scope.py
+++ b/sentry_sdk/scope.py
@@ -258,7 +258,7 @@ def get_global_scope(cls):
def get_client(cls):
# type: () -> Union[sentry_sdk.Client, NoopClient]
"""
- Returns the current :py:class:`sentry_sdk.Client`.
+ Returns the currently used :py:class:`sentry_sdk.Client`.
This checks the current scope, the isolation scope and the global scope for a client.
If no client is available a :py:class:`sentry_sdk.client.NoopClient` is returned.
@@ -443,7 +443,7 @@ def get_traceparent(self, *args, **kwargs):
Returns the Sentry "sentry-trace" header (aka the traceparent) from the
currently active span or the scopes Propagation Context.
"""
- client = kwargs.pop("client", None)
+ client = Scope.get_client()
# If we have an active span, return traceparent from there
if (
@@ -464,7 +464,7 @@ def get_traceparent(self, *args, **kwargs):
def get_baggage(self, *args, **kwargs):
# type: (Any, Any) -> Optional[Baggage]
- client = kwargs.pop("client", None)
+ client = Scope.get_client()
# If we have an active span, return baggage from there
if (
@@ -514,18 +514,16 @@ def trace_propagation_meta(self, *args, **kwargs):
"The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future."
)
- client = kwargs.pop("client", None)
-
meta = ""
- sentry_trace = self.get_traceparent(client=client)
+ sentry_trace = self.get_traceparent()
if sentry_trace is not None:
meta += '' % (
SENTRY_TRACE_HEADER_NAME,
sentry_trace,
)
- baggage = self.get_baggage(client=client)
+ baggage = self.get_baggage()
if baggage is not None:
meta += '' % (
BAGGAGE_HEADER_NAME,
@@ -556,16 +554,14 @@ def iter_trace_propagation_headers(self, *args, **kwargs):
from the span representing the request, if available, or the current
span on the scope if not.
"""
- span = kwargs.pop("span", None)
- client = kwargs.pop("client", None)
-
- propagate_traces = client and client.options["propagate_traces"]
- if not propagate_traces:
+ client = Scope.get_client()
+ if not client.options.get("propagate_traces"):
return
+ span = kwargs.pop("span", None)
span = span or self.span
- if client and has_tracing_enabled(client.options) and span is not None:
+ if has_tracing_enabled(client.options) and span is not None:
for header in span.iter_headers():
yield header
else:
@@ -796,12 +792,14 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
:param hint: An optional value that can be used by `before_breadcrumb`
to customize the breadcrumbs that are emitted.
"""
- client = kwargs.pop("client", None)
- if client is None:
+ client = Scope.get_client()
+
+ if not client.is_active():
+ logger.info("Dropped breadcrumb because no client bound")
return
- before_breadcrumb = client.options.get("before_breadcrumb")
- max_breadcrumbs = client.options.get("max_breadcrumbs")
+ before_breadcrumb = client.options["before_breadcrumb"]
+ max_breadcrumbs = client.options["max_breadcrumbs"]
crumb = dict(crumb or ()) # type: Breadcrumb
crumb.update(kwargs)
@@ -828,8 +826,8 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
while len(self._breadcrumbs) > max_breadcrumbs:
self._breadcrumbs.popleft()
- def capture_event(self, event, hint=None, client=None, scope=None, **scope_kwargs):
- # type: (Event, Optional[Hint], Optional[sentry_sdk.Client], Optional[Scope], Any) -> Optional[str]
+ def capture_event(self, event, hint=None, scope=None, **scope_kwargs):
+ # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str]
"""
Captures an event.
@@ -850,18 +848,13 @@ def capture_event(self, event, hint=None, client=None, scope=None, **scope_kwarg
:returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`).
"""
- if client is None:
- return None
-
if scope_kwargs is not None:
scope = _merge_scopes(self, scope, scope_kwargs)
- return client.capture_event(event=event, hint=hint, scope=scope)
+ return Scope.get_client().capture_event(event=event, hint=hint, scope=scope)
- def capture_message(
- self, message, level=None, client=None, scope=None, **scope_kwargs
- ):
- # type: (str, Optional[str], Optional[sentry_sdk.Client], Optional[Scope], Any) -> Optional[str]
+ def capture_message(self, message, level=None, scope=None, **scope_kwargs):
+ # type: (str, Optional[str], Optional[Scope], Any) -> Optional[str]
"""
Captures a message.
@@ -880,9 +873,6 @@ def capture_message(
:returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`).
"""
- if client is None:
- return None
-
if level is None:
level = "info"
@@ -891,10 +881,10 @@ def capture_message(
"level": level,
}
- return self.capture_event(event, client=client, scope=scope, **scope_kwargs)
+ return self.capture_event(event, scope=scope, **scope_kwargs)
- def capture_exception(self, error=None, client=None, scope=None, **scope_kwargs):
- # type: (Optional[Union[BaseException, ExcInfo]], Optional[sentry_sdk.Client], Optional[Scope], Any) -> Optional[str]
+ def capture_exception(self, error=None, scope=None, **scope_kwargs):
+ # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str]
"""Captures an exception.
:param error: An exception to capture. If `None`, `sys.exc_info()` will be used.
@@ -910,20 +900,17 @@ def capture_exception(self, error=None, client=None, scope=None, **scope_kwargs)
:returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`).
"""
- if client is None:
- return None
-
if error is not None:
exc_info = exc_info_from_error(error)
else:
exc_info = sys.exc_info()
- event, hint = event_from_exception(exc_info, client_options=client.options)
+ event, hint = event_from_exception(
+ exc_info, client_options=Scope.get_client().options
+ )
try:
- return self.capture_event(
- event, hint=hint, client=client, scope=scope, **scope_kwargs
- )
+ return self.capture_event(event, hint=hint, scope=scope, **scope_kwargs)
except Exception:
self._capture_internal_exception(sys.exc_info())
@@ -970,9 +957,9 @@ def start_transaction(
For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`.
"""
hub = kwargs.pop("hub", None)
- client = kwargs.pop("client", None)
+ client = Scope.get_client()
- configuration_instrumenter = client and client.options["instrumenter"]
+ configuration_instrumenter = client.options.get("instrumenter")
if instrumenter != configuration_instrumenter:
return NoOpSpan()
@@ -1023,9 +1010,9 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`.
"""
- client = kwargs.get("client", None)
+ client = Scope.get_client()
- configuration_instrumenter = client and client.options["instrumenter"]
+ configuration_instrumenter = client.options.get("instrumenter")
if instrumenter != configuration_instrumenter:
return NoOpSpan()
@@ -1056,8 +1043,6 @@ def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
logger.warning(deprecation_msg)
return span
- kwargs.pop("client")
-
active_span = self.span
if active_span is not None:
new_child_span = active_span.start_child(**kwargs)
@@ -1093,14 +1078,14 @@ def continue_trace(self, environ_or_headers, op=None, name=None, source=None):
def start_session(self, *args, **kwargs):
# type: (*Any, **Any) -> None
"""Starts a new session."""
- client = kwargs.pop("client", None)
session_mode = kwargs.pop("session_mode", "application")
- self.end_session(client=client)
+ self.end_session()
+ client = Scope.get_client()
self._session = Session(
- release=client.options["release"] if client else None,
- environment=client.options["environment"] if client else None,
+ release=client.options.get("release"),
+ environment=client.options.get("environment"),
user=self._user,
session_mode=session_mode,
)
@@ -1108,15 +1093,12 @@ def start_session(self, *args, **kwargs):
def end_session(self, *args, **kwargs):
# type: (*Any, **Any) -> None
"""Ends the current session if there is one."""
- client = kwargs.pop("client", None)
-
session = self._session
self._session = None
if session is not None:
session.close()
- if client is not None:
- client.capture_session(session)
+ Scope.get_client().capture_session(session)
def stop_auto_session_tracking(self, *args, **kwargs):
# type: (*Any, **Any) -> None
@@ -1125,10 +1107,7 @@ def stop_auto_session_tracking(self, *args, **kwargs):
This temporarily session tracking for the current scope when called.
To resume session tracking call `resume_auto_session_tracking`.
"""
- client = kwargs.pop("client", None)
-
- self.end_session(client=client)
-
+ self.end_session()
self._force_auto_session_tracking = False
def resume_auto_session_tracking(self):