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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3702675/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:20:30  来源:igfitidea点击:

How to print the full traceback without halting the program?

pythonexception-handling

提问by chriscauley

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

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

采纳答案by Sylvain Leroux

Some other answer have already pointed out the tracebackmodule.

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

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

...将显示最后一个异常的回溯:

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 tracebackone solution is to cache the exception infosas returned from exc_infoin 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:

    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 reclaimedwhen garbage collection is enabled and they become unreachable, but it remains more efficient to avoid creating cycles.

  • 从文档sys_info

    将回溯返回值分配给正在处理异常的函数中的局部变量将导致循环引用。这将防止同一函数中的局部变量或回溯引用的任何内容被垃圾收集。[...]如果您确实需要回溯,请确保在使用后将其删除(最好使用 try ... finally 语句完成)

  • 但是,来自同一个文档:

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



On the other hand, by allowing you to access the traceback associated withan 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!")

回答by Ivo van der Wijk

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 中失败的语句包装在最内部的循环中。

回答by nmichaels

You want the tracebackmodule. It will let you print stack dumps like Python normally does. In particular, the print_lastfunction will print the last exception and a stack trace.

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

回答by volting

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

traceback.format_exc()或者sys.exc_info()如果这是您想要的,则会产生更多信息。

import traceback
import sys

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

回答by dimo414

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.

无需手动引发异常只是为了再次捕获它。

回答by Aaron Hall

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 tracebackmodule from the standard library:

为了提取完整的回溯,我们将使用traceback标准库中的模块:

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 printthe full traceback, use the traceback.print_excmethod:

打印完整的回溯,请使用以下traceback.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.exceptionfunction instead:

在这种情况下,您将需要该logger.exception函数:

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_excfunction instead:

或者您可能只想要字符串,在这种情况下,您将需要traceback.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!

回答by Edward Newell

To get the precisestack trace, as a string, that wouldhave 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_funcis defined, and logcalls your favorite logging system):

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

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 sysand tracebackmodules.

捕获并重新提高KeyboardInterrupts是个好主意,这样您仍然可以使用 Ctrl-C 终止程序。日志记录超出了问题的范围,但一个不错的选择是loggingsystraceback模块的文档。

回答by Mark McDonald

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 的回答之外,如果您正在记录,但不想使用logging.exception()(因为它记录在 ERROR 级别),您可以使用较低的级别并通过exc_info=True。例如

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

回答by Basj

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 hellois 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).

关于这个答案的评论的评论:print(traceback.format_exc())对我来说比traceback.print_exc(). 对于后者,hello有时会与回溯文本奇怪地“混合”,就像两者都想同时写入 stdout 或 stderr,产生奇怪的输出(至少在从文本编辑器内部构建并查看输出时) “构建结果”面板)。

Traceback (most recent call last):
File "C:\Users\User\Desktop\test.py", line 7, in
helldo_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]

回溯(最近一次调用):
文件“C:\Users\User\Desktop\test.py”,第 7 行,在
地狱do_stuff()
文件“C:\Users\User\Desktop\test.py”,第 4 行, 在 do_stuff
1/0
ZeroDivisionError: 整数除法或模数为零
o
[在 0.1 秒内完成]

So I use:

所以我使用:

import traceback, sys

def do_stuff():
    1/0

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

回答by tosh

First, don't use prints for logging, there is astable, proven and well-thought out stdlib module to do that: logging. You definitely shoulduse it instead.

首先,不要用printS表示日志记录,有非稳态,证明和深思熟虑的STDLIB模块,这样做:logging。你绝对应该改用它。

Second, don't be tempted to do a messwith unrelated tools when there is 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.exceptionis actually doing is just a call to log.error(that is, log event with level ERROR) andprint traceback then.

什么log.exception是真正做只是为了通话log.error(即记录与级别的事件ERROR,并打印回溯然后。

Why is it better?

为什么更好?

Well, here is some considerations:

好吧,这里有一些注意事项:

  • it is just right;
  • it is straightforward;
  • it is simple.
  • 它仅仅是正确的;
  • 它很简单;
  • 很简单。

Why should nobody use tracebackor call logger with exc_info=Trueor get their hands dirty with sys.exc_info?

为什么没有人应该使用traceback或调用记录器exc_info=True或弄脏他们的手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.

嗯,只是因为!它们都出于不同的目的而存在。例如,traceback.print_exc的输出与解释器本身产生的回溯略有不同。如果你使用它,你会迷惑任何阅读你日志的人,他们会用头撞他们。

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

传递exc_info=True到日志调用是不合适的。但是,当捕获可恢复的错误并且您还想INFO用回溯记录它们(使用例如级别)时,它很有用,因为log.exception只生成一个级别的日志 - ERROR

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

而且你绝对应该尽量避免搞砸sys.exc_info。它不是一个公共接口,而是一个内部接口——如果您确定自己在做什么,就可以使用它。它不仅仅用于打印异常。