向sys.excepthook添加功能


问题内容

假设我有类似的东西,它将未处理的异常发送到logging.critical()

import sys

def register_handler():
    orig_excepthook = sys.excepthook

    def error_catcher(*exc_info):
        import logging
        log = logging.getLogger(__name__)
        log.critical("Unhandled exception", exc_info=exc_info)
        orig_excepthook(*exc_info)

    sys.excepthook = error_catcher

有用:

import logging
logging.basicConfig()

register_handler()

undefined() # logs, then runs original excepthook

但是,如果register_handler()多次error_catcher调用,则会在一个链中调用多个,并且记录消息会出现多次。

我可以想到几种方法,但是它们都不是一种特别好的方法(例如检查sys.excepthookerror_catcher函数是否有效,或者在模块上使用“
have_registered”属性以避免重复注册)

有推荐的方法吗?


问题答案:

具有模块级别的“已注册钩子”变量似乎是最简单,最可靠的方法。

其他可能的解决方案在某些(而不是晦涩的)情况下sys.excepthook会失效-
如果应用程序注册了一个自定义项excepthook,则检查内置函数是否会失败,excepthook在函数定义时存储原始函数将破坏随后注册的钩子函数。

import sys

_hook_registered = False

def register_handler(force = False):
    global _hook_registered

    if _hook_registered and not force:
        return

    orig_excepthook = sys.excepthook

    def error_catcher(*exc_info):
        import logging
        log = logging.getLogger(__name__)
        log.critical("Unhandled exception", exc_info=exc_info)
        orig_excepthook(*exc_info)

    sys.excepthook = error_catcher

    _hook_registered = True