15

I'm trying to write a nice error handler for my code, so that when it fails, the logs, traceback and other relevant info get emailed to me.

I can't figure out how to take an exception object and extract the traceback.

I find the traceback module pretty confusing, mostly because it doesn't deal with exceptions at all. It just fetches some global variables from somewhere, assuming that I want the most recent exception. But what if I don't? What if I want to ignore some exception in my error handler? (e.g. if I fail to send me email and want to retry.)

What I want

import traceback as tb

# some function that will fail after a few recursions
def myfunc(x):
   assert x > 0, "oh no"
   return myfunc(x-1)

try:
    myfunc(3)
except Exception as e:
    traceback_str = tb.something(e)

Note that tb.something takes e as an argument.

There's lots of questions on Stack Overflow about using the traceback module to get a traceback string. The unique thing about this question is how to get it from the caught exception, instead of global variables.

Result:

traceback_str contains the string:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in myfunc
  File "<stdin>", line 3, in myfunc
  File "<stdin>", line 3, in myfunc
  File "<stdin>", line 2, in myfunc
AssertionError: oh no

Note that it contains not just the most recent function call, but the whole stack, and includes the "AssertionError" at the end

Asclepius
  • 57,944
  • 17
  • 167
  • 143
falsePockets
  • 3,826
  • 4
  • 18
  • 37

3 Answers3

18

The correct function for tb.something(e) is

''.join(tb.format_exception(None, e, e.__traceback__))
falsePockets
  • 3,826
  • 4
  • 18
  • 37
  • 1
    Note from the [docs](https://docs.python.org/3/library/traceback.html#traceback.print_exception): Since Python 3.10, instead of passing _value_ and _tb_, an exception object can be passed as the first argument. If _value_ and _tb_ are provided, the first argument is ignored in order to provide backwards compatibility. – Jens Apr 22 '22 at 06:56
1

This approach of this answer is new since Python 3.10. This reusable function writes the exception and traceback into a string buffer, and from there as a string.

import io
import traceback


def get_exception_traceback_str(exc: Exception) -> str:
    # Ref: https://stackoverflow.com/a/76584117/
    file = io.StringIO()
    traceback.print_exception(exc, file=file)
    return file.getvalue().rstrip()

Sample usage:

try:
    assert False, 'testing assertion'
except Exception as exc:
    error = get_exception_traceback_str(exc)

print(error)

Sample output:

Traceback (most recent call last):
  File "<input>", line 2, in <module>
AssertionError: testing assertion

Note: To just print the exception and traceback instead, you don't need it in a string, so consider using just traceback.print_exception(exc).

Asclepius
  • 57,944
  • 17
  • 167
  • 143
0

For me @falsePockets' prints only the traceback. I've created this function, which would print the whole exception, including the type and the message:

def error_string(ex: Exception) -> str:
    return '\n'.join([
        ''.join(traceback.format_exception_only(None, ex)).strip(),
        ''.join(traceback.format_exception(None, ex, ex.__traceback__)).strip()
    ])
winwin
  • 958
  • 7
  • 25