139

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.

2
  • 6
    At least raise (without argument, so the stracktrace gets preserved) after logging, otherwise you swallow the exception silently. Commented Dec 22, 2010 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. Commented Dec 22, 2010 at 13:03

5 Answers 5

171

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.

Sign up to request clarification or add additional context in comments.

1 Comment

except catch-all can be a bad idea: stackoverflow.com/a/4990739/1091116
119

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)

2 Comments

This is exactly what I wanted. I needed to log an exception from a task and didn't have an except block.
Or just directly use logging.exception("..") and it will automatically log the traceback.
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.

3 Comments

"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.
You code is useful when you cannot use logging package. E.g. When implementing a logging Handler :-)
@Doomsday, I would think implementing a logging.Handler might be the only good use for this code. For the OP's original question, OP should definitely use logging with logging.exception or log at a lower level with exc_info=True.
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

4 Comments

You're going to a lot of effort. Why not just logging.exception(exc)?
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.
"explicit is better than implicit"
@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.
2

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

3 Comments

Printing is not logging. Use logging.exception(exc).
@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.
This is the most candid code here. Meaningful message as default.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.