在Python中记录未捕获的异常

Jac*_*ble 157 python logging exception-handling

如何通过logging模块而不是通过模块输出未捕获的异常stderr

我意识到这样做的最好方法是:

try:
    raise Exception, 'Throwing a boring exception'
except Exception, e:
    logging.exception(e)
Run Code Online (Sandbox Code Playgroud)

但是我的情况是如果在没有捕获到异常的情况下自动调用它会非常好logging.exception(...).

gnu*_*ien 155

这是一个完整的小例子,其中还包括一些其他技巧:

import sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

if __name__ == "__main__":
    raise RuntimeError("Test unhandled")
Run Code Online (Sandbox Code Playgroud)
  • 忽略KeyboardInterrupt,这样一个控制台python程序可以用Ctrl + C退出.

  • 完全依靠python的日志记录模块来格式化异常.

  • 使用带有示例处理程序的自定义记录器.这个更改未处理的异常以转到stdout而不是stderr,但是您可以将相同样式的各种处理程序添加到logger对象.

  • 为`logging`内置kwarg`exc_info` +1 (9认同)
  • 我会在excepthook处理程序中使用`logger.critical()`,因为未说明的异常非常关键. (9认同)
  • @gnu_lorien 值得注意的是,如果导入的模块引发异常,它可能不会使用新的 sys.excepthook (tkinter 是这种行为的一个著名示例)。当模块定义自己的 excepthook 时,就会发生这种情况。在这些情况下,您需要覆盖此 excepthook(对于 tkinter,请参阅 /sf/answers/3080308941/) (5认同)
  • @ChrisCollett“好的”python 模块旨在用作库,其设计/实现/测试不会有不可预测/不直观的行为(更改全局 excepthook 是“好的库”python 模块在我看来应该避免的事情)。 (4认同)
  • 这是IMO最实用的答案。 (2认同)

Jac*_*nda 132

正如Ned指出的那样,sys.excepthook每次引发异常并且未被捕获时都会调用它.这样做的实际意义是,在您的代码中,您可以覆盖默认行为,sys.excepthook以执行您想要的任何操作(包括使用logging.exception).

作为一个稻草人的例子:

>>> import sys
>>> def foo(exctype, value, tb):
...     print 'My Error Information'
...     print 'Type:', exctype
...     print 'Value:', value
...     print 'Traceback:', tb
... 
Run Code Online (Sandbox Code Playgroud)

覆盖sys.excepthook:

>>> sys.excepthook = foo
Run Code Online (Sandbox Code Playgroud)

提交明显的语法错误(省略冒号)并获取自定义错误信息:

>>> def bar(a, b)
My Error Information
Type: <type 'exceptions.SyntaxError'>
Value: invalid syntax (<stdin>, line 1)
Traceback: None
Run Code Online (Sandbox Code Playgroud)

有关以下内容的更多信息sys.excepthook:http://docs.python.org/library/sys.html#sys.excepthook

  • @Codemonkey它不是保留的关键字,它是一个预先存在的类型名称.你可以使用`type`作为函数参数,虽然IDE会抱怨隐藏全局`type`(很像在Javascript中使用`var self = this`).除非你需要访问函数内部的`type`对象,否则它并不重要,在这种情况下你可以使用`type_`作为参数. (4认同)
  • 短语*"每次"*这里都是误导性的::*"每次**都会调用sys.excepthook**引发异常并且未被捕获"*...因为在程序中,*可以是*****一个"未被捕获"的例外.此外,当"引发"异常时,不会调用`sys.excepthook`.当程序由于未捕获的异常而终止时调用它,这不会发生多次. (3认同)
  • @Nawaz:在 REPL 中它可以发生不止一次 (3认同)
  • @Nawaz 如果程序使用线程,它也可能发生多次。我也似乎 GUI 事件循环(如 Qt)继续运行,即使异常已进入 sys.excepthook (3认同)
  • 任何尝试测试上述代码的人,请确保在测试函数时生成回溯错误。SyntaxError 未由 sys.excepthook 处理。您可以使用 print(1/0) ,这将调用您定义的函数来覆盖 sys.excepthook (3认同)

Ned*_*der 26

sys.excepthook如果未捕获异常,则将调用该方法:http://docs.python.org/library/sys.html#sys.excepthook

当引发异常并且未被捕获时,解释器使用三个参数调用sys.excepthook,异常类,异常实例和回溯对象.在交互式会话中,这发生在控制返回到提示之前; 在Python程序中,这发生在程序退出之前.可以通过为sys.excepthook分配另一个三参数函数来自定义这种顶级异常的处理.

  • 为什么发送异常类?难道你不能总是通过在实例上调用`type`来获得它吗? (2认同)

Tia*_*nho 20

为什么不:

import sys
import logging
import traceback

def log_except_hook(*exc_info):
    text = "".join(traceback.format_exception(*exc_info))
    logging.error("Unhandled exception: %s", text)

sys.excepthook = log_except_hook

None()
Run Code Online (Sandbox Code Playgroud)

sys.excepthook如上所示,输出如下:

$ python tb.py
ERROR:root:Unhandled exception: Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable
Run Code Online (Sandbox Code Playgroud)

以下是sys.excepthook注释掉的输出:

$ python tb.py
Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable
Run Code Online (Sandbox Code Playgroud)

唯一的区别是前者ERROR:root:Unhandled exception:在第一行的开头.

  • sys.excepthook 中的错误:回溯(最近一次调用):文件“e:/Programing/pump/core2/SiteManager.py”,第 16515 行,在 log_ except_hook 文本 =“”.join(traceback.format_exception(*exc_info()) ))类型错误:“元组”对象不可调用 (2认同)

veu*_*ent 9

就我而言(使用python 3),当使用 @Jacinda 的答案时,回溯的内容未打印。相反,它只是打印对象本身:<traceback object at 0x7f90299b7b90>

相反,我这样做:

import sys
import logging
import traceback

def custom_excepthook(exc_type, exc_value, exc_traceback):
    # Do not print exception when user cancels the program
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logging.error("An uncaught exception occurred:")
    logging.error("Type: %s", exc_type)
    logging.error("Value: %s", exc_value)

    if exc_traceback:
        format_exception = traceback.format_tb(exc_traceback)
        for line in format_exception:
            logging.error(repr(line))

sys.excepthook = custom_excepthook
Run Code Online (Sandbox Code Playgroud)

  • 你的答案,特别是 `traceback,format(tb(exc_traceback)` 帮助我解决了我对处理程序 `traceback` 和对象 `exc_traceback` 之间差异的缺失理解。我试图在对象上使用处理程序的方法,如 `exc_traceback .format_tb` 显然不起作用。一旦我知道如何使用处理程序和对象,它就打开了使用回溯的宇宙!谢谢! (3认同)

Mik*_*ike 6

以Jacinda的答案为基础,但使用记录器对象:

def catchException(logger, typ, value, traceback):
    logger.critical("My Error Information")
    logger.critical("Type: %s" % typ)
    logger.critical("Value: %s" % value)
    logger.critical("Traceback: %s" % traceback)

# Use a partially applied function
func = lambda typ, value, traceback: catchException(logger, typ, value, traceback)
sys.excepthook = func
Run Code Online (Sandbox Code Playgroud)

  • @MariuszJamro 为什么? (3认同)
  • 最好使用`functools.partial()`而不是lambda.请参阅:https://docs.python.org/2/library/functools.html#functools.partial (2认同)

gun*_*sus 6

尽管@gnu_lorien的回答给了我一个很好的起点,但我的程序在第一个异常时崩溃了。

我提供了一个定制的(和/或)改进的解决方案,它默默地记录用@handle_error.

import logging

__author__ = 'ahmed'
logging.basicConfig(filename='error.log', level=logging.DEBUG)


def handle_exception(exc_type, exc_value, exc_traceback):
    import sys
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logging.critical(exc_value.message, exc_info=(exc_type, exc_value, exc_traceback))


def handle_error(func):
    import sys

    def __inner(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception, e:
            exc_type, exc_value, exc_tb = sys.exc_info()
            handle_exception(exc_type, exc_value, exc_tb)
        finally:
            print(e.message)
    return __inner


@handle_error
def main():
    raise RuntimeError("RuntimeError")


if __name__ == "__main__":
    for _ in xrange(1, 20):
        main()
Run Code Online (Sandbox Code Playgroud)


fla*_*ovs 5

将您的应用程序入口调用包装在一个try...except块中,以便您能够捕获并记录(并且可能重新引发)所有未捕获的异常。例如,而不是:

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

做这个:

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logger.exception(e)
        raise
Run Code Online (Sandbox Code Playgroud)