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
23 changes: 2 additions & 21 deletions sentry_sdk/integrations/fastapi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import sys
from copy import deepcopy
from functools import wraps
from typing import TYPE_CHECKING

import sentry_sdk
Expand All @@ -25,6 +24,7 @@
StarletteIntegration,
StarletteRequestExtractor,
_get_cached_request_body_attribute,
_wrap_sync_handler,
)
except DidNotEnable:
raise DidNotEnable("Starlette is not installed or incompatible")
Expand Down Expand Up @@ -163,27 +163,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_span = sentry_sdk.get_current_span()

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

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
24 changes: 20 additions & 4 deletions sentry_sdk/integrations/starlette.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,25 @@ def event_processor(event: "Event", hint: "Dict[str, Any]") -> "Event":
)


def _update_active_thread() -> None:
span = sentry_sdk.get_current_span()
if type(span) is Span:
span._segment._update_active_thread()


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 @@ -569,10 +588,7 @@ def _sentry_sync_func(*args: "Any", **kwargs: "Any") -> "Any":
if integration is None:
return old_func(*args, **kwargs)

current_span = sentry_sdk.get_current_span()

if type(current_span) is Span:
current_span._segment._update_active_thread()
_update_active_thread()

sentry_scope = sentry_sdk.get_isolation_scope()

Expand Down
Loading