如何在不停止程序的情况下打印完整的回溯?

时间:2022-03-25 22:54:13

I'm writing a program that parses 10 websites, locates data files, saves the files, and then parses them to make data that can be readily used in the NumPy library. There are tons of errors this file encounters through bad links, poorly formed XML, missing entries, and other things I've yet to categorize. I initially made this program to handle errors like this:

我正在编写一个程序,它解析10个网站,定位数据文件,保存文件,然后解析它们,以便在NumPy库中方便地使用数据。这个文件会遇到大量错误,包括坏链接、格式不佳的XML、丢失的条目以及其他我还没有归类的东西。我最初制作这个程序是为了处理这样的错误:

try:
    do_stuff()
except:
    pass

But now I want to log errors:

但现在我想记录错误:

try:
    do_stuff()
except Exception, err:
    print Exception, err

Note this is printing to a log file for later review. This usually prints very useless data. What I want is to print the exact same lines printed when the error triggers without the try-except intercepting the exception, but I don't want it to halt my program since it is nested in a series of for loops that I would like to see to completion.

注意,这是打印到日志文件以供以后查看。这通常会打印非常无用的数据。我想要的是打印错误触发时打印出的完全相同的行,但除了拦截异常之外,我不希望它停止我的程序,因为它嵌套在一系列for循环中,我希望看到它完成。

8 个解决方案

#1


299  

Some other answer have already pointed out the traceback module.

其他一些答案已经指出了traceback模块。

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

请注意,使用print_exc,在某些情况下,您将无法获得预期的结果。在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:

如果您确实需要访问原始回溯,一个解决方案是将从exc_info返回的异常信息缓存到一个本地变量中,并使用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:

    sys_info的doc:

    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)

    将traceback返回值分配给正在处理异常的函数中的一个局部变量将导致循环引用。这将防止同一函数中的局部变量或回溯引用的任何内容被垃圾收集。[…如果你确实需要traceback,请确保在使用后删除它(最好是尝试一下……)最后声明)

  • 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.

    从Python 2.2开始,当启用垃圾收集时,这些循环将被自动回收,并且它们变得不可访问,但是避免创建循环仍然更有效。


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

另一方面,通过允许您访问与异常相关的回溯,Python 3产生了一个不那么令人惊讶的结果:

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!")

#2


509  

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

formateback .exc()或sys.exc_info()将生成更多信息,如果这正是您想要的。

import traceback
import sys

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

#3


151  

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

如果您正在调试并只想查看当前堆栈跟踪,可以简单地调用:

traceback.print_stack()

traceback.print_stack()

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

不需要手动抛出异常来再次捕获它。

#4


52  

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/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:

要打印完整的回溯,请使用回溯。print_exc方法:

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:

或者你只是想要字符串,在这种情况下,你想要回溯。format_exc函数:

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!

#5


6  

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.

要获得精确的堆栈跟踪(作为字符串),如果没有try/except来跳过它,只需将其放在捕获违规异常的except块中。

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):

下面是如何使用它(假设flaky_func已定义,日志调用您最喜欢的日志记录系统):

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.

捕获并重新打开KeyboardInterrupts是一个好主意,这样您仍然可以使用Ctrl-C杀死程序。日志记录超出了问题的范围,但是日志记录是一个很好的选择。系统和回溯模块的文档。

#6


4  

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

您将需要将try/except放入可能发生错误的最内部循环中,例如。

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.

换句话说,您将需要对try/except中可能失败的语句进行包装,但尽可能在最内部循环中进行包装。

#7


2  

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.

您需要回溯模块。它可以让您像Python那样打印堆栈转储。特别是,print_last函数将打印最后一个异常和堆栈跟踪。

#8


1  

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.

除了@Aaron Hall的答案之外,如果您正在日志记录,但是不想使用logger .exception()(因为它在错误级别日志),您可以使用较低的级别并传递exc_info=True。如。

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)

#1


299  

Some other answer have already pointed out the traceback module.

其他一些答案已经指出了traceback模块。

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

请注意,使用print_exc,在某些情况下,您将无法获得预期的结果。在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:

如果您确实需要访问原始回溯,一个解决方案是将从exc_info返回的异常信息缓存到一个本地变量中,并使用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:

    sys_info的doc:

    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)

    将traceback返回值分配给正在处理异常的函数中的一个局部变量将导致循环引用。这将防止同一函数中的局部变量或回溯引用的任何内容被垃圾收集。[…如果你确实需要traceback,请确保在使用后删除它(最好是尝试一下……)最后声明)

  • 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.

    从Python 2.2开始,当启用垃圾收集时,这些循环将被自动回收,并且它们变得不可访问,但是避免创建循环仍然更有效。


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

另一方面,通过允许您访问与异常相关的回溯,Python 3产生了一个不那么令人惊讶的结果:

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!")

#2


509  

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

formateback .exc()或sys.exc_info()将生成更多信息,如果这正是您想要的。

import traceback
import sys

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

#3


151  

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

如果您正在调试并只想查看当前堆栈跟踪,可以简单地调用:

traceback.print_stack()

traceback.print_stack()

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

不需要手动抛出异常来再次捕获它。

#4


52  

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/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:

要打印完整的回溯,请使用回溯。print_exc方法:

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:

或者你只是想要字符串,在这种情况下,你想要回溯。format_exc函数:

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!

#5


6  

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.

要获得精确的堆栈跟踪(作为字符串),如果没有try/except来跳过它,只需将其放在捕获违规异常的except块中。

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):

下面是如何使用它(假设flaky_func已定义,日志调用您最喜欢的日志记录系统):

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.

捕获并重新打开KeyboardInterrupts是一个好主意,这样您仍然可以使用Ctrl-C杀死程序。日志记录超出了问题的范围,但是日志记录是一个很好的选择。系统和回溯模块的文档。

#6


4  

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

您将需要将try/except放入可能发生错误的最内部循环中,例如。

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.

换句话说,您将需要对try/except中可能失败的语句进行包装,但尽可能在最内部循环中进行包装。

#7


2  

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.

您需要回溯模块。它可以让您像Python那样打印堆栈转储。特别是,print_last函数将打印最后一个异常和堆栈跟踪。

#8


1  

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.

除了@Aaron Hall的答案之外,如果您正在日志记录,但是不想使用logger .exception()(因为它在错误级别日志),您可以使用较低的级别并传递exc_info=True。如。

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)