120

How can I log an exception in Python?

I've looked at some options and found out I can access the actual exception details using this code:

import sys
import traceback

try:
    1/0
except:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    traceback.print_exception(exc_type, exc_value, exc_traceback)

I would like to somehow get the string print_exception() throws to stdout so that I can log it.

vvvvv
  • 25,404
  • 19
  • 49
  • 81
Maxim Veksler
  • 29,272
  • 38
  • 131
  • 151
  • 5
    At least `raise` (without argument, so the stracktrace gets preserved) after logging, otherwise you swallow the exception silently. –  Dec 22 '10 at 11:53
  • 2
    You should always explicitly state the exception you are trying to catch: `except NameError as e`, say. That will prevent you catching things like `KeyboardInterrupt` *and* give you a reference to the exception object, which you can study for more details. – Katriel Dec 22 '10 at 13:03

5 Answers5

159

Take a look at logging.exception (Python Logging Module)

import logging 
def foo():
    try:
        some_code()
    except:
        logging.exception('')

This should automatically take care of getting the traceback for the current exception and logging it properly.

cyborg
  • 9,989
  • 4
  • 38
  • 56
rlotun
  • 7,897
  • 4
  • 28
  • 23
87

In Python 3.5 you can pass exception instance in exc_info argument:

import logging
try:
    1/0
except Exception as e:
   logging.error('Error at %s', 'division', exc_info=e)
yurez
  • 2,826
  • 1
  • 28
  • 22
  • 1
    This is exactly what I wanted. I needed to log an [exception from a task](https://docs.python.org/3/library/asyncio-task.html#asyncio.Task.exception) and didn't have an except block. – Eric Grunzke Nov 18 '21 at 19:43
  • 3
    Or just directly use `logging.exception("..")` and it will automatically log the traceback. – raghavsikaria Jun 29 '22 at 09:05
64

To answer your question, you can get the string version of print_exception() using the traceback.format_exception() function. It returns the traceback message as a list of strings rather than printing it to stdout, so you can do what you want with it. For example:

import sys
import traceback

try:
    asdf
except NameError:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
    print ''.join('!! ' + line for line in lines)  # Log it or whatever here

This displays:

!! Traceback (most recent call last):
!!   File "<stdin>", line 2, in <module>
!! NameError: name 'asdf' is not defined

However, I'd definitely recommend using the standard Python logging module, as suggested by rlotun. It's not the easiest thing to set up, but it's very customizable.

Ben Hoyt
  • 10,694
  • 5
  • 60
  • 84
  • 8
    "not the easiest thing to set up" sort of implies that it's *hard* to set up, but that's just not true, `logging.basicConfig()` in the main function is adequate for most simple applications. – SingleNegationElimination Aug 03 '11 at 04:41
  • 2
    You code is useful when you cannot use logging package. E.g. When implementing a logging Handler :-) – Doomsday Sep 23 '13 at 10:00
  • 4
    @Doomsday, I would think implementing a [`logging.Handler`](https://docs.python.org/library/logging.html#handler-objects) might be the only good use for this code. For the OP's original question, OP should definitely use [logging](http://docs.python.org/library/logging.html) with `logging.exception` or log at a lower level with `exc_info=True`. – KyleWpppd Jul 15 '15 at 18:44
62

Logging exceptions is as simple as adding the exc_info=True keyword argument to any log message, see entry for Logger.debug in http://docs.python.org/2/library/logging.html.

Example:

try: 
    raise Exception('lala')
except Exception:
    logging.info('blah', exc_info=True)

output (depending, of course, on your log handler config):

2012-11-29 10:18:12,778 - root - INFO - <ipython-input-27-5af852892344> : 3 - blah
Traceback (most recent call last):
  File "<ipython-input-27-5af852892344>", line 1, in <module>
    try: raise Exception('lala')
Exception: lala
NeilenMarais
  • 2,949
  • 1
  • 25
  • 23
  • 2
    You're going to a lot of effort. Why not just `logging.exception(exc)`? – Chris Johnson Jan 29 '16 at 06:09
  • 13
    logging.exception is short for logging.error(..., exc_info=True), so if you intend to log the exception as an error it would be better to use logging.exception. If you want to log a formatted exception at a log level other than error you must use exc_info=True. – NeilenMarais Jan 29 '16 at 09:54
  • 3
    "explicit is better than implicit" – DylanYoung Jun 21 '19 at 15:47
  • @ChrisJohnson - you could simply use logging.exception() in your example... Also, there are perfectly reasonable architectural reasons to want to log exception info and not set the log's `LEVEL` to `ERROR`. `logger.exception` is a unique case since it's the only call that doesn't explicitly set the `LEVEL` to be its function name. `logger.exception()` sets the level to `ERROR`. We have alarms tied to spikes in errors, and although some exceptions are not important enough to raise those alarms, we definitely want to log the exc_info for them. – Dougyfresh May 12 '20 at 16:16
3

First of all, consider using a proper Exception type on your except clause. Then, naming the exception, you can print it:

try:
    1/0
except Exception as e:
    print e

Dependending on your Python version, you must use

except Exception, e
erickrf
  • 2,069
  • 5
  • 21
  • 44
  • 8
    Printing is not logging. Use `logging.exception(exc)`. – Chris Johnson Jan 29 '16 at 06:09
  • 13
    @ChrisJohnson you've recommended `logging.exception(exc)` in a few comments on this page, but it's a bad idea. The first argument to `logging.exception` isn't for the exception to log (Python just grabs the one from the current `except:` block by magic), it's for the *message* to log before the traceback. Specifying the exception as the message causes it to be converted to a string, which results in the exception message being duplicated. If you don't have a meaningful message to log along with your exception, use `logging.exception('')`, but `logging.exception(exc)` makes little sense. – Mark Amery May 08 '17 at 22:27
  • This is the most candid code here. Meaningful message as default. – Flavio Jul 25 '19 at 11:28