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

我正在编写一个程序,该程序可以解析10个网站,找到数据文件,保存文件,然后解析它们以生成可以在NumPy库中轻松使用的数据。 有过不良链接,不好的XML,缺项,其他的事情我还没有进行分类文件遭遇的错误。 我最初制作该程序来处理以下错误:

try:
    do_stuff()
except:
    pass

但是现在我想记录错误:

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

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


#1楼

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

请注意,使用print_exc ,在某些print_exc情况下,您将无法获得预期的结果。 在Python 2.x中:

import traceback

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

    traceback.print_exc()

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

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

如果您确实需要访问原始的追溯,一种解决方案是将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

生产:

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

很少有这个陷阱:

  • sys_info的文档中:

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

  • 但是,根据同一文档:

    从Python 2.2开始,启用垃圾收集并使其无法访问时, 会自动回收此类循环 ,但是避免创建循环仍然更加有效。


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

import traceback

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

    traceback.print_tb(err.__traceback__)

...将显示:

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

#2楼

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

traceback.print_stack()

无需为了再次捕获而手动引发异常。


#3楼

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

当您不想因错误而暂停程序时,需要使用try / except处理该错误:

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

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

import traceback

并创建一个相当复杂的堆栈跟踪以演示我们获得了完整的堆栈跟踪:

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

def do_something_that_might_error():
    raise_error()

列印

打印完整的回溯,请使用traceback.print_exc方法:

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

哪些打印:

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!

比打印,记录更好:

但是,最佳实践是为模块设置一个记录器。 它将知道模块的名称,并能够更改级别(在其他属性中,例如处理程序)

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

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

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

哪个日志:

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!

或者,也许您只需要字符串,在这种情况下,您将需要traceback.format_exc函数:

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

哪个日志:

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!

结论

对于这三个选项,我们看到的输出与发生错误时的输出相同:

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

#4楼

为了获得精确的堆栈跟踪,作为一个字符串, 本来如果没有尝试/除非在那里步过它,只是把这个在除块捕获违规的异常上升。

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

这是使用方法(假设已定义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)

捕获并重新引发KeyboardInterrupt是一个好主意,这样您仍然可以使用Ctrl-C终止程序。 日志记录不在问题的范围内,但是很好的选择是loggingsystraceback模块的文档。


#5楼

您需要将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

... 等等

换句话说,您需要将可能在try / except中失败的语句包装在尽可能多的内部循环中,并尽可能不具体。


#6楼

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


#7楼

如果那是您想要的, 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])

#8楼

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

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

#9楼

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

追溯(最近一次通话):
在第7行的文件“ C:\\ Users \\ User \\ Desktop \\ test.py”
地狱 do_stuff()
文件“ C:\\ Users \\ User \\ Desktop \\ test.py”,行4,位于do_stuff中
1/0
ZeroDivisionError:整数除法或以零为模
Ø
[以0.1秒完成]

所以我用:

import traceback, sys

def do_stuff():
    1/0

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

#10楼

首先,不要使用print进行日志记录,有一个稳定,经过验证且经过深思熟虑的stdlib模块可以做到这一点: logging 。 您绝对应该改用它。

其次,当存在本机且简单的方法时,不要试图将无关的工具弄得一团糟 。 这里是:

log = logging.getLogger(__name__)

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

而已。 现在完成了。

对任何对引擎盖如何工作感兴趣的人的解释

log.exception实际在执行的只是对log.error的调用(即,级别为ERROR日志事件) 然后打印回溯。

为什么会更好?

好,这是一些注意事项:

  • 这是正确的 ;
  • 这很简单;
  • 很简单。

为什么没人应该使用traceback或用exc_info=True调用记录器,或者不使用sys.exc_info弄脏他们的手?

好吧,只是因为! 它们全都出于不同的目的而存在。 例如, traceback.print_exc的输出与解释器本身产生的追溯有些不同。 如果您使用它,将会使任何人阅读您的日志感到困惑,他们会将头撞向他们。

传递exc_info=True进行日志调用是不合适的。 但是 ,捉可恢复的错误时,它是有用的,要记录他们(例如使用INFO等级)与回溯为好,因为log.exception只产生一个级别的日志- ERROR

而且,您绝对应该尽可能避免使sys.exc_info混乱。 它不是一个公共接口,而是一个内部接口-如果您完全知道自己在做什么,就可以使用它。 它不仅仅用于打印例外。


#11楼

获得完整回溯从与异常对象的字符串traceback.format_exception

如果只有异常对象,则可以使用以下命令从Python 3中的代码的任何点以字符串形式获取跟踪:

import traceback

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

完整示例:

#!/usr/bin/env python3

import traceback

def f():
    g()

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

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

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

输出:

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

文档: https : //docs.python.org/3.7/library/traceback.html#traceback.format_exception

另请参阅: 从异常对象中提取回溯信息

在Python 3.7.3中测试。


#12楼

我在其他任何答案中都没有提到这一点。 如果出于任何原因要传递Exception对象...

在Python 3.5+中,您可以使用traceback.TracebackException.from_exception()从Exception对象获取跟踪 。 例如:

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

但是,以上代码导致:

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

这只是堆栈的两个级别,与如果在stack_lvl_2()引发异常并且未拦截该异常(取消注释# raise行)相比,在屏幕上所打印的内容相反。

据我了解,这是因为异常在引发时仅记录堆栈的当前级别,在这种情况下为stack_lvl_3() 。 随着它在堆栈中的传递,它的__traceback__被添加了更多的层次。 但是我们在stack_lvl_2()截获了它,这意味着它要记录的仅是3级和2级。要获得在stdout上打印的完整跟踪,我们必须在最高(最低?)级捕获它:

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

结果是:

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

注意,堆栈打印不同,缺少第一行和最后一行。 因为它是不同的format()

截取该异常离生成点越远越好,这可以简化代码,同时提供更多信息。


#13楼

一个班轮:

import traceback
traceback.print_exc()

使用traceback模块,该模块在以下内容中自动获取当前异常except:

https://stackoverflow.com/a/9555145/2217801


#14楼

如果您已经有一个Error对象,并且想要打印整个内容,则需要进行此稍显尴尬的调用:

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

没错, print_exception具有三个位置参数:异常的类型,实际的异常对象以及异常自己的内部回溯属性。

在python 3.5或更高版本中, type(err)是可选的...但是它是一个位置参数,因此您仍然必须在其位置显式传递None。

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

我不知道为什么所有这些都不只是traceback.print_exception(err) 。 为什么您要打印出错误以及与该错误无关的回溯,这超出了我的范围。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值