Django: Errors and Logging

This post is part of my Django series. You can see an overview of the series along with instruction on how to get all the source code here.

This article assumes you are comfortable creating a Django project, can create apps and register them into the INSTALLED_APPS list of the settings file. If not please read my Django HelloWorld article.

This article assumes you have a project called DjangoSandBox in an application called errorsandlogging.

Errors

We can enforce HTTP GET/POST methods upon a view function with the @require_http_methods decorator. Access denied error methods are raised for other HTTP methods.

#views.py
from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def error_if_not_get_or_post(request):
    return HttpResponse("This can only be called with a get or a post")

If we only want HTTP POST calls to our view function we can use the @require_POST decorator; access denied errors are raised for other HTTP methods.

#views.py

from django.views.decorators.http import require_POST

@require_POST
def error_if_not_post(request):
    return HttpResponse("{0}, this can only be called with a post".format(request.POST.get("name", "")))

If we only want HTTP GET method calls to our view function we can use the @require_GET decorator; access denied errors are raised for other http methods.

#views.py

from django.views.decorators.http import require_GET

@require_GET
def error_if_not_get(request):
    return HttpResponse("This can only be called with a get")

We can return a HTTP 404 error (response not found) by raising a Http404 error. This will use the 404 html file placed within the site templates directory.

#views.py
from django.http import Http404

def error_as_404(request):
    raise Http404("There is nothing to see here")

We can also return a custom HTTP 404 error by returning an instance of HttpResponseNotFound. This is similar to raising a Http404 but allows us to use custom html rather than the stock site 404 page.

#views.py

from django.http import HttpResponseNotFound

def error_as_custom_404(request):
    return HttpResponseNotFound('<h1>There is nothing to see here</h1>')

We can return a HTTP 405 error (response not allowed) by returning an instance of HttpResponseNotAllowed.

#views.py
from django.http import HttpResponseNotAllowed

def not_allowed(request):
    return HttpResponseNotAllowed("<h1>You are not allowed to see this</h1>")

Logging

This assumes that you have a basic working knowledge of Python logging. If this is not the case then you can come up to speed with my post here.

The logging config is defined within the settings config of the Django project; any instances of the logger will then automatically pick these settings up.

Below we create two loggers as defined within the ‘handlers’ section.

  • A terminal logger which will log anything with a level of DEBUG or higher; it is called ‘console’.
  • A file logger which will log anything with a level of ERROR or higher; it is called ‘file’.

We create two formatters and assign them to a logger via the formatter variable.

  • A simple formatter which will prefix our log message with the time and the log level. It is called ‘simple’ and is assigned to the console logger.
  • A verbose formatter which will prefix our log message with the level, time, module, process id and thread id. It is called verbose and is assigned to the file logger.

The loggers or handlers are then assigned in the loggers section to our helloworld app.

#settings.py

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(asctime)s %(levelname)s %(message)s'
        }
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            'level': 'ERROR',
            'class': 'logging.FileHandler',
            'filename': 'debug.log',
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'helloworld': {
            'handlers': ['console', 'file'],
            'level': 'WARNING',
            'propagate': True,
        },
    },
}

The following is a test view to check our custom Django log config is working. We throw a log message of each logging level at the logger as well as a trace stack and an exception.

#views.py

import logging
from django.http import HttpResponse

logger = logging.getLogger(__name__)

def with_logger(request):
    logger.debug('This is some debug')
    logger.info('This is information')
    logger.warning('This is a warning')
    logger.error('This is a error')
    logger.critical('This is a critical')

    logger.critical("Output with trace", exc_info=True)

    try:
        boom = 1 / 0
        logger.debug("Looks like we have broke the compiler as 1/0 = {0}".format(boom))
    except ZeroDivisionError as e:
        logger.exception(e)

    logger.critical('Final output to show we are logging still')

    return HttpResponse("Logged output")

The logging.getLogger function call passes in the name of the file; this ensures that we will only ever use one logger instance for any view function within this file; as long as they use the logger returned into the logger variable.

We can hook this view into our URL routing config.

#urls.py

from django.conf.urls import patterns, url

from . import views

urlpatterns = 
    patterns('',
             url(r'^logger/$', views.with_logger, name="logger")
             )

We hook into our errorsandlogging application into our project URL routing config:

# DjangoSandBox/urls.py
from django.conf.urls import patterns, include, url

from django.contrib import admin

from errorsandloggingimport urls as errorsandlogging_urls

urlpatterns = 
    patterns('',
url(r'^errorsandlogging/', include(errorsandlogging_urls, namespace="errorsandlogging")),
             )

To test run the development server and navigate to:

http://127.0.0.1:8000/errorsandlogging/logger/

References

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s