From bf7b6767d88804ad0a7609d06fdf120f8c692a73 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 21 Oct 2022 12:51:00 +0200 Subject: [PATCH 1/3] Make sure receive/send can also be functools.partial objects (having no __name__) --- sentry_sdk/integrations/starlette.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index aaf7fb3dc4..f635367f95 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -108,7 +108,8 @@ async def _sentry_receive(*args, **kwargs): span.set_tag("starlette.middleware_name", middleware_name) await receive(*args, **kwargs) - receive_patched = receive.__name__ == "_sentry_receive" + receive_name = getattr(receive, "__name__", str(receive)) + receive_patched = receive_name == "_sentry_receive" new_receive = _sentry_receive if not receive_patched else receive # Creating spans for the "send" callback @@ -121,7 +122,8 @@ async def _sentry_send(*args, **kwargs): span.set_tag("starlette.middleware_name", middleware_name) await send(*args, **kwargs) - send_patched = send.__name__ == "_sentry_send" + send_name = getattr(send, "__name__", str(send)) + send_patched = send_name == "_sentry_send" new_send = _sentry_send if not send_patched else send await old_call(app, scope, new_receive, new_send, **kwargs) From 6ce42e45f3ca9051c0507061a51c9b63a98c833b Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 21 Oct 2022 15:19:56 +0200 Subject: [PATCH 2/3] Added test --- sentry_sdk/integrations/starlette.py | 5 +- .../integrations/starlette/test_starlette.py | 97 ++++++++++++++++++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index dc98981ddf..323ac64210 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -103,7 +103,7 @@ async def _sentry_receive(*args, **kwargs): hub = Hub.current with hub.start_span( op=OP.MIDDLEWARE_STARLETTE_RECEIVE, - description=receive.__qualname__, + description=getattr(receive, "__qualname__", str(receive)), ) as span: span.set_tag("starlette.middleware_name", middleware_name) return await receive(*args, **kwargs) @@ -117,7 +117,8 @@ async def _sentry_send(*args, **kwargs): # type: (*Any, **Any) -> Any hub = Hub.current with hub.start_span( - op=OP.MIDDLEWARE_STARLETTE_SEND, description=send.__qualname__ + op=OP.MIDDLEWARE_STARLETTE_SEND, + description=getattr(send, "__qualname__", str(send)), ) as span: span.set_tag("starlette.middleware_name", middleware_name) return await send(*args, **kwargs) diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index 713505c61d..889db4e3ad 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -1,5 +1,6 @@ import asyncio import base64 +import functools import json import os @@ -189,6 +190,30 @@ async def __call__(self, scope, receive, send): await self.app(scope, receive, send) +class SamplePartialReceiveSendMiddleware: + def __init__(self, app): + self.app = app + + async def __call__(self, scope, receive, send): + message = await receive() + assert message + assert message["type"] == "http.request" + + send_output = await send({"type": "something-unimportant"}) + assert send_output is None + + async def my_receive(*args, **kwargs): + pass + + async def my_send(*args, **kwargs): + pass + + partial_receive = functools.partial(my_receive) + partial_send = functools.partial(my_send) + + await self.app(scope, partial_receive, partial_send) + + @pytest.mark.asyncio async def test_starlettrequestextractor_content_length(sentry_init): with mock.patch( @@ -659,8 +684,7 @@ def test_middleware_callback_spans(sentry_init, capture_events): idx += 1 -@pytest.mark.asyncio -async def test_middleware_receive_send(sentry_init, capture_events): +def test_middleware_receive_send(sentry_init, capture_events): sentry_init( traces_sample_rate=1.0, integrations=[StarletteIntegration()], @@ -678,6 +702,75 @@ async def test_middleware_receive_send(sentry_init, capture_events): pass +def test_middleware_partial_receive_send(sentry_init, capture_events): + sentry_init( + traces_sample_rate=1.0, + integrations=[StarletteIntegration()], + ) + starlette_app = starlette_app_factory( + middleware=[Middleware(SamplePartialReceiveSendMiddleware)] + ) + events = capture_events() + + client = TestClient(starlette_app, raise_server_exceptions=False) + try: + client.get("/message", auth=("Gabriela", "hello123")) + except Exception: + pass + + (_, transaction_event) = events + + expected = [ + { + "op": "middleware.starlette", + "description": "ServerErrorMiddleware", + "tags": {"starlette.middleware_name": "ServerErrorMiddleware"}, + }, + { + "op": "middleware.starlette", + "description": "SamplePartialReceiveSendMiddleware", + "tags": {"starlette.middleware_name": "SamplePartialReceiveSendMiddleware"}, + }, + { + "op": "middleware.starlette.receive", + "description": "_ASGIAdapter.send..receive", + "tags": {"starlette.middleware_name": "ServerErrorMiddleware"}, + }, + { + "op": "middleware.starlette.send", + "description": "ServerErrorMiddleware.__call__.._send", + "tags": {"starlette.middleware_name": "SamplePartialReceiveSendMiddleware"}, + }, + { + "op": "middleware.starlette.send", + "description": "_ASGIAdapter.send..send", + "tags": {"starlette.middleware_name": "ServerErrorMiddleware"}, + }, + { + "op": "middleware.starlette", + "description": "ExceptionMiddleware", + "tags": {"starlette.middleware_name": "ExceptionMiddleware"}, + }, + { + "op": "middleware.starlette.send", + "description": "functools.partial(.my_send at ", + "tags": {"starlette.middleware_name": "ExceptionMiddleware"}, + }, + { + "op": "middleware.starlette.send", + "description": "functools.partial(.my_send at ", + "tags": {"starlette.middleware_name": "ExceptionMiddleware"}, + }, + ] + + idx = 0 + for span in transaction_event["spans"]: + assert span["op"] == expected[idx]["op"] + assert span["description"].startswith(expected[idx]["description"]) + assert span["tags"] == expected[idx]["tags"] + idx += 1 + + def test_last_event_id(sentry_init, capture_events): sentry_init( integrations=[StarletteIntegration()], From c1d07e25fc5b980cc2aa38750e16396c85abc200 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 21 Oct 2022 15:34:25 +0200 Subject: [PATCH 3/3] Fixed tests --- tests/integrations/starlette/test_starlette.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index 889db4e3ad..cc3b38edf5 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -733,7 +733,9 @@ def test_middleware_partial_receive_send(sentry_init, capture_events): }, { "op": "middleware.starlette.receive", - "description": "_ASGIAdapter.send..receive", + "description": "_ASGIAdapter.send..receive" + if STARLETTE_VERSION < (0, 21) + else "_TestClientTransport.handle_request..receive", "tags": {"starlette.middleware_name": "ServerErrorMiddleware"}, }, { @@ -743,7 +745,9 @@ def test_middleware_partial_receive_send(sentry_init, capture_events): }, { "op": "middleware.starlette.send", - "description": "_ASGIAdapter.send..send", + "description": "_ASGIAdapter.send..send" + if STARLETTE_VERSION < (0, 21) + else "_TestClientTransport.handle_request..send", "tags": {"starlette.middleware_name": "ServerErrorMiddleware"}, }, {