Skip to content

Commit a57a6c2

Browse files
committed
implement instrumentaton for async views
1 parent 3cc58fc commit a57a6c2

File tree

5 files changed

+69
-13
lines changed

5 files changed

+69
-13
lines changed

sentry_sdk/integrations/django/asgi.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
`django.core.handlers.asgi`.
77
"""
88

9-
from sentry_sdk import Hub
9+
from sentry_sdk import Hub, _functools
1010
from sentry_sdk._types import MYPY
1111

12-
from sentry_sdk.integrations.django import DjangoIntegration
1312
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
1413

1514
if MYPY:
@@ -21,6 +20,9 @@
2120

2221
def patch_django_asgi_handler_impl(cls):
2322
# type: (Any) -> None
23+
24+
from sentry_sdk.integrations.django import DjangoIntegration
25+
2426
old_app = cls.__call__
2527

2628
async def sentry_patched_asgi_handler(self, scope, receive, send):
@@ -50,6 +52,9 @@ async def sentry_patched_get_response_async(self, request):
5052

5153
def patch_channels_asgi_handler_impl(cls):
5254
# type: (Any) -> None
55+
56+
from sentry_sdk.integrations.django import DjangoIntegration
57+
5358
old_app = cls.__call__
5459

5560
async def sentry_patched_asgi_handler(self, receive, send):
@@ -64,3 +69,16 @@ async def sentry_patched_asgi_handler(self, receive, send):
6469
return await middleware(self.scope)(receive, send)
6570

6671
cls.__call__ = sentry_patched_asgi_handler
72+
73+
74+
def wrap_async_view(hub, callback):
75+
@_functools.wraps(callback)
76+
async def sentry_wrapped_callback(request, *args, **kwargs):
77+
# type: (Any, *Any, **Any) -> Any
78+
79+
with hub.start_span(
80+
op="django.view", description=request.resolver_match.view_name
81+
):
82+
return await callback(request, *args, **kwargs)
83+
84+
return sentry_wrapped_callback

sentry_sdk/integrations/django/views.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66
from typing import Any
77

88

9+
try:
10+
from asyncio import iscoroutinefunction
11+
except ImportError:
12+
iscoroutinefunction = None
13+
14+
15+
try:
16+
from sentry_sdk.integrations.django.asgi import wrap_async_view
17+
except (ImportError, SyntaxError):
18+
wrap_async_view = None
19+
20+
921
def patch_views():
1022
# type: () -> None
1123

@@ -27,17 +39,27 @@ def sentry_patched_make_view_atomic(self, *args, **kwargs):
2739

2840
if integration is not None and integration.middleware_spans:
2941

30-
@_functools.wraps(callback)
31-
def sentry_wrapped_callback(request, *args, **kwargs):
32-
# type: (Any, *Any, **Any) -> Any
33-
with hub.start_span(
34-
op="django.view", description=request.resolver_match.view_name
35-
):
36-
return callback(request, *args, **kwargs)
42+
if iscoroutinefunction is not None and wrap_async_view is not None and iscoroutinefunction(callback):
43+
sentry_wrapped_callback = wrap_async_view(hub, callback)
44+
else:
45+
print((iscoroutinefunction, wrap_async_view, iscoroutinefunction(callback)))
46+
sentry_wrapped_callback = _wrap_sync_view(hub, callback)
3747

3848
else:
3949
sentry_wrapped_callback = callback
4050

4151
return sentry_wrapped_callback
4252

4353
BaseHandler.make_view_atomic = sentry_patched_make_view_atomic
54+
55+
56+
def _wrap_sync_view(hub, callback):
57+
@_functools.wraps(callback)
58+
def sentry_wrapped_callback(request, *args, **kwargs):
59+
# type: (Any, *Any, **Any) -> Any
60+
with hub.start_span(
61+
op="django.view", description=request.resolver_match.view_name
62+
):
63+
return callback(request, *args, **kwargs)
64+
65+
return sentry_wrapped_callback

tests/integrations/django/asgi/test_asgi.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ async def test_async_views(sentry_init, capture_events, application):
5151

5252
events = capture_events()
5353

54-
comm = HttpCommunicator(application, "GET", "/async_ok")
54+
comm = HttpCommunicator(application, "GET", "/async_message")
5555
response = await comm.get_response()
5656
assert response["status"] == 200
57+
58+
event, = events
59+
60+
assert event["transaction"] == "/async_message"
61+
assert event["request"] == {
62+
"cookies": {},
63+
"headers": {},
64+
"method": "GET",
65+
"query_string": None,
66+
"url": "/async_message",
67+
}

tests/integrations/django/myapp/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def path(path, *args, **kwargs):
5555
views.csrf_hello_not_exempt,
5656
name="csrf_hello_not_exempt",
5757
),
58-
path("async_ok", views.async_ok, name="async_ok"),
58+
path("async_message", views.async_message, name="async_message"),
5959
]
6060

6161

tests/integrations/django/myapp/views.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,10 @@ def csrf_hello_not_exempt(*args, **kwargs):
122122
return HttpResponse("ok")
123123

124124

125-
async def async_ok(request):
126-
return HttpResponse("ok")
125+
try:
126+
exec("""async def async_message(request):
127+
sentry_sdk.capture_message("hi")
128+
return HttpResponse("ok")""")
129+
130+
except SyntaxError:
131+
async_message = None

0 commit comments

Comments
 (0)