Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 2 additions & 33 deletions sentry_sdk/integrations/fastapi.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import sys
from copy import deepcopy
from functools import wraps
from typing import TYPE_CHECKING

import sentry_sdk
from sentry_sdk.consts import SPANDATA
from sentry_sdk.integrations import DidNotEnable
from sentry_sdk.traces import StreamedSpan, get_current_span
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
from sentry_sdk.tracing_utils import has_span_streaming_enabled
from sentry_sdk.utils import has_data_collection_enabled, transaction_from_function

if TYPE_CHECKING:
Expand All @@ -21,6 +19,7 @@
StarletteIntegration,
StarletteRequestExtractor,
_get_cached_request_body_attribute,
_wrap_sync_handler,
)
except DidNotEnable:
raise DidNotEnable("Starlette is not installed")
Expand Down Expand Up @@ -184,38 +183,8 @@ def _sentry_get_request_handler(*args: "Any", **kwargs: "Any") -> "Any":
dependant
and dependant.call is not None
and not iscoroutinefunction(dependant.call)
# FastAPI >= 0.137 calls get_request_handler() on every request
# (router-tree traversal) rather than once at registration. Guard
# against accumulating _sentry_call wrappers on the shared
# dependant object, which would cause a RecursionError after ~987
# requests as the call chain grows past Python's recursion limit.
and not getattr(dependant.call, "_sentry_is_patched", False)
):
old_call = dependant.call

@wraps(old_call)
def _sentry_call(*args: "Any", **kwargs: "Any") -> "Any":
current_scope = sentry_sdk.get_current_scope()

client = sentry_sdk.get_client()
if has_span_streaming_enabled(client.options):
current_span = current_scope.streamed_span

if type(current_span) is StreamedSpan:
segment = current_span._segment
segment._update_active_thread()

elif current_scope.transaction is not None:
current_scope.transaction.update_active_thread()

sentry_scope = sentry_sdk.get_isolation_scope()
if sentry_scope.profile is not None:
sentry_scope.profile.update_active_thread_id()

return old_call(*args, **kwargs)

_sentry_call._sentry_is_patched = True # type: ignore[attr-defined]
dependant.call = _sentry_call
dependant.call = _wrap_sync_handler(dependant.call)

old_app = old_get_request_handler(*args, **kwargs)

Expand Down
44 changes: 32 additions & 12 deletions sentry_sdk/integrations/starlette.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,37 @@ def event_processor(event: "Event", hint: "Dict[str, Any]") -> "Event":
)


def _update_active_thread() -> None:
client = sentry_sdk.get_client()
current_scope = sentry_sdk.get_current_scope()

span_streaming = has_span_streaming_enabled(client.options)
if span_streaming:
current_span = current_scope.streamed_span

if type(current_span) is StreamedSpan:
current_span._segment._update_active_thread()
elif current_scope.transaction is not None:
current_scope.transaction.update_active_thread()

sentry_scope = sentry_sdk.get_isolation_scope()
if sentry_scope.profile is not None:
sentry_scope.profile.update_active_thread_id()


def _wrap_sync_handler(handler: "Callable[..., Any]") -> "Callable[..., Any]":
if getattr(handler, "_sentry_patched", False):
return handler

@functools.wraps(handler)
def _sentry_sync_handler(*args: "Any", **kwargs: "Any") -> "Any":
_update_active_thread()
return handler(*args, **kwargs)

_sentry_sync_handler._sentry_patched = True # type: ignore[attr-defined]
return _sentry_sync_handler


def patch_request_response() -> None:
old_request_response = starlette.routing.request_response

Expand All @@ -654,20 +685,9 @@ def _sentry_sync_func(*args: "Any", **kwargs: "Any") -> "Any":
if integration is None:
return old_func(*args, **kwargs)

_update_active_thread()
Comment thread
cursor[bot] marked this conversation as resolved.
current_scope = sentry_sdk.get_current_scope()

span_streaming = has_span_streaming_enabled(client.options)
if span_streaming:
current_span = current_scope.streamed_span

if type(current_span) is StreamedSpan:
current_span._segment._update_active_thread()
elif current_scope.transaction is not None:
current_scope.transaction.update_active_thread()

sentry_scope = sentry_sdk.get_isolation_scope()
if sentry_scope.profile is not None:
sentry_scope.profile.update_active_thread_id()

request = args[0]

Expand Down
Loading