首页 文章

在Python中记录未捕获的异常

提问于
浏览
129

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

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

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

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

8 回答

  • 115

    为什么不:

    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: .

  • 3

    以Jacinda的答案为基础,但使用 Logger 对象:

    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
    
  • 6

    将您的应用程序条目调用包装在 try...except 块中,这样您就可以捕获并记录(并可能重新引发)所有未捕获的异常 . 例如 . 代替:

    if __name__ == '__main__':
        main()
    

    做这个:

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

    正如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 的更多信息:http://docs.python.org/library/sys.html#sys.excepthook

  • 15

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

    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的日志记录模块来格式化异常 .

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

  • 4

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

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

  • 2

    也许你可以在模块的顶部做一些事情,将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() )
    
  • 24

    虽然@ 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()
    

相关问题