Skip to content

Commit d0b70df

Browse files
authored
Let SentryAsgiMiddleware work with Starlette and FastAPI integrations (#1594)
People where complaining (rightly so) that just raising an error when SentryAsgiMiddleware and Starlette/Fastapi is used is not a nice thing to do. So we tried again to make this work together. To not break our users code. The plan was to make SentryASGIMiddleware no-op when there is already one there. Turns out this works already on Starlette but on FastAPI it broke. (This was because of how FastAPI deals with middlewares) We debugged the whole thing and it turns out that we where patching our own SentryAsgiMiddleware (like the FastAPI internal ones) to create spans when they are executed. This and the fact that we use __slots__ extensively made the integration break. We found out, that if we are not patching our own middleware this fixes the problem when initializing the middleware twice (once by our users and once by our auto-enabled FastAPI integration). Fixes #1592
1 parent 8853cfc commit d0b70df

File tree

4 files changed

+42
-29
lines changed

4 files changed

+42
-29
lines changed

sentry_sdk/integrations/asgi.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
An ASGI middleware.
33
4-
Based on Tom Christie's `sentry-asgi <https://github.com/encode/sentry-asgi>`_.
4+
Based on Tom Christie's `sentry-asgi <https://github.com/encode/sentry-asgi>`.
55
"""
66

77
import asyncio
@@ -23,6 +23,7 @@
2323
event_from_exception,
2424
HAS_REAL_CONTEXTVARS,
2525
CONTEXTVARS_ERROR_MESSAGE,
26+
logger,
2627
transaction_from_function,
2728
)
2829
from sentry_sdk.tracing import Transaction
@@ -104,20 +105,21 @@ def __init__(
104105
"Invalid value for transaction_style: %s (must be in %s)"
105106
% (transaction_style, TRANSACTION_STYLE_VALUES)
106107
)
107-
self.transaction_style = transaction_style
108-
self.mechanism_type = mechanism_type
109-
self.app = app
110108

111109
asgi_middleware_while_using_starlette_or_fastapi = (
112-
"starlette" in _get_installed_modules() and self.mechanism_type == "asgi"
110+
"starlette" in _get_installed_modules() and mechanism_type == "asgi"
113111
)
114112
if asgi_middleware_while_using_starlette_or_fastapi:
115-
raise RuntimeError(
113+
logger.warning(
116114
"The Sentry Python SDK can now automatically support ASGI frameworks like Starlette and FastAPI. "
117115
"Please remove 'SentryAsgiMiddleware' from your project. "
118116
"See https://docs.sentry.io/platforms/python/guides/asgi/ for more information."
119117
)
120118

119+
self.transaction_style = transaction_style
120+
self.mechanism_type = mechanism_type
121+
self.app = app
122+
121123
if _looks_like_asgi3(app):
122124
self.__call__ = self._run_asgi3 # type: Callable[..., Any]
123125
else:
@@ -138,7 +140,6 @@ async def _run_asgi3(self, scope, receive, send):
138140
async def _run_app(self, scope, callback):
139141
# type: (Any, Any) -> Any
140142
is_recursive_asgi_middleware = _asgi_middleware_applied.get(False)
141-
142143
if is_recursive_asgi_middleware:
143144
try:
144145
return await callback()

sentry_sdk/integrations/starlette.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ def patch_middlewares():
257257

258258
def _sentry_middleware_init(self, cls, **options):
259259
# type: (Any, Any, Any) -> None
260+
if cls == SentryAsgiMiddleware:
261+
return old_middleware_init(self, cls, **options)
262+
260263
span_enabled_cls = _enable_span_for_middleware(cls)
261264
old_middleware_init(self, span_enabled_cls, **options)
262265

@@ -285,6 +288,7 @@ async def _sentry_patched_asgi_app(self, scope, receive, send):
285288
lambda *a, **kw: old_app(self, *a, **kw),
286289
mechanism_type=StarletteIntegration.identifier,
287290
)
291+
288292
middleware.__call__ = middleware._run_asgi3
289293
return await middleware(scope, receive, send)
290294

tests/integrations/fastapi/test_fastapi.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,21 @@ def test_transaction_style(
117117
assert "transaction" not in event
118118

119119

120-
def test_legacy_setup(sentry_init):
121-
# Check for error message if the user
122-
# updates and the integrations are auto enabled
123-
# and the SentryAsgiMiddleware is still there
120+
def test_legacy_setup(
121+
sentry_init,
122+
capture_events,
123+
):
124+
# Check that behaviour does not change
125+
# if the user just adds the new Integrations
126+
# and forgets to remove SentryAsgiMiddleware
124127
sentry_init()
128+
app = fastapi_app_factory()
129+
asgi_app = SentryAsgiMiddleware(app)
125130

126-
with pytest.raises(RuntimeError) as exc:
127-
app = fastapi_app_factory()
128-
app = SentryAsgiMiddleware(app)
131+
events = capture_events()
129132

130-
assert (
131-
"The Sentry Python SDK can now automatically support ASGI frameworks like Starlette and FastAPI."
132-
in str(exc)
133-
)
133+
client = TestClient(asgi_app)
134+
client.get("/message/123456")
135+
136+
(event,) = events
137+
assert event["transaction"] == "/message/{message_id}"

tests/integrations/starlette/test_starlette.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -543,17 +543,21 @@ def test_middleware_spans(sentry_init, capture_events):
543543
idx += 1
544544

545545

546-
def test_legacy_setup(sentry_init):
547-
# Check for error message if the user
548-
# updates and the integration is auto enabled
549-
# and the SentryAsgiMiddleware is still there
546+
def test_legacy_setup(
547+
sentry_init,
548+
capture_events,
549+
):
550+
# Check that behaviour does not change
551+
# if the user just adds the new Integration
552+
# and forgets to remove SentryAsgiMiddleware
550553
sentry_init()
554+
app = starlette_app_factory()
555+
asgi_app = SentryAsgiMiddleware(app)
551556

552-
with pytest.raises(RuntimeError) as exc:
553-
app = starlette_app_factory()
554-
app = SentryAsgiMiddleware(app)
557+
events = capture_events()
555558

556-
assert (
557-
"The Sentry Python SDK can now automatically support ASGI frameworks like Starlette and FastAPI."
558-
in str(exc)
559-
)
559+
client = TestClient(asgi_app)
560+
client.get("/message/123456")
561+
562+
(event,) = events
563+
assert event["transaction"] == "/message/{message_id}"

0 commit comments

Comments
 (0)