Django Support

edit

Getting Elastic APM set up for your Django project is easy, and there are various ways you can tweak it to fit to your needs.

Installation

edit

Install the Elastic APM agent using pip:

$ pip install elastic-apm

or add it to your project’s requirements.txt file.

For apm-server 6.2+, make sure you use version 2.0 or higher of elastic-apm.

If you use Django with uwsgi, make sure to enable threads.

Setup

edit

Set up the Elastic APM agent in Django with these two steps:

  1. Add elasticapm.contrib.django to INSTALLED_APPS in your settings:
INSTALLED_APPS = (
   # ...
   'elasticapm.contrib.django',
)
  1. Choose a service name, and set the secret token if needed.
ELASTIC_APM = {
   'SERVICE_NAME': '<SERVICE-NAME>',
   'SECRET_TOKEN': '<SECRET-TOKEN>',
}

or as environment variables:

ELASTIC_APM_SERVICE_NAME=<SERVICE-NAME>
ELASTIC_APM_SECRET_TOKEN=<SECRET-TOKEN>

You now have basic error logging set up, and everything resulting in a 500 HTTP status code will be reported to the APM Server.

You can find a list of all available settings in the Configuration page.

The agent only captures and sends data if you have DEBUG = False in your settings. To force the agent to capture data in Django debug mode, set the debug configuration option, e.g.:

ELASTIC_APM = {
   'SERVICE_NAME': '<SERVICE-NAME>',
   'DEBUG': True,
}

Performance Metrics

edit

To send performance metrics to Elastic APM, simply add our tracing middleware to your MIDDLEWARE settings:

MIDDLEWARE = (
    'elasticapm.contrib.django.middleware.TracingMiddleware',
    # ...
)

For Django 1.8, you can add the same middleware to MIDDLEWARE_CLASSES.

Make sure that it is the first middleware in the list.

Instrumenting custom Python code

edit

To gain further insights into the performance of your code, please see instrumenting custom code.

Ignoring specific views

edit

You can use the TRANSACTIONS_IGNORE_PATTERNS configuration option to ignore specific views. The list given should be a list of regular expressions which are matched against the transaction name as seen in the Elastic APM user interface:

ELASTIC_APM['TRANSACTIONS_IGNORE_PATTERNS'] = ['^OPTIONS ', 'views.api.v2']

This example ignores any requests using the OPTIONS method and any requests containing views.api.v2.

Using the route as transaction name

edit

By default, we use the function or class name of the view as the transaction name. Starting with Django 2.2, Django makes the route (e.g. users/<int:user_id>/) available on the request.resolver_match object. If you want to use the route instead of the view name as the transaction name, you can set the django_transaction_name_from_route config option to true.

ELASTIC_APM['DJANGO_TRANSACTION_NAME_FROM_ROUTE'] = True

in versions previous to Django 2.2, changing this setting will have no effect.

Integrating with the RUM agent

edit

To correlate performance measurement in the browser with measurements in your Django app, you can help the RUM (Real User Monitoring) agent by configuring it with the Trace ID and Span ID of the backend request. We provide a handy template context processor which adds all the necessary bits into the context of your templates.

To enable this feature, first add the rum_tracing context processor to your TEMPLATES setting. You most likely already have a list of context_processors, in which case you can simply append ours to the list.

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'OPTIONS': {
            'context_processors': [
                # ...
                'elasticapm.contrib.django.context_processors.rum_tracing',
            ],
        },
    },
]

Then, update the call to initialize the RUM agent (which probably happens in your base template) like this:

elasticApm.init({
    serviceName: "my-frontend-service",
    pageLoadTraceId: "{{ apm.trace_id }}",
    pageLoadSpanId: "{{ apm.span_id }}",
    pageLoadSampled: {{ apm.is_sampled_js }}
})

See the JavaScript RUM agent documentation for more information.

Enabling and disabling the agent

edit

The easiest way to disable the agent is to set Django’s DEBUG option to True in your development configuration. No errors or metrics will be logged to Elastic APM.

However, if during debugging you would like to force logging of errors to Elastic APM, then you can set DEBUG to True inside of the Elastic APM configuration dictionary, like this:

ELASTIC_APM = {
   # ...
   'DEBUG': True,
}

Logging

edit

For fine-grained control over logging use Python’s built-in logging module. If you are new to how the logging module works together with Django, read more in the Django documentation.

An example of how your LOGGING setting could look:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
    },
    'handlers': {
        'elasticapm': {
            'level': 'WARNING',
            'class': 'elasticapm.contrib.django.handlers.LoggingHandler',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose'
        }
    },
    'loggers': {
        'django.db.backends': {
            'level': 'ERROR',
            'handlers': ['console'],
            'propagate': False,
        },
        'mysite': {
            'level': 'WARNING',
            'handlers': ['elasticapm'],
            'propagate': False,
        },
        # Log errors from the Elastic APM module to the console (recommended)
        'elasticapm.errors': {
            'level': 'ERROR',
            'handlers': ['console'],
            'propagate': False,
        },
    },
}

With this configuration, logging can be done like this in any module in the myapp django app:

You can now use the logger in any module in the myapp Django app, for instance myapp/views.py:

import logging
logger = logging.getLogger('mysite')

try:
    instance = MyModel.objects.get(pk=42)
except MyModel.DoesNotExist:
    logger.error(
        'Could not find instance, doing something else',
        exc_info=True
    )

Note that exc_info=True adds the exception information to the data that gets sent to Elastic APM. Without it, only the message is sent.

Extra data

edit

If you want to send more data than what you get with the agent by default, logging can be done like so:

import logging
logger = logging.getLogger('mysite')

try:
    instance = MyModel.objects.get(pk=42)
except MyModel.DoesNotExist:
    logger.error(
        'There was some crazy error',
        exc_info=True,
        extra={
            'datetime': str(datetime.now()),
        }
    )

Celery Integration

edit

For a general guide on how to set up Django with Celery, head over to Celery’s Django documentation.

Elastic APM will automatically log errors from your celery tasks, and record performance data.

Logging "HTTP 404 Not Found" Errors

edit

By default, Elastic APM does not log HTTP 404 errors. If you wish to log these errors, add 'elasticapm.contrib.django.middleware.Catch404Middleware' to MIDDLEWARE in your settings:

MIDDLEWARE = (
    # ...
    'elasticapm.contrib.django.middleware.Catch404Middleware',
    # ...
)

Note that this middleware respects Django’s IGNORABLE_404_URLS setting.

Disable the agent during tests

edit

To prevent the agent from sending any data to the APM Server during tests, set the ELASTIC_APM_DISABLE_SEND environment variable to true, e.g.:

ELASTIC_APM_DISABLE_SEND=true python manage.py test

Troubleshooting

edit

Elastic APM comes with a Django command that helps troubleshooting your setup. To check your configuration, run

python manage.py elasticapm check

To send a test exception using the current settings, run

python manage.py elasticapm test

If the command succeeds in sending a test exception, it will print a success message:

python manage.py elasticapm test

Trying to send a test error using these settings:

SERVICE_NAME:      <SERVICE_NAME>
SECRET_TOKEN:      <SECRET_TOKEN>
SERVER:            http://localhost:8200

Success! We tracked the error successfully! You should be able to see it in a few seconds.

Supported Django and Python versions

edit

A list of supported Django and Python versions can be found on our Supported Technologies page.