在Python中记录未捕获的异常

262 投票
10 回答
102406 浏览
提问于 2025-04-16 18:56

如何让未处理的错误通过 logging 模块输出,而不是直接输出到 stderr 呢?

我知道最好的方法是:

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

但是我的情况是,如果每当出现未处理的错误时,能够自动调用 logging.exception(...) 那就太好了。

10 个回答

38

为什么不这样做:

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:

285

这里有一个完整的小例子,还包含了一些其他的小技巧:

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)。你还可以在这个日志记录器对象中添加各种不同风格的处理器。

178

正如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.excepthook的信息,可以查看官方文档

撰写回答