Skip to content

fix(Litestar): Apply failed_request_status_codes to exceptions raised in middleware #4074

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0b64a40
Allow to override asgi capturers
vrslev Feb 19, 2025
4f1963c
Add test
vrslev Feb 19, 2025
169177f
Override request exception catcher for litestar
vrslev Feb 19, 2025
3950e2a
Fix formatting
vrslev Feb 19, 2025
e1cd18e
Remove debugging print
vrslev Feb 19, 2025
2b91e0d
Merge branch 'master' into fix-litestar-middleware-failed-request-sta…
vrslev Feb 19, 2025
6ac821b
Merge branch 'master' into fix-litestar-middleware-failed-request-sta…
vrslev Feb 21, 2025
0c2da8f
Merge branch 'master' into fix-litestar-middleware-failed-request-sta…
antonpirker Mar 27, 2025
204c8a7
Add missing import line for test_litestar.py
vrslev Apr 1, 2025
bace5ce
Refactor exception capture in SentryAsgiMiddleware to use a shared fu…
vrslev Apr 1, 2025
fb1274f
Amend comment to clarify exception handling behavior in SentryLitesta…
vrslev Apr 1, 2025
babfe27
Adjust documentation to clarify exception handling behavior in Sentry…
vrslev Apr 1, 2025
33b11cc
Added docstrings to lifespan and request exception capture methods in…
vrslev Apr 1, 2025
41b5fd5
Adjusts exception documentation in SentryAsgiMiddleware for clarity
vrslev Apr 1, 2025
3819888
Adjust comment for Litestar integration to remove unnecessary newline
vrslev Apr 1, 2025
5b27acd
Adjusts exception handling comments for clarity in SentryLitestarASGI…
vrslev Apr 1, 2025
53baddd
Merge branch 'master' into fix-litestar-middleware-failed-request-sta…
vrslev Apr 1, 2025
c1c318d
Merge branch 'master' into fix-litestar-middleware-failed-request-sta…
vrslev Apr 7, 2025
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
22 changes: 19 additions & 3 deletions sentry_sdk/integrations/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ def __init__(
else:
self.__call__ = self._run_asgi2

def _capture_lifespan_exception(self, exc):
# type: (Exception) -> None
"""Capture exceptions raise in application lifespan handlers.

The separate function is needed to support overriding in derived integrations that use different catching mechanisms.
"""
return _capture_exception(exc=exc, mechanism_type=self.mechanism_type)

def _capture_request_exception(self, exc):
# type: (Exception) -> None
"""Capture exceptions raised in incoming request handlers.

The separate function is needed to support overriding in derived integrations that use different catching mechanisms.
"""
return _capture_exception(exc=exc, mechanism_type=self.mechanism_type)

def _run_asgi2(self, scope):
# type: (Any) -> Any
async def inner(receive, send):
Expand All @@ -158,7 +174,7 @@ async def _run_asgi3(self, scope, receive, send):
return await self._run_app(scope, receive, send, asgi_version=3)

async def _run_app(self, scope, receive, send, asgi_version):
# type: (Any, Any, Any, Any, int) -> Any
# type: (Any, Any, Any, int) -> Any
is_recursive_asgi_middleware = _asgi_middleware_applied.get(False)
is_lifespan = scope["type"] == "lifespan"
if is_recursive_asgi_middleware or is_lifespan:
Expand All @@ -169,7 +185,7 @@ async def _run_app(self, scope, receive, send, asgi_version):
return await self.app(scope, receive, send)

except Exception as exc:
_capture_exception(exc, mechanism_type=self.mechanism_type)
self._capture_lifespan_exception(exc)
raise exc from None

_asgi_middleware_applied.set(True)
Expand Down Expand Up @@ -255,7 +271,7 @@ async def _sentry_wrapped_send(event):
scope, receive, _sentry_wrapped_send
)
except Exception as exc:
_capture_exception(exc, mechanism_type=self.mechanism_type)
self._capture_request_exception(exc)
raise exc from None
finally:
_asgi_middleware_applied.set(False)
Expand Down
9 changes: 9 additions & 0 deletions sentry_sdk/integrations/litestar.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ def __init__(self, app, span_origin=LitestarIntegration.origin):
span_origin=span_origin,
)

def _capture_request_exception(self, exc):
# type: (Exception) -> None
"""Avoid catching exceptions from request handlers.

Those exceptions are already han in Litestar.after_exception handler.
We still catch exceptions from application lifespan handlers.
"""
pass


def patch_app_init():
# type: () -> None
Expand Down
35 changes: 34 additions & 1 deletion tests/integrations/litestar/test_litestar.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ async def __call__(self, scope, receive, send):


@parametrize_test_configurable_status_codes
def test_configurable_status_codes(
def test_configurable_status_codes_handler(
sentry_init,
capture_events,
failed_request_status_codes,
Expand All @@ -427,3 +427,36 @@ async def error() -> None:
client.get("/error")

assert len(events) == int(expected_error)


@parametrize_test_configurable_status_codes
def test_configurable_status_codes_middleware(
sentry_init,
capture_events,
failed_request_status_codes,
status_code,
expected_error,
):
integration_kwargs = (
{"failed_request_status_codes": failed_request_status_codes}
if failed_request_status_codes is not None
else {}
)
sentry_init(integrations=[LitestarIntegration(**integration_kwargs)])

events = capture_events()

def create_raising_middleware(app):
async def raising_middleware(scope, receive, send):
raise HTTPException(status_code=status_code)

return raising_middleware

@get("/error")
async def error() -> None: ...

app = Litestar([error], middleware=[create_raising_middleware])
client = TestClient(app)
client.get("/error")

assert len(events) == int(expected_error)
Loading