332 lines
9.9 KiB
ReStructuredText
332 lines
9.9 KiB
ReStructuredText
Django
|
|
======
|
|
|
|
.. default-domain:: py
|
|
|
|
`Django <http://djangoproject.com/>`_ is one of (if not the) Python's most
|
|
popular web frameworks. Support is built into Raven but needs some
|
|
configuration. While older versions of Django will likely work,
|
|
officially only version 1.4 and newer are supported.
|
|
|
|
Setup
|
|
-----
|
|
|
|
Using the Django integration is as simple as adding
|
|
:mod:`raven.contrib.django.raven_compat` to your installed apps::
|
|
|
|
INSTALLED_APPS = (
|
|
'raven.contrib.django.raven_compat',
|
|
)
|
|
|
|
.. note:: This causes Raven to install a hook in Django that will
|
|
automatically report uncaught exceptions.
|
|
|
|
Additional settings for the client are configured using the
|
|
``RAVEN_CONFIG`` dictionary::
|
|
|
|
import raven
|
|
|
|
RAVEN_CONFIG = {
|
|
'dsn': '___DSN___',
|
|
# If you are using git, you can also automatically configure the
|
|
# release based on the git info.
|
|
'release': raven.fetch_git_sha(os.path.dirname(__file__)),
|
|
}
|
|
|
|
Once you've configured the client, you can test it using the standard Django
|
|
management interface::
|
|
|
|
python manage.py raven test
|
|
|
|
You'll be referencing the client slightly differently in Django as well::
|
|
|
|
from raven.contrib.django.raven_compat.models import client
|
|
|
|
client.captureException()
|
|
|
|
|
|
Using with Raven.js
|
|
-------------------
|
|
|
|
A Django template tag is provided to render a proper public DSN inside
|
|
your templates, you must first load ``raven``:
|
|
|
|
.. sourcecode:: django
|
|
|
|
{% load raven %}
|
|
|
|
Inside your template, you can now use:
|
|
|
|
.. sourcecode:: html+django
|
|
|
|
<script>Raven.config('{% sentry_public_dsn %}').install()</script>
|
|
|
|
By default, the DSN is generated in a protocol relative fashion, e.g.
|
|
``//public@example.com/1``. If you need a specific protocol, you can
|
|
override:
|
|
|
|
.. sourcecode:: html+django
|
|
|
|
{% sentry_public_dsn 'https' %}
|
|
|
|
.. sentry:edition:: hosted, on-premise
|
|
|
|
See the :doc:`Raven.js documentation <../../../clients/javascript/index>`
|
|
for more information.
|
|
|
|
|
|
Integration with :mod:`logging`
|
|
-------------------------------
|
|
|
|
To integrate with the standard library's :mod:`logging` module the
|
|
following config can be used::
|
|
|
|
LOGGING = {
|
|
'version': 1,
|
|
'disable_existing_loggers': True,
|
|
'root': {
|
|
'level': 'WARNING',
|
|
'handlers': ['sentry'],
|
|
},
|
|
'formatters': {
|
|
'verbose': {
|
|
'format': '%(levelname)s %(asctime)s %(module)s '
|
|
'%(process)d %(thread)d %(message)s'
|
|
},
|
|
},
|
|
'handlers': {
|
|
'sentry': {
|
|
'level': 'ERROR',
|
|
'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler',
|
|
},
|
|
'console': {
|
|
'level': 'DEBUG',
|
|
'class': 'logging.StreamHandler',
|
|
'formatter': 'verbose'
|
|
}
|
|
},
|
|
'loggers': {
|
|
'django.db.backends': {
|
|
'level': 'ERROR',
|
|
'handlers': ['console'],
|
|
'propagate': False,
|
|
},
|
|
'raven': {
|
|
'level': 'DEBUG',
|
|
'handlers': ['console'],
|
|
'propagate': False,
|
|
},
|
|
'sentry.errors': {
|
|
'level': 'DEBUG',
|
|
'handlers': ['console'],
|
|
'propagate': False,
|
|
},
|
|
},
|
|
}
|
|
|
|
Usage
|
|
~~~~~
|
|
|
|
Logging usage works the same way as it does outside of Django, with the
|
|
addition of an optional ``request`` key in the extra data::
|
|
|
|
logger.error('There was some crazy error', exc_info=True, extra={
|
|
# Optionally pass a request and we'll grab any information we can
|
|
'request': request,
|
|
})
|
|
|
|
|
|
404 Logging
|
|
-----------
|
|
|
|
In certain conditions you may wish to log 404 events to the Sentry server. To
|
|
do this, you simply need to enable a Django middleware::
|
|
|
|
MIDDLEWARE_CLASSES = (
|
|
'raven.contrib.django.raven_compat.middleware.Sentry404CatchMiddleware',
|
|
...,
|
|
) + MIDDLEWARE_CLASSES
|
|
|
|
It is recommended to put the middleware at the top, so that any only 404s
|
|
that bubbled all the way up get logged. Certain middlewares (e.g. flatpages)
|
|
capture 404s and replace the response.
|
|
|
|
Message References
|
|
------------------
|
|
|
|
Sentry supports sending a message ID to your clients so that they can be
|
|
tracked easily by your development team. There are two ways to access this
|
|
information, the first is via the ``X-Sentry-ID`` HTTP response header.
|
|
Adding this is as simple as appending a middleware to your stack::
|
|
|
|
MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + (
|
|
# We recommend putting this as high in the chain as possible
|
|
'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware',
|
|
...,
|
|
)
|
|
|
|
Another alternative method is rendering it within a template. By default,
|
|
Sentry will attach :attr:`request.sentry` when it catches a Django
|
|
exception. In our example, we will use this information to modify the
|
|
default :file:`500.html` which is rendered, and show the user a case
|
|
reference ID. The first step in doing this is creating a custom
|
|
:func:`handler500` in your :file:`urls.py` file::
|
|
|
|
from django.conf.urls.defaults import *
|
|
|
|
from django.views.defaults import page_not_found, server_error
|
|
from django.template import Context, loader
|
|
from django.http import HttpResponseServerError
|
|
|
|
def handler500(request):
|
|
"""500 error handler which includes ``request`` in the context.
|
|
|
|
Templates: `500.html`
|
|
Context: None
|
|
"""
|
|
|
|
t = loader.get_template('500.html') # You need to create a 500.html template.
|
|
return HttpResponseServerError(t.render(Context({
|
|
'request': request,
|
|
})))
|
|
|
|
Once we've successfully added the :data:`request` context variable, adding the
|
|
Sentry reference ID to our :file:`500.html` is simple:
|
|
|
|
.. sourcecode:: html+django
|
|
|
|
<p>You've encountered an error, oh noes!</p>
|
|
{% if request.sentry.id %}
|
|
<p>If you need assistance, you may reference this error as
|
|
<strong>{{ request.sentry.id }}</strong>.</p>
|
|
{% endif %}
|
|
|
|
WSGI Middleware
|
|
---------------
|
|
|
|
If you are using a WSGI interface to serve your app, you can also apply a
|
|
middleware which will ensure that you catch errors even at the fundamental
|
|
level of your Django application::
|
|
|
|
from raven.contrib.django.raven_compat.middleware.wsgi import Sentry
|
|
from django.core.handlers.wsgi import WSGIHandler
|
|
|
|
application = Sentry(WSGIHandler())
|
|
|
|
|
|
Additional Settings
|
|
-------------------
|
|
|
|
.. describe:: SENTRY_CLIENT
|
|
|
|
In some situations you may wish for a slightly different behavior to
|
|
how Sentry communicates with your server. For this, Raven allows you
|
|
to specify a custom client::
|
|
|
|
SENTRY_CLIENT = 'raven.contrib.django.raven_compat.DjangoClient'
|
|
|
|
.. describe:: SENTRY_CELERY_LOGLEVEL
|
|
|
|
If you are also using Celery, there is a handler being automatically
|
|
registered for you that captures the errors from workers. The default
|
|
logging level for that handler is ``logging.ERROR`` and can be
|
|
customized using this setting::
|
|
|
|
SENTRY_CELERY_LOGLEVEL = logging.INFO
|
|
RAVEN_CONFIG = {
|
|
'CELERY_LOGLEVEL': logging.INFO
|
|
}
|
|
|
|
Caveats
|
|
-------
|
|
|
|
The following things you should keep in mind when using Raven with Django.
|
|
|
|
Error Handling Middleware
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you already have middleware in place that handles :func:`process_exception`
|
|
you will need to take extra care when using Sentry.
|
|
|
|
For example, the following middleware would suppress Sentry logging due to it
|
|
returning a response::
|
|
|
|
class MyMiddleware(object):
|
|
def process_exception(self, request, exception):
|
|
return HttpResponse('foo')
|
|
|
|
To work around this, you can either disable your error handling middleware, or
|
|
add something like the following::
|
|
|
|
from django.core.signals import got_request_exception
|
|
|
|
class MyMiddleware(object):
|
|
def process_exception(self, request, exception):
|
|
# Make sure the exception signal is fired for Sentry
|
|
got_request_exception.send(sender=self, request=request)
|
|
return HttpResponse('foo')
|
|
|
|
Note that this technique may break unit tests using the Django test client
|
|
(:class:`django.test.client.Client`) if a view under test generates a
|
|
:exc:`Http404 <django.http.Http404>` or :exc:`PermissionDenied` exception,
|
|
because the exceptions won't be translated into the expected 404 or 403
|
|
response codes.
|
|
|
|
Or, alternatively, you can just enable Sentry responses::
|
|
|
|
from raven.contrib.django.raven_compat.models import sentry_exception_handler
|
|
|
|
class MyMiddleware(object):
|
|
def process_exception(self, request, exception):
|
|
# Make sure the exception signal is fired for Sentry
|
|
sentry_exception_handler(request=request)
|
|
return HttpResponse('foo')
|
|
|
|
|
|
Gunicorn
|
|
~~~~~~~~
|
|
|
|
If you are running Django with `gunicorn <http://gunicorn.org/>`_ and
|
|
using the ``gunicorn`` executable, instead of the ``run_gunicorn``
|
|
management command, you will need to add a hook to gunicorn to activate
|
|
Raven::
|
|
|
|
from django.core.management import call_command
|
|
|
|
def when_ready(server):
|
|
call_command('validate')
|
|
|
|
Circus
|
|
~~~~~~
|
|
|
|
If you are running Django with `circus <http://circus.rtfd.org/>`_ and
|
|
`chaussette <http://chaussette.readthedocs.org/>`_ you will also need
|
|
to add a hook to circus to activate Raven::
|
|
|
|
from django.conf import settings
|
|
from django.core.management import call_command
|
|
|
|
def run_raven(*args, **kwargs):
|
|
"""Set up raven for django by running a django command.
|
|
It is necessary because chaussette doesn't run a django command.
|
|
"""
|
|
if not settings.configured:
|
|
settings.configure()
|
|
|
|
call_command('validate')
|
|
return True
|
|
|
|
And in your circus configuration:
|
|
|
|
.. sourcecode:: ini
|
|
|
|
[socket:dwebapp]
|
|
host = 127.0.0.1
|
|
port = 8080
|
|
|
|
[watcher:dwebworker]
|
|
cmd = chaussette --fd $(circus.sockets.dwebapp) dproject.wsgi.application
|
|
use_sockets = True
|
|
numprocesses = 2
|
|
hooks.after_start = dproject.hooks.run_raven
|