Catch and print full Python exception traceback without halting/exiting the program
Asked Answered
I

19

1466

I want to catch and log exceptions without exiting, e.g.,

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 print the exact same output that is printed when the exception is raised without the try/except intercepting the exception, and I do not want it to exit my program.

Ionopause answered 13/9, 2010 at 17:3 Comment(5)
Not a full answer, but someone might want to know that you can access lots of info looking into err.__traceback__ (at least in Python 3.x)Tar
It seems I'm the only one in the world who wants to print the stack when there's no error (= only to see how I got here at this precise line (it's not my code, and it's so ugly I cant figure out how it did come here!)).Skinflint
@OlivierPons probably very bad practice but add a "1/0" line and you'll see what got you there ;)Subordinary
@Subordinary I am working on the Python code of a person who was dyslexic and had converted his old PHP 'Symfony 1' code into Python code... I'm doing every day JavaScript, and Python, I know well C, C#, Java, C++, Python, Php, and LUA: I have never seen such bad code in my entire life. Even the names of the functions have nothing to do with what they do, and the comments are unreadable. Just doing a print is not enough. And even the call to the stack is often not sufficient.Skinflint
@OlivierPons time to find a new job maybe. Sometimes people intentionally obfuscate their code for job security purposes and there's not much you can do except live in misery.Sherasherar
S
851

Some other answer have already pointed out the traceback module.

Please notice that with print_exc, in some corner cases, you will not obtain what you would expect. In Python 2.x:

import traceback

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

    traceback.print_exc()

...will display the traceback of the last exception:

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

If you really need to access the original traceback one solution is to cache the exception infos as returned from exc_info in a local variable and display it 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!

Few pitfalls with this though:

  • From the doc of sys_info:

    Assigning the traceback return value to a local variable in a function that is handling an exception will cause a circular reference. This will prevent anything referenced by a local variable in the same function or by the traceback from being garbage collected. [...] If you do need the traceback, make sure to delete it after use (best done with a try ... finally statement)

  • but, from the same doc:

    Beginning with Python 2.2, such cycles are automatically reclaimed when garbage collection is enabled and they become unreachable, but it remains more efficient to avoid creating cycles.


On the other hand, by allowing you to access the traceback associated with an exception, Python 3 produce a less surprising result:

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!")
Schlenger answered 5/6, 2013 at 18:5 Comment(0)
V
1314

traceback.format_exc() will yield more info if that's what you want.

import traceback

def do_stuff():
    raise Exception("test exception")

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())

This outputs:

Traceback (most recent call last):
  File "main.py", line 9, in <module>
    do_stuff()
  File "main.py", line 5, in do_stuff
    raise Exception("test exception")
Exception: test exception
Vorfeld answered 13/9, 2010 at 17:27 Comment(8)
print(sys.exc_info()[0] prints <class 'Exception'>.Paver
dont use exc... the traceback contains all the info #4565059Denison
It might be expected (or not), but this doesn't produce the full traceback. It's limited by the points raising the exception, and handling it (python 3.8). You don't need to try/catch to display the traceback.Boser
print(sys.exc_info()[2]) yields <traceback object at 0x0000028A79E6B2C8>.Sovereign
print(traceback.format_exc()) is better than traceback.print_tb(exc.__traceback__). print(sys.exc_info()) returns the whole tuple and looks like (<class 'UnicodeDecodeError'>, UnicodeDecodeError('utf-8', b'\x81', 0, 1, 'invalid start byte'), <traceback object at 0x7f179d64ae00>) So indeed traceback.format_exc() is really superior because that prints Traceback (most recent call last): File "<ipython-input-15-9e3d6e01ef04>", line 2, in <module> b"\x81".decode() UnicodeDecodeError: 'utf-8' codec can't decode byte 0x81 in position 0: invalid start byteWilk
Remove that exc_info() it does not work.Ventriloquist
For those that want the formatted string output to return to some client this answer helped me: https://mcmap.net/q/36785/-extract-traceback-info-from-an-exception-objectWirewove
This answer should include more details about what these functions do. In the case of traceback.format_exc(), it simply returns a formatted string exactly like what you would see in stdout. This is exactly what I needed and what many people want.Size
S
851

Some other answer have already pointed out the traceback module.

Please notice that with print_exc, in some corner cases, you will not obtain what you would expect. In Python 2.x:

import traceback

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

    traceback.print_exc()

...will display the traceback of the last exception:

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

If you really need to access the original traceback one solution is to cache the exception infos as returned from exc_info in a local variable and display it 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!

Few pitfalls with this though:

  • From the doc of sys_info:

    Assigning the traceback return value to a local variable in a function that is handling an exception will cause a circular reference. This will prevent anything referenced by a local variable in the same function or by the traceback from being garbage collected. [...] If you do need the traceback, make sure to delete it after use (best done with a try ... finally statement)

  • but, from the same doc:

    Beginning with Python 2.2, such cycles are automatically reclaimed when garbage collection is enabled and they become unreachable, but it remains more efficient to avoid creating cycles.


On the other hand, by allowing you to access the traceback associated with an exception, Python 3 produce a less surprising result:

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!")
Schlenger answered 5/6, 2013 at 18:5 Comment(0)
A
367

If you're debugging and just want to see the current stack trace, you can simply call:

traceback.print_stack()

There's no need to manually raise an exception just to catch it again.

Amazonite answered 28/4, 2015 at 21:40 Comment(3)
The traceback module does exactly that - raise and catch an exception.Synovia
Output goes to STDERR by default BTW. Wasn't appearing in my logs because it was being redirected somewhere else.Hydrangea
@Synovia I can't see it with python 3.8. And the thing with try and catch is that it doesn't display the full traceback, Only from raise to except.Boser
J
227

How to print the full traceback without halting the program?

When you don't want to halt your program on an error, you need to handle that error with a try/except:

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

To extract the full traceback, we'll use the traceback module from the standard library:

import traceback

And to create a decently complicated stacktrace to demonstrate that we get the full stacktrace:

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

def do_something_that_might_error():
    raise_error()

Printing

To print the full traceback, use the traceback.print_exc method:

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!

Better than printing, logging:

However, a best practice is to have a logger set up for your module. It will know the name of the module and be able to change levels (among other attributes, such as handlers)

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

In which case, you'll want the logger.exception function 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!

Or perhaps you just want the string, in which case, you'll want the traceback.format_exc function instead:

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!

Conclusion

And for all three options, we see we get the same output 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!

Which to use

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)

Logging levels and outputs can be adjusted, making it easy to turn off without touching the code. And usually doing what's directly needed is the most efficient way to do it.

Jennette answered 16/7, 2015 at 3:23 Comment(8)
as said above and for me too, traceback.print_exc() returns only the last call : how do you succeed to return several level of the stack (and possibly all levele s?)Paedo
@geekobi I'm not sure what you're asking here. I demonstrate that we get the traceback up to the entry point of the program/interpreter. What are you not clear on?Jennette
What @geekobi is saying is if you catch and re-raise, traceback.print_exc() will just return the re-raise stack, not the original stack.Helpmeet
@Helpmeet how are you "reraising"? Are you doing a bare raise or exception chaining, or are you hiding the original traceback? see #2052890Jennette
In terms of performance is there any difference between the 2 approaches?Gounod
@Gounod I added a section addressing your question.Jennette
Although logger.exception is a forward compatible way, it always logs at the level of ERROR. Suppose I want to log at level of warning and I also want the full stack trace then I have 2 options: 1) logger.warning("Some warning message.", exc_info=true) OR 2) logger.warning(f"Some warning message - {traceback.format_exc()}") - Do you have any recommendation in terms of which one to choose?Gounod
Thanks for that followup question - I think the exc_info=True argument is actually better for logging, a keyword argument is more maintainable than custom code that puts the traceback into a string. I'll get around to updating my answer.Jennette
H
51

First, do not use prints for logging, there is a stable, proven and well-thought out stdlib module to do that: logging. You definitely should use it instead.

Second, do not be tempted to do a mess with unrelated tools when there is a native and simple approach. Here it is:

log = logging.getLogger(__name__)

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

That's it. You are done now.

Explanation for anyone who is interested in how things work under the hood

What log.exception is actually doing is just a call to log.error (that is, log event with level ERROR) and print traceback then.

Why is it better?

Well, here are some considerations:

  • it is just right;
  • it is straightforward;
  • it is simple.

Why should nobody use traceback or call logger with exc_info=True or get their hands dirty with sys.exc_info?

Well, just because! They all exist for different purposes. 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.

Passing exc_info=True to log calls is just inappropriate. But, it is useful when catching recoverable errors and you want to log them (using, e.g INFO level) with tracebacks as well, because log.exception produces logs of only one level - ERROR.

And you definitely should avoid messing with sys.exc_info as much as you can. It's just not a public interface, it's an internal one - you can use it if you definitely know what you are doing. It is not intended for just printing exceptions.

Harmonium answered 14/12, 2018 at 12:28 Comment(10)
It also doesn't work as-is. That's not it. I'm not done now: this answer just wastes time.Holography
I would also add that you can just do logging.exception(). No need to create instance of log unless you have special requirements.Trichiasis
I find this answer kind of ridiculous. It's full of "do/don't do this just because" without explaining why. Your points in "why is it better?" is practically just all saying the same thing: "because it is." Which I do not find helpful. You did explain a little bit at least.Melonymelos
Good information (I didn't know about logging.exception) but a bit condescending. I think this is due to language barrier rather than malicious intent.Nineteen
What this guy said. In my company we'll fire anyone who logs using print. /sBelie
"Well, just because" is an exceptionally poor justification for blanket statements on what people should use. Rule of thumb is generally to assume every dev knows at least as much as you when writing docs. The overwhelming majority of large Python codebases are going to encounter circumstances where functions have been passed around as objects and invoked in weird places, and in these circumstances the stacktrace of the actual exception is irrelevant because the calling of the function is decoupled from its actual use in code.Spline
I want to pass the exception traceback to a AWS SNS topic from a remote function and I do not think this is possible to do in a straightforward manner through the logging interface. Therefore, I think there are use cases that you haven't thought about when typing this answer and mentioning so many things we should not do with no explanation. This is a bad and very opinionated answer.Mailman
If you want to do so, you simply need to use standard logging facility. Eg. to log exception first, then pass logs to whatever you want to. If this is not enough four you, you still need to stick with standard logging facility. E.g. to write custom handler and to make it it do whatever you want to exceptions. Those cases do covered in the answer as a special cases. So, if all you need is to simply log exception, you still need to stick with standard logging facility and use log.exception. I still don't see any "badness" and "opinionated" here.Harmonium
It is interesting, though. The whole point of this answer can be stated as: "just" use standard library. Don't invent your own wheel. Interesting part is how people reacted to that simple advise: stick with standards. Some call this answer "bad". Some other argue that "just because" isn't enough in this simple case. Who so? May it be that in order to actually do so, you need to know that standard library? May be it is "just because" you should be read the docs in the first place?Harmonium
This is what i needed. Elegant and Simple. Thanks.Frobisher
B
49

traceback.format_exception(exception_object)

If you only have the exception object, you can get the traceback as a string from any point of the code in Python 3 with:

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

Since Python 3.10, this can be further simplified to: thanks

tb_str = ''.join(traceback.format_exception(exc_obj))

Full example:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc_obj = e

tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

or with the simpler Python 3.10 interface:

tb_str = ''.join(traceback.format_exception(exc_obj))

Output for either of them:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf

Documentation: https://docs.python.org/3.10/library/traceback.html#traceback.format_exception

See also: Extract traceback info from an exception object

Tested Python 3.11.4, Ubuntu 23.04.

Bicarbonate answered 18/5, 2019 at 13:14 Comment(3)
Why does the only way to do this in the language involving passing the same information twice (both exc_obj and exc_obj.__traceback__), and an irrelevant third argument None?Merciless
I think that this only works if you raise and then catch the exception, but not if you try getting the traceback before raising an exception object that you create, which you might want to do in some designs.Waterproof
So, repeating what herve-guerin asked in Nov 22, 2017 -- the python traceback only returns the last call level (i.e., only g()): how do you succeed to return several level of the stack (i.e., including f() and possibly all up levels) ?Wimple
O
27

In addition to Aaron Hall's answer, if you are logging, but don't want to use logging.exception() (since it logs at the ERROR level), you can use a lower level and pass exc_info=True. e.g.

try:
    do_something_that_might_error()
except Exception:
    logging.info('General exception noted.', exc_info=True)
Ophthalmia answered 24/4, 2018 at 3:19 Comment(1)
This is good too when dealing with a detected failure of logging... i.e. when for some reason you have failed to create an actual Logger object.Kamerun
A
27

I don't see this mentioned in any of the other answers. If you're passing around an Exception object for whatever reason...

In Python 3.5+ you can get a trace from an Exception object using traceback.TracebackException.from_exception(). For example:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    try:
        stack_lvl_3()
    except Exception as e:
        # raise
        return e


def stack_lvl_1():
    e = stack_lvl_2()
    return e

e = stack_lvl_1()

tb1 = traceback.TracebackException.from_exception(e)
print(''.join(tb1.format()))

However, the above code results in:

Traceback (most recent call last):
  File "exc.py", line 10, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Exception: ('a1', 'b2', 'c3')

This is just two levels of the stack, as opposed to what would have been printed on screen had the exception been raised in stack_lvl_2() and not intercepted (uncomment the # raise line).

As I understand it, that's because an exception records only the current level of the stack when it is raised, stack_lvl_3() in this case. As it's passed back up through the stack, more levels are being added to its __traceback__. But we intercepted it in stack_lvl_2(), meaning all it got to record was levels 3 and 2. To get the full trace as printed on stdout we'd have to catch it at the highest (lowest?) level:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    stack_lvl_3()


def stack_lvl_1():
    stack_lvl_2()


try:
    stack_lvl_1()
except Exception as exc:
    tb = traceback.TracebackException.from_exception(exc)

print('Handled at stack lvl 0')
print(''.join(tb.stack.format()))

Which results in:

Handled at stack lvl 0
  File "exc.py", line 17, in <module>
    stack_lvl_1()
  File "exc.py", line 13, in stack_lvl_1
    stack_lvl_2()
  File "exc.py", line 9, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')

Notice that the stack print is different, the first and last lines are missing. Because it's a different format().

Intercepting the exception as far away from the point where it was raised as possible makes for simpler code while also giving more information.

Affectional answered 1/6, 2019 at 11:24 Comment(3)
This is a lot better than the previous method(s), but is still ridiculously convoluted just to print out a stacktrace. Java takes less code FGS.Derangement
An alternative to print(''.join(tb1.format())) is sys.stdout.writelines(tb1.format()).Choreographer
The trace info will be empty though, if you try accessing it before the exception is raisedWaterproof
C
24

In python3 (works in 3.9) we can define a function and can use that where ever we want to print the details.

import traceback

def get_traceback(e):
    lines = traceback.format_exception(type(e), e, e.__traceback__)
    return ''.join(lines)

try:
    1/0
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')

try:
    spam(1,2)
except Exception as e:
    print('------Start--------')
    print(get_traceback(e))
    print('------End--------')

The output would be like:

bash-3.2$ python3 /Users/soumyabratakole/PycharmProjects/pythonProject/main.py
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 26, in <module>
    1/0
ZeroDivisionError: division by zero

------End--------
------Start--------
Traceback (most recent call last):
  File "/Users/soumyabratakole/PycharmProjects/pythonProject/main.py", line 33, in <module>
    spam(1,2)
NameError: name 'spam' is not defined

------End--------
Carafe answered 3/10, 2021 at 3:11 Comment(0)
L
15

If you have an Error object already, and you want to print the whole thing, you need to make this slightly awkward call:

import traceback
traceback.print_exception(type(err), err, err.__traceback__)

That's right, print_exception takes three positional arguments: The type of the exception, the actual exception object, and the exception's own internal traceback property.

In python 3.5 or later, the type(err) is optional... but it's a positional argument, so you still have to explicitly pass None in its place.

traceback.print_exception(None, err, err.__traceback__)

I have no idea why all of this isn't just traceback.print_exception(err). Why you would ever want to print out an error, along with a traceback other than the one that belongs to that error, is beyond me.

Lindstrom answered 26/11, 2019 at 2:43 Comment(0)
F
10

To get the precise stack trace, as a string, that would have been raised if no try/except were there to step over it, simply place this in the except block that catches the offending exception.

desired_trace = traceback.format_exc(sys.exc_info())

Here's how to use it (assuming flaky_func is defined, and log calls your favorite logging system):

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

It's a good idea to catch and re-raise KeyboardInterrupts, so that you can still kill the program using Ctrl-C. Logging is outside the scope of the question, but a good option is logging. Documentation for the sys and traceback modules.

Fermium answered 15/11, 2015 at 18:9 Comment(3)
This doesn't work in Python 3 and needs to be changed to desired_trace = traceback.format_exc(). Passing sys.exc_info() as the argument was never the correct thing to do, but gets silently ignored in Python 2—but not in Python 3 (3.6.4 anyway).Jointress
KeyboardInterrupt is not derived (directly or indirectly) from Exception. (Both are derived from BaseException.) This means except Exception: will never catch a KeyboardInterrupt, and thus the except KeyboardInterrupt: raise is completely unnecessary.Lifegiving
traceback.format_exc(sys.exc_info()) not working for me with python 3.6.10Mythological
M
9

You will need to put the try/except inside the most innerloop where the error may occur, i.e.

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e

... and so on

In other words, you will need to wrap statements that may fail in try/except as specific as possible, in the most inner-loop as possible.

Malikamalin answered 13/9, 2010 at 17:10 Comment(0)
R
9
import io
import traceback

try:
    call_code_that_fails()
except:

    errors = io.StringIO()
    traceback.print_exc(file=errors)  # Instead of printing directly to stdout, the result can be further processed
    contents = str(errors.getvalue())
    print(contents)
    errors.close()
Rung answered 18/11, 2021 at 17:17 Comment(3)
2 comments: The use of traceback.print_exc() was already discussed in previous answers. More importantly, why all that mucking about with io.StringIO when those last five lines are exactly equivalent to traceback.print_exc()?Jeramey
@Jeramey I believe those lines are useful if you want to get access to the error body and not just print it. Personally I found it useful because I am recording the stack trace to a database.Perforate
@Jeramey This is exactly what I needed, to send the traceback to logs. Though others have used format_exception function, which is helpful and would be using it instead. But this is great to know, as it is generic to use in such cases, where the output goes directly to stdout. He uses file option to send the errors to the errors variable and captures it.Setback
S
7

A remark about this answer's comments: print(traceback.format_exc()) does a better job for me than traceback.print_exc(). With the latter, the hello is sometimes strangely "mixed" with the traceback text, like if both want to write to stdout or stderr at the same time, producing weird output (at least when building from inside a text editor and viewing the output in the "Build results" panel).

Traceback (most recent call last):
File "C:\Users\User\Desktop\test.py", line 7, in
hell do_stuff()
File "C:\Users\User\Desktop\test.py", line 4, in do_stuff
1/0
ZeroDivisionError: integer division or modulo by zero
o
[Finished in 0.1s]

So I use:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')
Swainson answered 6/11, 2018 at 17:18 Comment(0)
M
6

This usage in this answer is new since Python 3.10, and has not been covered by any prior answer. To print a traceback, it is possible to give the exception to traceback.print_exception.

Example:

import traceback

try:
    object.bad_attr
except Exception as exc:
    traceback.print_exception(exc)

Output traceback:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: type object 'object' has no attribute 'bad_attr'

As a reminder, Python 3.10 or newer is required for this answer to work.

To capture the trackeback as a string, see this answer.

Matsuyama answered 16/5, 2023 at 17:57 Comment(4)
@intergallactic Please try the example in the answer as it has been tested to work.Matsuyama
I can confirm that this does work, and is simpler and easy-to-implement than Alexander Serkin's answer.Sarcous
I confirm this doesn't workChroma
@Chroma Check your version of Python. It needs to be at least 3.10.Matsuyama
B
4

You want the traceback module. It will let you print stack dumps like Python normally does. In particular, the print_last function will print the last exception and a stack trace.

Boote answered 13/9, 2010 at 17:11 Comment(0)
C
3

python 3 solution

stacktrace_helper.py:

from linecache import getline
import sys
import traceback


def get_stack_trace():
    exc_type, exc_value, exc_tb = sys.exc_info()
    trace = traceback.format_stack()
    trace = list(filter(lambda x: ("\\lib\\" not in x and "/lib/" not in x and "stacktrace_helper.py" not in x), trace))
    ex_type = exc_type.__name__
    ex_line = exc_tb.tb_lineno
    ex_file = exc_tb.tb_frame.f_code.co_filename
    ex_message = str(exc_value)
    line_code = ""
    try:
        line_code = getline(ex_file, ex_line).strip()
    except:
        pass

    trace.insert(
        0, f'File "{ex_file}", line {ex_line}, line_code: {line_code} , ex: {ex_type} {ex_message}',
    )
    return trace


def get_stack_trace_str(msg: str = ""):
    trace = list(get_stack_trace())
    trace_str = "\n".join(list(map(str, trace)))
    trace_str = msg + "\n" + trace_str
    return trace_str
Cellarer answered 16/10, 2020 at 13:34 Comment(0)
J
2

This is my solution to write the error in a log file and also on console:

import logging, sys
import traceback
logging.basicConfig(filename='error.log', level=logging.DEBUG)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    exc_info=(exc_type, exc_value, exc_traceback)
    logging.critical("\nDate:" + str(datetime.datetime.now()), exc_info=(exc_type, exc_value, exc_traceback))
    print("An error occured, check error.log to see the error details")
    traceback.print_exception(*exc_info)


sys.excepthook = handle_exception
Jahdol answered 10/10, 2020 at 12:50 Comment(0)
B
-7

You could do:

try:
    do_stuff()
except Exception, err:
    print(Exception, err)
    raise err
Butcherbird answered 20/1, 2021 at 22:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.