Django catches view exceptions internally to render its 500 error page, so they never reach Python's default exception hook — a generic monitoring SDK will see nothing. Django does fire a got_request_exception signal every time this happens, and that's the hook to connect to. With Ravn, this is ravn.init() plus one call to install() from ravn.integrations.django, wired up once in AppConfig.ready().
If you've added a Python error monitoring SDK to a Django project and it reported zero errors despite users hitting 500 pages, this is why: Django doesn't let unhandled view exceptions propagate to sys.excepthook. It catches them itself, logs them through its own logging config, and renders an error page. Any tool that only hooks the global exception handler is watching a door that Django never uses.
Why doesn't Django report exceptions the normal way?
When a view raises an exception, Django's request/response middleware chain catches it before it can bubble up to the interpreter. That's necessary — it's how Django can show you a debug page (or a clean 500) instead of crashing the whole worker process. But it also means the exception's journey ends inside Django, invisible to anything listening at the process level.
What Django does expose is a signal: django.core.signals.got_request_exception. It's sent every time this catch happens, with the exception still live on the current thread via sys.exc_info(). That's the correct integration point.
How do you capture Django view exceptions with Ravn?
Two calls, once at startup:
import ravn
from ravn.integrations.django import install
ravn.init(api_key="your_api_key")
install()
# Every unhandled exception in any view is now captured,
# with the request path and method attached automatically.
The best place to run this is your app's AppConfig.ready() method, so it runs exactly once after Django has finished configuring itself:
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
import ravn
from ravn.integrations.django import install
ravn.init(api_key="your_api_key")
install()
wsgi.py or asgi.py works too, as long as it executes once before the app starts serving requests.
Why does the signal handler need weak=False?
This is the detail that silently breaks most hand-rolled integrations. Django's signal dispatcher holds only a weak reference to connected receivers by default — it doesn't keep them alive on its own. If your handler is a local closure with nothing else referencing it, Python's garbage collector reclaims it almost immediately, and the signal connection dies with no error, no warning, nothing in your logs. Your app looks instrumented. It isn't.
Ravn's install() passes weak=False when connecting, so the handler holds a strong reference for the lifetime of the process:
got_request_exception.connect(
_handle, dispatch_uid="ravn-got-request-exception", weak=False
)
If you're writing your own signal-based integration for any tool, this is the one line that decides whether it works in production or just in your first manual test.
Does this work with Django REST Framework?
Yes, without any extra code. DRF views still run through Django's request cycle, so an unhandled exception in a DRF viewset triggers got_request_exception the same way a plain Django view does. The path and method attached to each captured error will reflect the DRF endpoint that raised it.
What metadata gets attached automatically?
Each captured exception includes the request path and HTTP method, so you can see which endpoint is failing without digging through a stack trace first:
metadata={
"mechanism": "django",
"path": request.path,
"method": request.method,
}
For a broader look at the setup philosophy and pricing, see the Sentry alternative comparison and the general Python error monitoring guide.
Add error monitoring to your Django app in two lines.
Automatic exception capture, AI root-cause analysis, free to start.
Get Started FreeFrequently asked questions
Does Django report unhandled exceptions automatically?
No. Django catches view exceptions internally to render a 500 page, so they never reach sys.excepthook. A tool that only hooks the global exception handler sees nothing from a Django app, even while it's actively erroring.
How do I capture Django view exceptions?
Connect to Django's got_request_exception signal, which fires every time Django catches a view exception. With Ravn this is one call: install() from ravn.integrations.django, after ravn.init().
Where should I call ravn.init() and install()?
In your AppConfig.ready() method, or at the top of wsgi.py / asgi.py. Either works as long as it runs once at startup, before requests are served.
Why does the exception handler need weak=False?
Django's signal dispatcher holds only a weak reference to receivers by default. A local closure with no other reference gets garbage-collected right after connecting, silently breaking the signal. weak=False forces a strong reference so it stays connected.
Does this work with Django REST Framework?
Yes. DRF views run through Django's request cycle and still trigger got_request_exception, so the same integration captures them without any DRF-specific code.