Integrating Sentry Into Django Applications

  • 2018-06-13 02:56 AM
  • 91

Integrating Sentry Into Django Applications. Sentry is a web application written in Django that can be used to log exceptions and other useful messages from an application written in different programming languages in a UI intsead of the traditional log files. Sentry provides real-time error reporting and exception tracking for your web and mobile apps.

In this article, we will look at how to integrate Sentry into our existing Django application for production mode which would be hosted on Heroku .

Why Sentry

A good service to report exceptions that might come up in your application of the various browsers and devices you support should be able to do following:

Table of Contents

  1. Why Sentry
  2. Why Deploy Django on Heroku
  3. Prerequisites
  4. Overview
  5. Create Virtual Environment
  6. Deploy to Heroku
  7. Configure Static Assets
  8. Run your app locally with Heroku
  9. Deploy Application To Heroku
  10. Integrate Sentry Into Django Application
  11. Log Caught Exceptions
  12. Get User Feedback
  • Sentry provides significantly more context and debug capabilities, similar to the DEBUG = True flag
  • Sentry provides insight into production and deployment information on how to reproduce events and how to fix issues
  • It provides full out-of-the-box support for many of the popular frameworks and even for applications that do not live on the web

Why Deploy Django on Heroku

Heroku is a cloud application platform, it is basically a Platform-as-a-Service (PaaS). They support several programming languages, including Python. It is very easy to deploy Django applications on Heroku. They also offer a free plan, which is quite limited, but it is great to get started and to host demos of Django applications.

Prerequisites

The practices in this article assume that you have:

  • Knowledge in developing web application using Django web framework
  • A GitHub account
  • A free Heroku account
  • Heroku Toolbelt installed Locally
  • Python Installed Locally
  • PostgreSQL database installed locally
  • An existing Python application

Overview

We will be deploying a Django application and set up Sentry inside the application for error reporting. The application is the official Django tutorials polling application where users can click on polls and vote.

Preparing the Application

In this tutorial, we will deploy an existing application, Django Polls. It’s the official Django tutorial project, and its source code is available GitHub, so you actually can clone the repository and install it locally.
Now the next step is to setup an environment to run the application, your best bet is to use a virtual environment.

Create Virtual Environment

// install virtualenv
sudo apt-get install virtualenv

// create virtual environment
 virtualenv venv

// activate virtual envronment
source venv/bin/activate

You will get a prompt with the environment name. i.e (venv). It means the environment is now active. Now we're ready to install our requirements inside our environment. Move to the root folder of your project directory and run the command:

    pip install -r requirements.txt 

If you have cloned the GitHub repo your project folder should look like this. Feel free to give it any other name. The folder structure should look like this:

    django_polls
        ├── db.sqlite3
        ├── django_polls
            ├── __init__.py  
            ├── settings.py
            ├── urls.py
            └── wsgi.py
        ├── manage.py
        ├── polls
            ├── admin.py
            ├── apps.py
            ├── __init__.py
            ├── migrations
            ├── models.py
            │── __pycache__
            ├── templates
            ├── tests.py
            ├── urls.py
            └── views.py
            ├── requirements.txt
            ├── README.md
            ├── static
            │   └── bootstrap

Deploy to Heroku

Here is a list of things you need to add to your project:

  1. Add a Procfile in the project root
  2. Generate a requirements.txt file
  3. Add Gunicorn to requirements.txt
  4. Install psycopg2 – PostgreSQL database adapter for Python
  5. A runtime.txt to specify the correct Python version in the project root;
  6. Configure Whitenoise to serve static files.

Procfile

A Procfile is a text file in the root directory of your application that defines process types and explicitly declares what command should be executed to start your app. This Procfile requires Gunicorn, the production web server that we recommend for Django applications. Your profile will look something like this

    web: gunicorn django_polls.wsgi --log-file - 

Next, we need to install Gunicorn. The Procfile requires Gunicorn, the production web server that is recommended for Django applications

    pip install gunicorn

Next, we create a runtime.txt in the project root to specify the version of Python to run our project with. I used Python 3.6 so my runtime.txt looks like this

    python-3.6.0

Replace with whatever version of Python you are using. For more information, see Supported Python Runtimes.

Configure Static Assets

We almost ready to deploy the application with Heroku one more thing we need to do is serve our static files with Whitenoise. WhiteNoise allows your web app to serve its own static files, making it a self-contained unit that can be deployed anywhere without relying on Nginx, Amazon S3 or any other external service. (Especially useful on Heroku, OpenShift, and other PaaS providers.).The best part is you don't have to do too much configuration.

Make sure your STATIC configurations are correct in your** settings.py** file

    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.10/howto/static-files/

    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    STATIC_URL = '/static/'

    # Extra places for collectstatic to find static files.
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'static'),
    )

To install whitenoise with pip

    pip install whitenoise

Edit your settings.py file add WhiteNoise to the MIDDLEWARE_CLASSES list, above all other middleware apart from Django’s SecurityMiddleware:

    MIDDLEWARE = [
        # 'django.middleware.security.SecurityMiddleware',
        'whitenoise.middleware.WhiteNoiseMiddleware',
        # [...]
    ]
    # for caching files and compression support
    STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'

Update your wsgi.py file, add Whitenoise to your Django application:


    import os

    from django.core.wsgi import get_wsgi_application
    from whitenoise.django import DjangoWhiteNoise

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_polls.settings")

    application = get_wsgi_application()
    # To enable WhiteNoise you need to wrap your existing WSGI application in a WhiteNoise instance 
    application = DjangoWhiteNoise(application)

Don't forget to update your project dependencies

    pip freeze > requirements.txt

Run your app locally with Heroku

We need to install the PostgreSQL database adapter for Python

    pip install psycopg2

Next we configure our database settings in settings.py to use PostgresSQL database

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME':'django_polls',
            'USER': 'username',
            'PASSWORD':'password',
            'HOST':'127.0.0.1',
            'PORT': '',
        }
}

For production, we can use dj-database-url a Django utility that allows you to utilize the 12factor inspired DATABASE_URL environment variable to configure your Django application.

    pip install  dj-database-url

Now update your database configuration.


    import dj_database_url

    # Update database configuration with $DATABASE_URL.
    db_from_env = dj_database_url.config(conn_max_age=500)
    DATABASES['default'].update(db_from_env)

The dj-database-url package parses the DATABASE_URL environment variable and converts it to Django’s desired configuration format automatically. Make sure you have logged in your Heroku account.

    heroku login
    Enter your Heroku credentials.
    Email: ...
    Password (typing will be hidden):

Run your application locally by using the Heroku local command.

    python manage.py migrate
    heroku local web
    8:06:12 PM web.1 |  [2017-03-20 20:06:12 +0100] [11908] [INFO] Starting gunicorn 19.7.0
    8:06:13 PM web.1 |  [2017-03-20 20:06:12 +0100] [11908] [INFO] Listening at: http://0.0.0.0:5000 (11908)
    8:06:13 PM web.1 |  [2017-03-20 20:06:12 +0100] [11908] [INFO] Using worker: sync
    8:06:13 PM web.1 |  [2017-03-20 20:06:12 +0100] [11911] [INFO] Booting worker with pid: 11911

Deploy Application To Heroku

Now that your application should be running without errors on your local machine, we can now deploy to Heroku. The Heroku deployment process is done through Git. Your application will be stored in a remote Git repository for the Heroku Cloud. To prevent some files from going into the repository created by Heroku we need to declare a .gitignore file Here is a typical .gitignore file:

    __pycache__
    .*
    db.sqlite3

Now commit your changes to git.

    git add .
    git commit -m "Added a Procfile."

Create your application.

    heroku create demo-dj-polls
    Creating ⬢ demo-dj-polls... done  https://demo-dj-polls.herokuapp.com/ | https://git.heroku.com/demo-dj-polls.git

    git push heroku master

If you can omit the app name parameter (demo-dj-polls), then Heroku will name the application for you.

To open the app in your browser, type “heroku open”, navigate to the polls URL “https://demo-dj-polls.herokuapp.com/polls/

Heroku allows you to run commands in a by using the heroku run command.This can be useful for scripts that only need to be executed when needed, such as maintenance tasks, loading fixtures into a database, or database migrations during application updates.

Migrate database.

 heroku run python manage.py migrate

Create superuser.

     heroku run python manage.py createsuperuser

Login as admin and add polls to the application.


Navigate to the polls URL “https://demo-dj-polls.herokuapp.com/polls/

Integrate Sentry Into Django Application

To use Sentry you need to have a Sentry Account. Once you sign up for a developer’s account

  1. Create a new project
  2. Selecting the platform or language that powers your application.

Sentry provides you with personalized documentation that you can follow to integrating Sentry in your Application.

To use Sentry with Python applications you use Raven which is the Python client for Sentry.

Installation

    pip install raven --upgrade

Integrating Sentry with Django is very simple, in your settings.py update installed apps,

    INSTALLED_APPS = [
        ...
        'raven.contrib.django.raven_compat', # installs a hook in Django that will automatically report uncaught exceptions.
        ...
    ]
    RAVEN_CONFIG={
    # format &apos;dsn&apos;:&apos;https://<public_key>:<secret_key>@sentry.io/<project_id>
&apos;dsn&apos;:&apos;https://bbb7eef6523d444fa6265268c5adefbe:[email protected]/150009&apos;
    }

That's it. Now you can test if Sentry is working .

    //update dependencies
    pip freeze > requirements.txt

    git add --all
    git commit -m "Install Raven"
    git push heroku master

Test your Sentry config using the standard Django management interface:

    python manage.py raven test
    Client configuration:
      base_url       : https://sentry.io
      project        : 150009
      public_key     : bbb7eef6523d444fa6265268c5adefbe
      secret_key     : 1fcf9c32a660487c82c14c888b66051d
    Sending a test message... Event ID was &apos;88327bfcca864d58a8c69d950d7602fe&apos;

It works! In our Sentry Project Dashboard, we will see the test message below.

We can continue to test Sentry's functionality by breaking our code to see what happens.
Go ahead and turn off debug in your settings.py

    # SECURITY WARNING: don&apos;t run with debug turned on in production!
    DEBUG = False

In your polls/view.py


class IndexView(generic.ListView):
   # template_name = &apos;polls/index.html&apos;
   template_name = &apos;polls/index.htm&apos; #this should raise a template not found error
    context_object_name = &apos;latest_poll_list&apos;

    def get_queryset(self):
        return Poll.objects.all()[:5]

Now update your project on Heroku.

    git add --all
    git commit -m "Break Code To Test Raven"
    git push heroku master

When you reload your application page in the browser, you should get a 500 response. Check your Sentry project dashboard, and you should see the error message. Let’s go back and fix the broken code.

Log Caught Exceptions

To capture an error that fails silently on your application you can use the Raven client to record these errors. In your polls/view.py file, add the following code.

    from raven.contrib.django.raven_compat.models import client

    ...
    def vote(request, poll_id):
        p = get_object_or_404(Poll, pk=poll_id)
        try:
            # selected_choice = p.choice_set.get(pk=request.POST[&apos;choice&apos;])
            selected_choice = p.choice_set.get(pk=request.POST[&apos;choices&apos;]) #break code to catch exception
        except (KeyError, Choice.DoesNotExist):
            client.captureException()
            # Redisplay the poll voting form.
            return render(request, &apos;polls/detail.html&apos;, {&apos;poll&apos;: p,
                &apos;error_message&apos;: "You didn&apos;t select a choice.",
            })
        else:
            selected_choice.votes += 1
            selected_choice.save()
            return HttpResponseRedirect(reverse(&apos;results&apos;, args=(p.id,)))

Now try to vote by clicking on a poll and selecting one of the options.

Let's see what we've got in our Sentry project's dashboard.

There is a whole lot information we get back from Sentry that could be useful when we are trying to simulate the events that lead up to this error and debug them.

Get User Feedback

User feedback strengthens the connection to the user of your application. If a user triggers an error in the application a feedback dialog would be displayed where the user describes the error he/she just encountered. Typically this form is optional, and the developers will still collect a basic crash report regardless of whether it is filled, but getting any extra helpful feedback can be crucial in determining the root cause of an issue. The first step in doing this is creating a custom handler500() in your urls.py file:

    from django.template import Context,loader
    from django.http import  HttpResponseServerError

    def handler500(request):
        t = loader.get_template(&apos;500.html&apos;)  # You need to create a 500.html template.
        return HttpResponseServerError(t.render(Context({
            &apos;request&apos;: request,
        })))

base.html

    <head>
    ...
    <script src="https://cdn.ravenjs.com/3.12.1/raven.min.js">
        Raven.config(&apos;https://[email protected]/146791&apos;).install()
        window.onerror = Raven.process;
    </script>

    {% if request.sentry.id %}
      <script>
      Raven.showReportDialog({
        // grab the eventId generated by the Sentry SDK
        eventId: &apos;{{ request.sentry.id }}&apos;,

        // use the public DSN (dont include your secret!)
        dsn: &apos;https://[email protected]/146791&apos;
      });
      </script>
    {% endif %}
    </head

500.html

    {% extends &apos;base.html&apos; %}
    <p>You&apos;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 %}

To test this let's make our vote method throw a 500 error. Inside polls/views.py, add the following code.

    def vote(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    try:
        selected_choice = p.choice_set.get(pk=request.POST[&apos;choice&apos;])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the poll voting form.
        client.captureException()
        # logger.error(&apos;There was some crazy error&apos;, exc_info=True, extra={
        #     &apos;request&apos;: request,
        # })
        return render(request, &apos;polls/detail.html&apos;, {
            &apos;poll&apos;: p,
            &apos;error_message&apos;: "You didn&apos;t select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # return HttpResponseRedirect(reverse(&apos;results&apos;, args=(p.id,)))
        return HttpResponseRedirect(reverse(&apos;results&apos;, args=(p,)))

Update your application on the server.

    git add .
    git commit -m "Test User Feedback"
    git push heroku master

Try to vote.
In the Sentry project dashboard under the user feedback tab,add the following code

Conclusion

As your application grows traditional logging can become too much of a headache and really boring. Sentry is designed to solve these problems. In addition, Sentry can also be utilized as a warning system so a development team can quickly pay attention to issues that may arise and helps you understand how your application behaves over time. Good logs can save a lot of headaches when trying to solve a problem.

Learn More

Python and Django Full Stack Web Developer Bootcamp

Django 2 & Python | The Ultimate Web Development Bootcamp

The Ultimate Beginner’s Guide to Django 1.11

Django Core | A Reference Guide to Core Django Concepts

Source viva: https://scotch.io/tutorials/integrating-sentry-into-django-applications#why-sentry

Suggest