在Python中记录未捕获的异常

时间:2011-06-04 02:53:22

标签: python logging exception-handling

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

我意识到最好的方法是:

try:
    raise Exception, 'Throwing a boring exception'
except Exception, e:
    logging.exception(e)

但是,如果没有捕获到异常,如果logging.exception(...)被自动调用,那么我的情况就是非常好

10 个答案:

答案 0 :(得分:131)

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

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")
  • 忽略KeyboardInterrupt,以便控制台python程序可以使用Ctrl + C退出。

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

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

答案 1 :(得分:118)

正如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
... 

覆盖sys.excepthook

>>> sys.excepthook = foo

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

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

有关sys.excepthookhttp://docs.python.org/library/sys.html#sys.excepthook

的详细信息

答案 2 :(得分:25)

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

  

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

答案 3 :(得分:17)

为什么不:

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

以上是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

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

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

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

答案 4 :(得分: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

答案 5 :(得分:4)

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

if __name__ == '__main__':
    main()

这样做:

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logger.exception(e)
        raise

答案 6 :(得分:3)

也许你可以在模块的顶部做一些事情,将stderr重定向到一个文件,然后在底部登录该文件

sock = open('error.log', 'w')               
sys.stderr = sock

doSomething() #makes errors and they will log to error.log

logging.exception(open('error.log', 'r').read() )

答案 7 :(得分:2)

尽管@ 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()

答案 8 :(得分:1)

要回答已接受答案的注释部分中讨论的宙斯先生的问题,我将其用于在交互式控制台中记录未捕获的异常(已通过PyCharm 2018-2019测试)。我发现sys.excepthook在python shell中不起作用,因此我更深入地了解了我可以改用sys.exc_info。但是,sys.exc_info不接受任何参数,而sys.excepthook不接受3个参数。

在这里,我同时使用sys.excepthooksys.exc_info来在交互式控制台和带有包装函数的脚本中记录这两个异常。要将挂钩函数附加到这两个函数,我有两个不同的接口,具体取决于是否给定参数。

代码如下:

def log_exception(exctype, value, traceback):
    logger.error("Uncaught exception occurred!",
                 exc_info=(exctype, value, traceback))


def attach_hook(hook_func, run_func):
    def inner(*args, **kwargs):
        if not (args or kwargs):
            # This condition is for sys.exc_info
            local_args = run_func()
            hook_func(*local_args)
        else:
            # This condition is for sys.excepthook
            hook_func(*args, **kwargs)
        return run_func(*args, **kwargs)
    return inner


sys.exc_info = attach_hook(log_exception, sys.exc_info)
sys.excepthook = attach_hook(log_exception, sys.excepthook)

可以在gnu_lorien的答案中找到日志记录设置。

答案 9 :(得分:0)

就我而言,在使用@Jacinda的答案时(使用python 3)没有打印回溯的内容。而是仅打印对象本身:<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