An Extended Exception Formatter for Python

This logging formatter for Python 2 add useful information to logged exceptions. In comparision with Python´s built-in formatter a lot of useful information like

  • local variables of each stack level
  • used encodings
  • program and library versions (do be added by you)
  • a few lines around the triggering statement

are added to the output.

I gave a presentation in German "Python Exceptions besser auswerten" with some more details.

Supported and tested Python versions

  • Python 2.7

Requirements

There are no additional requirements. All you need is Python 2.

Installation

Add the file extended_exec_formatter.py to your project and activate it like described in the section "Usage".

Additionally you can add the version number of your application to the app_version variable. On demand you can add further version numbers parallel to app_version.

Usage

import logging
import extended_exec_formatter

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# create a stream handler for console output
console = logging.StreamHandler()
console.setLevel(logging.ERROR)

# specify the logging format using the ExtendedExceptionFormatter
formatter = extended_exec_formatter.ExtendedExceptionFormatter('%(asctime)s %(levelname)s - %(message)s')
console.setFormatter(formatter)

# process uncaught exceptions
extended_exec_formatter.install_exec_handler()

# add the handler to the logger
logger.addHandler(console)

Comparision Python built-in formatter vs. extended formatter

Built-in formatter

$ cat wrong_simple.py
#!/usr/bin/env python2

def wrong():
    d = {1: 'A'}
    k = 'non-existing'
    d[k]

wrong()

$ python2 wrong_simple.py 
Traceback (most recent call last):
  File "wrong_simple.py", line 8, in <module>
    wrong()
  File "wrong_simple.py", line 6, in wrong
    d[k]
KeyError: 'non-existing'

Extended formatter

$ cat wrong_extended.py
#!/usr/bin/env python2
import logging
import extended_exec_formatter
logger = logging.getLogger()
logger.setLevel(logging.INFO)
console = logging.StreamHandler()
console.setLevel(logging.ERROR)
formatter = extended_exec_formatter.ExtendedExceptionFormatter('%(asctime)s %(levelname)s - %(message)s')
console.setFormatter(formatter)
extended_exec_formatter.install_exec_handler()
logger.addHandler(console)

def wrong():
    d = {1: 'A'}
    k = 'non-existing'
    d[k]

wrong()

$ python2 wrong_extended.py 
2017-04-13 21:47:45,135 ERROR - An unhandled exception occurred
An unexpected error occurred!

Date and time:       2017-04-13T21:47:45.136039
Python version:      2.7.13
Application version: unknown
Language code:       None
Encoding:            None
Filesystem encoding: UTF-8

Exception type:      <type 'exceptions.KeyError'>
Exception details:   'non-existing'
Application stack trace (most recent call first):
Stack frame at level 0
======================
  File "wrong.py", line 16
  Function "wrong()"
  Source code context:

      def wrong():
          d = {1: 'A'}
          k = 'non-existing'
  ->      d[k]

      wrong()
  Local variables:
  -> k (<type 'str'>): 'non-existing'
  -> d (<type 'dict'>): {1: 'A'}

Stack frame at level 1
======================
  File "wrong.py", line 18
  Function "<module>()"
  Source code context:

      def wrong():
          d = {1: 'A'}
          k = 'non-existing'
          d[k]

  ->  wrong()
  Local variables:
  -> logging (<type 'module'>): <module 'logging' from '/usr/lib/python2.7/logging/__init__.pyc'>
  -> __builtins__ (<type 'module'>): <module '__builtin__' (built-in)>
  -> __file__ (<type 'str'>): 'wrong.py'
  -> console (<class 'logging.StreamHandler'>): <logging.StreamHandler object at 0x7efdaeb50b10>
  -> __package__ (<type 'NoneType'>): None
  -> wrong (<type 'function'>): <function wrong at 0x7efdaeb5c410>
  -> extended_exec_formatter (<type 'module'>): <module 'extended_exec_formatter' from '/home/carsten/Projekte.cvs/PythonExceptionHandler/extended_exec_formatter.pyc'>
  -> __name__ (<type 'str'>): '__main__'
  -> logger (<class 'logging.RootLogger'>): <logging.RootLogger object at 0x7efdaeb50650>
  -> formatter (<class 'extended_exec_formatter.ExtendedExceptionFormatter'>): <extended_exec_formatter.ExtendedExceptionFormatter object at 0x7efdae6b7290>
  -> __doc__ (<type 'NoneType'>): None

Project page and feedback

You find the project page at https://www.carstengrohmann.de/PythonExtendedExceptionFormatter.html.

The source code is hosted at https://bitbucket.org/carstengrohmann/pythonexceptionhandler.

Comments, suggestions and patches are welcome and appreciated. Please file an "issue" or send me an email.

License

This project is covered by the MIT License.

Copyright (c) 2014-2018 Carsten Grohmann mail@carsten-grohmann.de

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.