{{announcement.body}}
{{announcement.title}}

Error Tracking in Python

DZone 's Guide to

Error Tracking in Python

In this article, we discuss how to use Error Tracker in Python to better manage exceptions in Flask and Django applications.

· Web Dev Zone ·
Free Resource

404-the-strand

Error tracking is essential for any application when many people are relying on the app. If your application is not working in production for whatever reason, it can be a nightmare experience for users depending on correct functionality.  

There are many exceptions tracking plugins and modules available. Some of them are open source whereas others are paid, like HoneyBadgerNewRelicELK, etc. 

What about having a custom exception handler that can track errors in different contexts with additional features like:

  •  Send emails or notify developers (interested entities).

  • Create or update tickets in Bugzilla or Jira.

  • Push exceptions to the ELK stack or any other system for analytics and store detailed exceptions in DB.

  • While capturing exceptions, provide local and global variables details (failure context).

You may also like: The Top Four Exception Tracking Services

Error Tracker

Error tracker is a python library. It provides all required interfaces and supports all the above features. One interesting thing about this library is that it can mask variables that are sensitive (i.e should not be exposed, like password, credit card number, etc.). It's well documented at Error Tracker doc and available via PyPI  

A typical setup of Error Tracker can be visualized as:

Error Tracking

Error Tracking


Error Tracker can be used with any type of Python application. We'll start with Flask.

Tools we need:

  •  Editor any IDE.

  • Python.

  • Pip.

Installation

 pip install error-tracker 

We'll use settings file for app configuration you might call it config.py or some other format like YAML etc, we'll use python file for simplicity. 

settings.py

APP_ERROR_SEND_NOTIFICATION = True
APP_ERROR_RECIPIENT_EMAIL = ('dev@example.com',)
APP_ERROR_EMAIL_SENDER="server@example.com"
APP_ERROR_SUBJECT_PREFIX = ""
APP_ERROR_MASK_WITH = "**************"
APP_ERROR_MASKED_KEY_HAS = ("password", "secret")
APP_ERROR_URL_PREFIX = "/dev/error"


 There're seven configurations in this file:

 APP_ERROR_SEND_NOTIFICATION  — This configures whether a notification has to be sent or not. If it's set, then it will try to send notification using the notification interface. 

 APP_ERROR_RECIPIENT_EMAIL  — This refers to whom the email has to be sent to. It is required to build email content. It can be a list of emails or a single email address. 

 APP_ERROR_EMAIL_SENDER  — Who is sending email to the recipient(s).

 APP_ERROR_SUBJECT_PREFIX  — Any email subject prefix. By default, it will be [IndexError] [GET] http://127.0.0.1:5000/go.

 APP_ERROR_MASK_WITH — What should be used to mask the sensitive information.

 APP_ERROR_MASKED_KEY_HAS — What variables should be masked. It can mask all local and global variables. Masking is done based on the variable name. If a data type is a dictionary, or it's subclass then their keys are examined. 

 APP_ERROR_URL_PREFIX — All unique exceptions are stored in the database and can be browsed at this endpoint.

Create another python file 

For email-sender, we'll implement NotificationMixin 

from flask_mail import Mail, Message
class Notifier(Mail, NotificationMixin):
    def notify(self, request, exception,
               email_subject=None,
               email_body=None,
               from_email=None,
               recipient_list=None):
        message = Message(email_subject, recipient_list, email_body, sender=from_email)
        self.send(message)


For issue tracking, we'll implement TicketingMixin 

class Ticketing(TicketingMixin):
    def raise_ticket(self, exception, request=None):
        # implement this method to communicate with Jira or Bugzilla etc
        pass


Let's create a Flask app to serve requests. We'll use SQLite for simplicity.

app = Flask(__name__)
app.config.from_object(settings)
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///tmp.sqlite"
db = SQLAlchemy(app)


The errors can be tracked by creating an instance of AppErrorTracker and initializing this with proper details. This application can store all the exceptions in the database. 

app_error = AppErrorTracker(app=app, db=db,
                            notifier=Notifier(app=app), ticketing=Ticketing())
db.create_all()


For error tracking, we are going to use the decorator  track_exception provided by AppErrorTracker  class. We'll attach this decorator whenever HTTP 500 occurs. 

@app.errorhandler(500)
@app_error.track_exception
def error_500(e):
    return render_template('500.html'), 500


Now, it's ready to be used. We can fire some requests and see how it works. For testing, let's add one endpoint that will throw 500 errors with different types of exceptions.

@app.route('/go')
def die():
    import random
    password = "test"
    foo = {}
    foo['password'] = "Oh! My Gosh"
    foo['secret'] = "NO ONE KNOWS"
    exceptions = [KeyError, ArithmeticError, BaseException, IndentationError, IndexError, MemoryError,
                  NameError, NotImplementedError, ImportError, FloatingPointError, EOFError,
                  OSError, AssertionError, AttributeError, GeneratorExit, Exception,
                  EnvironmentError, ImportError,
                  NotImplementedError, RuntimeError]
    raise random.choice(exceptions)


Now, run the app in debug=False mode and browse, as it will fail. Now, go to http://127.0.0.1:5000/dev/error/. This will display the current exceptions in a table like: 

Exceptions caught

Exceptions caught

Any of the specific exceptions can be browsed by clicking the corresponding link, in the Exception detail  section, all frame details that have been captured can be seen. 

Exceptions available for analysis

Exceptions available for analysis

The complete Flask application can be found at https://github.com/sonus21/error-tracker/tree/master/examples/flask-sample.

Implementation With Django

Using Exception Tracker with Django is also very simple. Install the application using pip install error-tracker. Once we've installed it, we need to configure some part of the applications using the settings.py file.

For the Django application, we need to add this app in the INSTALLED_APPS  list and add Middleware  to catch exceptions. Middleware should be added at the end of the list so that it will be called first whenever an exception occurs. 

APP_ERROR_RECIPIENT_EMAIL = ('example@example.com',)
APP_ERROR_SUBJECT_PREFIX = "Server Error"
APP_ERROR_EMAIL_SENDER = 'user@example.com'

INSTALLED_APPS = [
    ...
    'error_tracker.DjangoErrorTracker'
]
MIDDLEWARE = [
    ...
    'error_tracker.django.middleware.ExceptionTrackerMiddleWare'
]


Error Tracker provides a few default pages for listing exception, deleting exception, and browsing a specific exception. These endpoints can be plugged in with Django's other URLs.

 from error_tracker.django import urls

urlpatterns = [
    ...
    url("dev/", include(urls)),
]


These are all the code changes we need for the Django app. The complete code is available at https://github.com/sonus21/error-tracker/tree/master/examples/DjangoSample.

Conclusion

Apps using Error Tracker can be further customized, like what models to use. Currently, it records only one instance of failure, while all failures can be tracked and dumped to some database. Read more about them at https://error-tracker.readthedocs.io/en/latest/index.html#.

The complete code is available on GitHub.

If you found this post helpful, please share and give a thumbs up.


Further Reading

Topics:
python ,flask ,exception notifications ,ticketing system ,bug and issue tracker ,django

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}