在 Python 中记录未捕获的异常
- 2025-01-21 09:01:00
- admin 原创
- 87
问题描述:
如何使未捕获的异常通过logging
模块而不是输出stderr
?
我意识到做到这一点的最佳方式是:
try:
raise Exception, 'Throwing a boring exception'
except Exception, e:
logging.exception(e)
但我的情况是这样的,如果在未捕获到异常时自动调用它,那就太好了。logging.exception(...)
解决方案 1:
这是一个完整的小例子,其中还包含一些其他技巧:
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,但您可以向记录器对象添加相同样式的各种处理程序。
解决方案 2:
正如 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
,请阅读文档。
解决方案 3:
为什么不:
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:
sys.excepthook
如果未捕获异常,则将调用该方法: http://docs.python.org/library/sys.html#sys.excepthook
当引发异常但未被捕获时,解释器会使用三个参数调用 sys.excepthook,即异常类、异常实例和回溯对象。在交互式会话中,这会在控制权返回提示符之前发生;在 Python 程序中,这会在程序退出之前发生。可以通过将另一个三参数函数分配给 sys.excepthook 来自定义此类顶级异常的处理。
解决方案 5:
在我的情况下 (使用python 3
),当使用@Jacinda 的答案时,不会打印回溯的内容。相反,它只打印对象本身:<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
解决方案 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
解决方案 7:
尽管@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:
将应用程序入口调用包装在一个try...except
块中,这样您就可以捕获并记录(并可能重新引发)所有未捕获的异常。例如,而不是:
if __name__ == '__main__':
main()
这样做:
if __name__ == '__main__':
try:
main()
except Exception as e:
logger.exception(e)
raise
解决方案 9:
为了回答 Mr.Zeus 在已接受答案的评论部分中讨论的问题,我使用它在交互式控制台中记录未捕获的异常(使用 PyCharm 2018-2019 测试)。我发现sys.excepthook
在 python shell 中不起作用,所以我深入研究并发现我可以使用它sys.exc_info
。但是,与需要 3 个参数sys.exc_info
不同,它不带参数sys.excepthook
。
在这里,我使用sys.excepthook
和sys.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 的回答中找到日志设置。
解决方案 10:
也许你可以在模块顶部做一些事情,将 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() )