-
Notifications
You must be signed in to change notification settings - Fork 549
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
base: master
Are you sure you want to change the base?
Changes from 8 commits
0b64a40
4f1963c
169177f
3950e2a
e1cd18e
2b91e0d
6ac821b
0c2da8f
204c8a7
bace5ce
fb1274f
babfe27
33b11cc
41b5fd5
3819888
5b27acd
53baddd
c1c318d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,17 +57,6 @@ | |
TRANSACTION_STYLE_VALUES = ("endpoint", "url") | ||
|
||
|
||
def _capture_exception(exc, mechanism_type="asgi"): | ||
# type: (Any, str) -> None | ||
|
||
event, hint = event_from_exception( | ||
exc, | ||
client_options=sentry_sdk.get_client().options, | ||
mechanism={"type": mechanism_type, "handled": False}, | ||
) | ||
sentry_sdk.capture_event(event, hint=hint) | ||
|
||
|
||
def _looks_like_asgi3(app): | ||
# type: (Any) -> bool | ||
""" | ||
|
@@ -145,6 +134,23 @@ def __init__( | |
else: | ||
self.__call__ = self._run_asgi2 | ||
|
||
def _capture_exception(self, exc): | ||
# type: (Exception) -> None | ||
event, hint = event_from_exception( | ||
exc, | ||
client_options=sentry_sdk.get_client().options, | ||
mechanism={"type": self.mechanism_type, "handled": False}, | ||
) | ||
sentry_sdk.capture_event(event, hint=hint) | ||
|
||
def _capture_lifespan_exception(self, exc): | ||
# type: (Exception) -> None | ||
return self._capture_exception(exc) | ||
|
||
def _capture_request_exception(self, exc): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit unsure of the difference between these two functions. Please add docstrings to both to clarify the distinction There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
# type: (Exception) -> None | ||
return self._capture_exception(exc) | ||
|
||
def _run_asgi2(self, scope): | ||
# type: (Any) -> Any | ||
async def inner(receive, send): | ||
|
@@ -158,7 +164,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 | ||
vrslev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
is_recursive_asgi_middleware = _asgi_middleware_applied.get(False) | ||
is_lifespan = scope["type"] == "lifespan" | ||
if is_recursive_asgi_middleware or is_lifespan: | ||
|
@@ -169,7 +175,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) | ||
|
@@ -255,7 +261,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) | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -87,6 +87,10 @@ def __init__(self, app, span_origin=LitestarIntegration.origin): | |||||||||||||||
span_origin=span_origin, | ||||||||||||||||
) | ||||||||||||||||
|
||||||||||||||||
def _capture_request_exception(self, exc): | ||||||||||||||||
# type: (Exception) -> None | ||||||||||||||||
"""Ignore exceptions for requests here: we catch them in Litestar.after_exception handler.""" | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am slightly unsure why this function is needed/how it relates to what you are trying to explain in this PR. Could you please explain in a bit more detail? Also, I think you need a
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, Litestar integration catches exceptions raised in middleware twice:
The custom ASGI handler doesn't filter exceptions using Interestingly, this behavior doesn't apply to exceptions raised in request handlers because Litestar prepends the built-in exception handling middleware. This leads us to the conclusion that the ASGI handler only catches exceptions raised in middleware or lifespan handlers. It's still valuable to catch exceptions from lifespan handlers. In this case, we patch |
||||||||||||||||
|
||||||||||||||||
|
||||||||||||||||
def patch_app_init(): | ||||||||||||||||
# type: () -> None | ||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to keep this function the same, as a standalone function instead of a method. I don't think it has to be modified to implement your changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!