【问题标题】:Adding function to sys.excepthook向 sys.excepthook 添加函数
【发布时间】:2023-03-23 09:57:01
【问题描述】:

假设我有这样的事情,它将未经处理的异常发送到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.excepthook是否是error_catcher函数,或者在模块上使用“have_registered”属性以避免重复注册)

有推荐的方法吗?

【问题讨论】:

    标签: python coding-style exception-handling


    【解决方案1】:

    您可以在注册处理程序之前检查sys.excepthook 是否仍然是内置函数:

    >>> import sys, types
    >>> isinstance(sys.excepthook, types.BuiltinFunctionType)
    True
    >>> sys.excepthook = lambda x: x
    >>> isinstance(sys.excepthook, types.BuiltinFunctionType)
    False
    

    【讨论】:

      【解决方案2】:

      如果将问题中的代码放入模块中,可以多次导入,但只会执行第一次。

      【讨论】:

        【解决方案3】:

        拥有一个模块级别的“已经注册了钩子”变量似乎是最简单、最可靠的方法。

        其他可能的解决方案在某些(相当模糊的)情况下会失败 - 如果应用程序注册了自定义 excepthook,则检查 sys.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
        

        【讨论】:

          【解决方案4】:

          如果您将orig_excepthook 设为具有默认值的参数,则默认值会在定义时固定一次。所以重复调用register_handler 不会改变orig_excepthook

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

          只调用一次log.critical

          【讨论】:

            【解决方案5】:

            你应该使用sys.__excepthook__[1]。
            它是一个对象,在程序开始时包含sys.excepthook[2] 的原始值。
            IE;

            import sys
            import logging
            
            def register_handler():
                log = logging.getLogger(__name__)
                def error_catcher(*exc_info):
                    log.critical("Unhandled exception", exc_info=exc_info)
                    sys.__excepthook__(*exc_info)
            
                sys.excepthook = error_catcher
            

            参考:
            1.https://docs.python.org/3/library/sys.html#sys.excepthook
            2.https://docs.python.org/3/library/sys.html#sys.excepthook

            【讨论】:

              猜你喜欢
              • 2013-05-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-24
              • 2017-08-26
              • 2017-02-11
              • 2019-09-23
              • 2017-11-01
              相关资源
              最近更新 更多