Coder Perfect

Without suspending or terminating the program, how can I catch and print the whole exception traceback?

Problem

I’d like to be able to catch and log exceptions without departing, for example,

try:
    do_stuff()
except Exception as err:
    print(Exception, err)
    # I want to print the entire traceback here,
    # not just the exception name and details

I want to publish the same output as when the exception is raised without the try…except without intercepting the exception and without exiting my program. How do I go about doing this?

Asked by chriscauley

Solution #1

If you want more information, use traceback.format exc() or sys.exc info().

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])

Answered by volting

Solution #2

The traceback module has previously been mentioned in another answer.

Please keep in mind that in some circumstances, print exc will not produce the desired result. Python 2.x has the following features:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

…will reveal the last exception’s traceback:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

If you truly need to see the original traceback, you can save the exception infos from exc info in a local variable and display them using print exception:

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

Producing:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

However, there are a few drawbacks:

Python 3, on the other hand, produces a less startling outcome by allowing you to retrieve the traceback associated with an exception:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

… will display:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")

Answered by Sylvain Leroux

Solution #3

You can just call: if you’re debugging and just want to examine the current stack trace.

traceback.print_stack()

It’s not necessary to raise an exception manually only to catch it again.

Answered by dimo414

Solution #4

If you don’t want your application to stop because of a mistake, you should use a try/except:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

We’ll use the traceback module from the standard library to get the full traceback:

import traceback

And to demonstrate that we get the whole stacktrace, we’ll make a reasonably complicated stacktrace:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Use the traceback.print exc function to print the whole traceback:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

Which prints:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

However, having a logger set up for your module is a smart practice. It will recognize the module’s name and be able to adjust levels (along with other qualities like handl)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

In that scenario, the logger.exception function should be used instead:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

Which logs:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Maybe you just want the string, in which case the traceback.format exc function will suffice:

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

Which logs:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

And we notice that we get the same output for all three alternatives as when we have an error:

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

Performance concerns aren’t important here as IO usually dominates. I’d prefer, since it does precisely what’s being requested in a forward compatible way:

logger.exception(error)

It’s simple to turn off without affecting the code by adjusting the logging levels and outputs. And, in most cases, doing what is immediately required is the most efficient method.

Answered by Aaron Hall

Solution #5

First, instead of using prints for logging, use the logging stdlib module, which is robust, proven, and well-thought out. You should absolutely use it instead.

Second, when there is a native and easy approach, resist the temptation to make a mess with unrelated technologies. It’s as follows:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

That is all there is to it. You’ve completed your task.

Log.exception is simply a call to log.error (that is, log event with level ERROR) and then printing the traceback.

Here are some things to think about:

Because, well, why not? They’re all there for different reasons. For example, traceback.print_exc’s output is a little bit different from tracebacks produced by the interpreter itself. If you use it, you will confuse anyone who reads your logs, they will be banging their heads against them.

It’s simply not proper to pass exc info=True to log calls. However, log.exception is handy when collecting recoverable failures and logging them (using, for example, the INFO level) with tracebacks, because log.exception only produces logs of one level – ERROR.

And you should try to stay away from sys.exc info as much as possible. It’s an internal interface, not a public one; you can use it if you’re sure you know what you’re doing. It is not designed just for the purpose of printing exceptions.

Answered by tosh

Post is based on https://stackoverflow.com/questions/3702675/how-to-catch-and-print-the-full-exception-traceback-without-halting-exiting-the