Skip to content

Commit d9e3843

Browse files
authored
Fix FastAPI issues (#1532) ( #1514)
* Fixed patching of middlewares to fix the 'coroutine' error for non existent routes. * Only capture server errors * Fixed form POST in FastApiIntegration. * Fixed form uploads on starlette projects * Fixed error while handling 404 errors. * Fix error during handling of form validation error. * Find the correct handler (for classes with parent classes * Do not call starlette integration, because it needs to be set in the init()
1 parent 67144c9 commit d9e3843

File tree

2 files changed

+213
-140
lines changed

2 files changed

+213
-140
lines changed

sentry_sdk/integrations/fastapi.py

Lines changed: 47 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from sentry_sdk._types import MYPY
2-
from sentry_sdk.hub import Hub
2+
from sentry_sdk.hub import Hub, _should_send_default_pii
33
from sentry_sdk.integrations import DidNotEnable
44
from sentry_sdk.integrations.starlette import (
5-
SentryStarletteMiddleware,
65
StarletteIntegration,
6+
StarletteRequestExtractor,
77
)
88
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TRANSACTION_SOURCE_ROUTE
99
from sentry_sdk.utils import transaction_from_function
@@ -14,16 +14,10 @@
1414
from sentry_sdk._types import Event
1515

1616
try:
17-
from fastapi import FastAPI # type: ignore
18-
from fastapi import Request
17+
import fastapi # type: ignore
1918
except ImportError:
2019
raise DidNotEnable("FastAPI is not installed")
2120

22-
try:
23-
from starlette.types import ASGIApp, Receive, Scope, Send # type: ignore
24-
except ImportError:
25-
raise DidNotEnable("Starlette is not installed")
26-
2721

2822
_DEFAULT_TRANSACTION_NAME = "generic FastAPI request"
2923

@@ -34,27 +28,7 @@ class FastApiIntegration(StarletteIntegration):
3428
@staticmethod
3529
def setup_once():
3630
# type: () -> None
37-
StarletteIntegration.setup_once()
38-
patch_middlewares()
39-
40-
41-
def patch_middlewares():
42-
# type: () -> None
43-
44-
old_build_middleware_stack = FastAPI.build_middleware_stack
45-
46-
def _sentry_build_middleware_stack(self):
47-
# type: (FastAPI) -> Callable[..., Any]
48-
"""
49-
Adds `SentryStarletteMiddleware` and `SentryFastApiMiddleware` to the
50-
middleware stack of the FastAPI application.
51-
"""
52-
app = old_build_middleware_stack(self)
53-
app = SentryStarletteMiddleware(app=app)
54-
app = SentryFastApiMiddleware(app=app)
55-
return app
56-
57-
FastAPI.build_middleware_stack = _sentry_build_middleware_stack
31+
patch_get_request_handler()
5832

5933

6034
def _set_transaction_name_and_source(event, transaction_style, request):
@@ -82,42 +56,55 @@ def _set_transaction_name_and_source(event, transaction_style, request):
8256
event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]}
8357

8458

85-
class SentryFastApiMiddleware:
86-
def __init__(self, app, dispatch=None):
87-
# type: (ASGIApp, Any) -> None
88-
self.app = app
59+
def patch_get_request_handler():
60+
# type: () -> None
61+
old_get_request_handler = fastapi.routing.get_request_handler
62+
63+
def _sentry_get_request_handler(*args, **kwargs):
64+
# type: (*Any, **Any) -> Any
65+
old_app = old_get_request_handler(*args, **kwargs)
66+
67+
async def _sentry_app(*args, **kwargs):
68+
# type: (*Any, **Any) -> Any
69+
hub = Hub.current
70+
integration = hub.get_integration(FastApiIntegration)
71+
if integration is None:
72+
return await old_app(*args, **kwargs)
73+
74+
with hub.configure_scope() as sentry_scope:
75+
request = args[0]
76+
extractor = StarletteRequestExtractor(request)
77+
info = await extractor.extract_request_info()
8978

90-
async def __call__(self, scope, receive, send):
91-
# type: (Scope, Receive, Send) -> Any
92-
if scope["type"] != "http":
93-
await self.app(scope, receive, send)
94-
return
79+
def _make_request_event_processor(req, integration):
80+
# type: (Any, Any) -> Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]]
81+
def event_processor(event, hint):
82+
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
9583

96-
hub = Hub.current
97-
integration = hub.get_integration(FastApiIntegration)
98-
if integration is None:
99-
await self.app(scope, receive, send)
100-
return
84+
# Extract information from request
85+
request_info = event.get("request", {})
86+
if info:
87+
if "cookies" in info and _should_send_default_pii():
88+
request_info["cookies"] = info["cookies"]
89+
if "data" in info:
90+
request_info["data"] = info["data"]
91+
event["request"] = request_info
10192

102-
with hub.configure_scope() as sentry_scope:
103-
request = Request(scope, receive=receive, send=send)
93+
_set_transaction_name_and_source(
94+
event, integration.transaction_style, req
95+
)
10496

105-
def _make_request_event_processor(req, integration):
106-
# type: (Any, Any) -> Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]]
107-
def event_processor(event, hint):
108-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
97+
return event
10998

110-
_set_transaction_name_and_source(
111-
event, integration.transaction_style, req
112-
)
99+
return event_processor
113100

114-
return event
101+
sentry_scope._name = FastApiIntegration.identifier
102+
sentry_scope.add_event_processor(
103+
_make_request_event_processor(request, integration)
104+
)
115105

116-
return event_processor
106+
return await old_app(*args, **kwargs)
117107

118-
sentry_scope._name = FastApiIntegration.identifier
119-
sentry_scope.add_event_processor(
120-
_make_request_event_processor(request, integration)
121-
)
108+
return _sentry_app
122109

123-
await self.app(scope, receive, send)
110+
fastapi.routing.get_request_handler = _sentry_get_request_handler

0 commit comments

Comments
 (0)