Skip to content

Commit 19200af

Browse files
authored
feat: Instrument ASGI under Django 3.0 (#573)
* feat: Instrument ASGI under Django 3.0 * fix: Clarify doc comment
1 parent 807abc6 commit 19200af

File tree

5 files changed

+73
-3
lines changed

5 files changed

+73
-3
lines changed

sentry_sdk/integrations/django/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ def sentry_patched_wsgi_handler(self, environ, start_response):
109109

110110
WSGIHandler.__call__ = sentry_patched_wsgi_handler
111111

112+
_patch_django_asgi_handler()
113+
112114
# patch get_response, because at that point we have the Django request
113115
# object
114116
from django.core.handlers.base import BaseHandler
@@ -314,6 +316,30 @@ def sentry_patched_asgi_handler(self, receive, send):
314316
AsgiHandler.__call__ = sentry_patched_asgi_handler
315317

316318

319+
def _patch_django_asgi_handler():
320+
# type: () -> None
321+
try:
322+
from django.core.handlers.asgi import ASGIHandler
323+
except ImportError:
324+
return
325+
326+
if not HAS_REAL_CONTEXTVARS:
327+
# We better have contextvars or we're going to leak state between
328+
# requests.
329+
#
330+
# We cannot hard-raise here because Django may not be used at all in
331+
# the current process.
332+
logger.warning(
333+
"We detected that you are using Django 3. To get proper "
334+
"instrumentation for ASGI requests, the Sentry SDK requires "
335+
"Python 3.7+ or the aiocontextvars package from PyPI."
336+
)
337+
338+
from sentry_sdk.integrations.django.asgi import patch_django_asgi_handler_impl
339+
340+
patch_django_asgi_handler_impl(ASGIHandler)
341+
342+
317343
def _make_event_processor(weak_request, integration):
318344
# type: (Callable[[], WSGIRequest], DjangoIntegration) -> EventProcessor
319345
def event_processor(event, hint):
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Instrumentation for Django 3.0
3+
4+
Since this file contains `async def` it is conditionally imported in
5+
`sentry_sdk.integrations.django` (depending on the existence of
6+
`django.core.handlers.asgi`.
7+
"""
8+
9+
from sentry_sdk import Hub
10+
from sentry_sdk._types import MYPY
11+
12+
from sentry_sdk.integrations.django import DjangoIntegration
13+
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
14+
15+
if MYPY:
16+
from typing import Any
17+
18+
19+
def patch_django_asgi_handler_impl(cls):
20+
# type: (Any) -> None
21+
old_app = cls.__call__
22+
23+
async def sentry_patched_asgi_handler(self, scope, receive, send):
24+
# type: (Any, Any, Any, Any) -> Any
25+
if Hub.current.get_integration(DjangoIntegration) is None:
26+
return await old_app(self, scope, receive, send)
27+
28+
middleware = SentryAsgiMiddleware(old_app.__get__(self, cls))._run_asgi3
29+
return await middleware(scope, receive, send)
30+
31+
cls.__call__ = sentry_patched_asgi_handler

tests/integrations/django/channels/test_channels.py renamed to tests/integrations/django/asgi/test_asgi.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
import pytest
22

3+
import django
34

45
from channels.testing import HttpCommunicator
56

67
from sentry_sdk import capture_message
78
from sentry_sdk.integrations.django import DjangoIntegration
89

9-
from tests.integrations.django.myapp.asgi import application
10+
from tests.integrations.django.myapp.asgi import channels_application
1011

12+
APPS = [channels_application]
13+
if django.VERSION >= (3, 0):
14+
from tests.integrations.django.myapp.asgi import asgi_application
1115

16+
APPS += [asgi_application]
17+
18+
19+
@pytest.mark.parametrize("application", APPS)
1220
@pytest.mark.asyncio
13-
async def test_basic(sentry_init, capture_events):
21+
async def test_basic(sentry_init, capture_events, application):
1422
sentry_init(integrations=[DjangoIntegration()], send_default_pii=True)
1523
events = capture_events()
1624

tests/integrations/django/myapp/asgi.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,9 @@
1212
)
1313

1414
django.setup()
15-
application = get_default_application()
15+
channels_application = get_default_application()
16+
17+
if django.VERSION >= (3, 0):
18+
from django.core.asgi import get_asgi_application
19+
20+
asgi_application = get_asgi_application()

0 commit comments

Comments
 (0)