Skip to content

Commit

Permalink
Move add_breadcrumb and session function from Hub to Scope (#2544)
Browse files Browse the repository at this point in the history
Moved some functionality from Hub to Scope or Client:
- moved add_breadcrumb from Hub to Scope
- moved session functions from Hub to Scope
- moved get_integration1 from Hub to Client.

This is preparation work for refactoring how we deal with Hubs and Scopes in the future.
  • Loading branch information
antonpirker authored Dec 6, 2023
1 parent 67c963d commit 354d7bb
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 56 deletions.
19 changes: 19 additions & 0 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
from typing import Dict
from typing import Optional
from typing import Sequence
from typing import Type
from typing import Union

from sentry_sdk.integrations import Integration
from sentry_sdk.scope import Scope
from sentry_sdk._types import Event, Hint
from sentry_sdk.session import Session
Expand Down Expand Up @@ -653,6 +656,22 @@ def capture_session(
else:
self.session_flusher.add_session(session)

def get_integration(
self, name_or_class # type: Union[str, Type[Integration]]
):
# type: (...) -> Any
"""Returns the integration for this client by name or class.
If the client does not have that integration then `None` is returned.
"""
if isinstance(name_or_class, str):
integration_name = name_or_class
elif name_or_class.identifier is not None:
integration_name = name_or_class.identifier
else:
raise ValueError("Integration has no name")

return self.integrations.get(integration_name)

def close(
self,
timeout=None, # type: Optional[float]
Expand Down
62 changes: 10 additions & 52 deletions sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from contextlib import contextmanager

from sentry_sdk._compat import datetime_utcnow, with_metaclass
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
Expand All @@ -15,7 +15,6 @@
BAGGAGE_HEADER_NAME,
SENTRY_TRACE_HEADER_NAME,
)
from sentry_sdk.session import Session
from sentry_sdk.tracing_utils import (
has_tracing_enabled,
normalize_incoming_data,
Expand Down Expand Up @@ -294,18 +293,9 @@ def get_integration(
If the return value is not `None` the hub is guaranteed to have a
client attached.
"""
if isinstance(name_or_class, str):
integration_name = name_or_class
elif name_or_class.identifier is not None:
integration_name = name_or_class.identifier
else:
raise ValueError("Integration has no name")

client = self.client
if client is not None:
rv = client.integrations.get(integration_name)
if rv is not None:
return rv
return client.get_integration(name_or_class)

@property
def client(self):
Expand Down Expand Up @@ -430,31 +420,9 @@ def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
logger.info("Dropped breadcrumb because no client bound")
return

crumb = dict(crumb or ()) # type: Breadcrumb
crumb.update(kwargs)
if not crumb:
return

hint = dict(hint or ()) # type: Hint

if crumb.get("timestamp") is None:
crumb["timestamp"] = datetime_utcnow()
if crumb.get("type") is None:
crumb["type"] = "default"

if client.options["before_breadcrumb"] is not None:
new_crumb = client.options["before_breadcrumb"](crumb, hint)
else:
new_crumb = crumb

if new_crumb is not None:
scope._breadcrumbs.append(new_crumb)
else:
logger.info("before breadcrumb dropped breadcrumb (%s)", crumb)
kwargs["client"] = client

max_breadcrumbs = client.options["max_breadcrumbs"] # type: int
while len(scope._breadcrumbs) > max_breadcrumbs:
scope._breadcrumbs.popleft()
scope.add_breadcrumb(crumb, hint, **kwargs)

def start_span(self, span=None, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
# type: (Optional[Span], str, Any) -> Span
Expand Down Expand Up @@ -712,26 +680,17 @@ def start_session(
):
# type: (...) -> None
"""Starts a new session."""
self.end_session()
client, scope = self._stack[-1]
scope._session = Session(
release=client.options["release"] if client else None,
environment=client.options["environment"] if client else None,
user=scope._user,
scope.start_session(
client=client,
session_mode=session_mode,
)

def end_session(self):
# type: (...) -> None
"""Ends the current session if there is one."""
client, scope = self._stack[-1]
session = scope._session
self.scope._session = None

if session is not None:
session.close()
if client is not None:
client.capture_session(session)
scope.end_session(client=client)

def stop_auto_session_tracking(self):
# type: (...) -> None
Expand All @@ -740,18 +699,17 @@ 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`.
"""
self.end_session()
client, scope = self._stack[-1]
scope._force_auto_session_tracking = False
scope.stop_auto_session_tracking(client=client)

def resume_auto_session_tracking(self):
# type: (...) -> None
"""Resumes automatic session tracking for the current scope if
disabled earlier. This requires that generally automatic session
tracking is enabled.
"""
client, scope = self._stack[-1]
scope._force_auto_session_tracking = None
scope = self._stack[-1][1]
scope.resume_auto_session_tracking()

def flush(
self,
Expand Down
99 changes: 95 additions & 4 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import uuid

from sentry_sdk.attachments import Attachment
from sentry_sdk._compat import datetime_utcnow
from sentry_sdk.consts import FALSE_VALUES
from sentry_sdk._functools import wraps
from sentry_sdk.session import Session
from sentry_sdk.tracing_utils import (
Baggage,
extract_sentrytrace_data,
Expand All @@ -20,9 +23,6 @@
from sentry_sdk._types import TYPE_CHECKING
from sentry_sdk.utils import logger, capture_internal_exceptions

from sentry_sdk.consts import FALSE_VALUES


if TYPE_CHECKING:
from typing import Any
from typing import Dict
Expand All @@ -36,6 +36,7 @@

from sentry_sdk._types import (
Breadcrumb,
BreadcrumbHint,
Event,
EventProcessor,
ErrorProcessor,
Expand All @@ -46,7 +47,6 @@

from sentry_sdk.profiler import Profile
from sentry_sdk.tracing import Span
from sentry_sdk.session import Session

F = TypeVar("F", bound=Callable[..., Any])
T = TypeVar("T")
Expand Down Expand Up @@ -517,6 +517,97 @@ def add_attachment(
)
)

def add_breadcrumb(self, crumb=None, hint=None, **kwargs):
# type: (Optional[Breadcrumb], Optional[BreadcrumbHint], Any) -> None
"""
Adds a breadcrumb.
:param crumb: Dictionary with the data as the sentry v7/v8 protocol expects.
: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:
return

before_breadcrumb = client.options.get("before_breadcrumb")
max_breadcrumbs = client.options.get("max_breadcrumbs")

crumb = dict(crumb or ()) # type: Breadcrumb
crumb.update(kwargs)
if not crumb:
return

hint = dict(hint or ()) # type: Hint

if crumb.get("timestamp") is None:
crumb["timestamp"] = datetime_utcnow()
if crumb.get("type") is None:
crumb["type"] = "default"

if before_breadcrumb is not None:
new_crumb = before_breadcrumb(crumb, hint)
else:
new_crumb = crumb

if new_crumb is not None:
self._breadcrumbs.append(new_crumb)
else:
logger.info("before breadcrumb dropped breadcrumb (%s)", crumb)

while len(self._breadcrumbs) > max_breadcrumbs:
self._breadcrumbs.popleft()

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._session = Session(
release=client.options["release"] if client else None,
environment=client.options["environment"] if client else None,
user=self._user,
session_mode=session_mode,
)

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)

def stop_auto_session_tracking(self, *args, **kwargs):
# type: (*Any, **Any) -> None
"""Stops automatic session tracking.
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._force_auto_session_tracking = False

def resume_auto_session_tracking(self):
# type: (...) -> None
"""Resumes automatic session tracking for the current scope if
disabled earlier. This requires that generally automatic session
tracking is enabled.
"""
self._force_auto_session_tracking = None

def add_event_processor(
self, func # type: EventProcessor
):
Expand Down

0 comments on commit 354d7bb

Please sign in to comment.