sentry is something like an logs aggregation application. It can be used to log exceptions and other log messages from Django or other framework/programming languages (including JavaScript). Instead of sending a lot of emails to a project admin Django will send them to Sentry which will aggregate them and present in a readable form. Sentry is actually a Django application that can be launched setup on your local computer - perfect for testing.

In this article I'll show you how to run Sentry on your local computer (basic setup) and how to configure Django and other parts of the Python code for optimal Sentry logging results.

Installing and configuring Sentry

Installation is standard:
pip install sentry

Under Ubuntu-based system I had to also install Python header files for setproctitle compilation (dependency of sentry). I've installed libpython-all-dev and that solved the problem. Note that current Sentry release (0.5.5) is not compatible with Django 1.5 and newer so if you have such version in your system use a virtualenv to install Sentry in it.

When the installation is done we have to generate a configuration file like so:
sentry init conf.py
A nice configuration will be created that we can edit. It's good for local testing, but you can change EMAIL_BACKEND to console output to prevent Sentry from throwing exception on mail sending failuers:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Next we create the tables and a superuser account with:
sentry --config=conf.py upgrade
After that you can start the application with:
sentry --config=conf.py start
Go to http://localhost:9000 to finish the registration and to create first logged project:
Creating initial team in Sentry

Creating initial team in Sentry

Creating first logged project

Creating first logged project

As the platform we pick the framework or language we are interested in. In the case of Django we will get a basic setup instruction after we create the project. Presented configuration covers basic exception logging only.

Configuring Django

Install raven at start:
pip install raven
Add 'raven.contrib.django.raven_compat', to INSTALLED_APPS. Sentry will also give you RAVEN_CONFIG:
RAVEN_CONFIG = {
    'dsn': 'YOUR_SENTRY_DSN',
}
And that's all you have to do to log exceptions from a Django project. When the Sentry is running any exception thrown in Django will be logged. If your code uses Python logging module (directly or indirectly) or it has Celery tasks then you will have to add some more configuration.

Exception logging

Here is our test view:
from django.views import generic

class ExceptionView(generic.View):
    def get(self, request, **kwargs):
        raise ValueError('I don\'t like this value')

exception_view = ExceptionView.as_view()
Exception thrown by a Django view
Which should show up in Sentry as well:
Exception logged in Sentry

Exception logged in Sentry

You can click on the exception title to get more details like the whole exception, stacktrace, Django user data etc.

Logging messages from logging module

Logging module is often use to log some events that did occur, while they shouldn't in general (either in the case of a catch exception or odd result that isn't a critical error for which exception would be suitable). Here is an example usage:

import logging

from django import http
from django.views import generic

logger = logging.getLogger(__name__)


class LoggingView(generic.View):
    def get(self, request, **kwargs):
        logger.warning("An error")
        return http.HttpResponse("ok")

logging_view = LoggingView.as_view()


class InheritingView(LoggingView):
    pass

inheriting_view = InheritingView.as_view()
Opening any of the two view won't log anything as there is no handler configured yet. Development server will write something like this:
No handlers could be found for logger "myapp.views"
In Django (1.3 or newer) settings find LOGGING and replace it with:
LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'root': {
        'level': 'DEBUG',
        'handlers': ['sentry'],
    },
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
    },
    'handlers': {
        'sentry': {
            'level': 'DEBUG',
            '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,
        },
    },
}

This configuration is described on Sentry documentation. Whats worth checking is that "root" and "sentry" have "DEBUG" log level. They will log everything (usually you would send errors and warnings to sentry, and low important debug or info to some files etc.).

With new LOGGING configuration the logger will log to Sentry:

Error logged in Sentry
You may notice that the logged event doesn't contain to much data. There is no request or stacktrace. Python logging supports passing a dictionary under extra argument. Sentry has some extended support for it. If you pass the request object under request key it will log it nicely. If you pass stack key set to True it will add a stacktrace and you will be able to tell which of the two test views triggered the logged event (and more).
logger.warning("an error", extra={'request': request, 'stack': True})
You can also use SENTRY_AUTO_LOG_STACKS in settings to log stacktraces by default:
SENTRY_AUTO_LOG_STACKS = True
Error event with an request object

Error event with an request object

Error, warning, info and debug in Sentry

Logging from Celery tasks to Sentry

To log exceptions from Celery task we have to add one extra logger to LOGGING:
'celery': {
    'level': 'WARNING',
    'handlers': ['sentry'],
    'propagate': False,
},
Which will cover exceptions. To make logging work you have to use it like so:
from celery import task

@task()
def celery_task():
    logger = celery_task.get_logger()
    logger.warning("celerrrrrrrrrry log")
Where "celery_task" is the function-task name.
Warning with a stacktrace triggered from a Celery task

Warning with a stacktrace triggered from a Celery task

To use the logging like in the views logging you can set CELERYD_HIJACK_ROOT_LOGGER to False:

CELERYD_HIJACK_ROOT_LOGGER = False
This will prevent Celery from catching and using the logger on its own. When the logger is hijacked you will see logger output in the celery console (django-admin.py celeryd -l info). When the hijacking is disabled you will see them in Sentry.

Some other Python services may require their own logger. But that's all you need to get a nicely configured Sentry logging.

blog comments powered by Disqus

Categories