您如何导致未捕获的异常通过logging
模块而不是通过模块输出stderr
?
我意识到最好的方法是:
try:
raise Exception, 'Throwing a boring exception'
except Exception, e:
logging.exception(e)
但是我的情况是,如果在没有捕获到异常的情况下自动调用它,那将非常好logging.exception(...)
。
您如何导致未捕获的异常通过logging
模块而不是通过模块输出stderr
?
我意识到最好的方法是:
try:
raise Exception, 'Throwing a boring exception'
except Exception, e:
logging.exception(e)
但是我的情况是,如果在没有捕获到异常的情况下自动调用它,那将非常好logging.exception(...)
。
Answers:
正如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
type
尽管IDE会抱怨隐藏全局type
(就像var self = this
在Javascript中使用),但您可以将其用作函数参数。除非您需要访问type
函数内部的对象,否则这并不重要,在这种情况下,您可以将其type_
用作参数。
sys.excepthook
“引发”异常时不调用。当程序由于未捕获的异常而终止时调用,该异常不能发生多次。
这是一个完整的小示例,其中还包括其他一些技巧:
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,但是您可以将相同样式的各种处理程序添加到logger对象。
logger.critical()
在excepthook处理程序中使用,因为未捕获的异常非常关键。
logging.basicConfig(level=logging.DEBUG, filename="debug.log", format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
但没有帮助。
sys.excepthook
如果未捕获到异常,则将调用该方法:http : //docs.python.org/library/sys.html#sys.excepthook
当引发并捕获异常时,解释器使用三个参数(异常类,异常实例和回溯对象)调用sys.excepthook。在交互式会话中,这恰好在控制权返回到提示之前发生。在Python程序中,这恰好在程序退出之前发生。可以通过为sys.excepthook分配另一个三参数函数来定制此类顶级异常的处理。
type
实例来获得该信息吗?
为什么不:
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:
在第一行的开头。
sys.stderr
。
以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
functools.partial()
而不是lambda。请参阅: docs.python.org/2/library/functools.html#functools.partial
将您的应用程序入口调用包装在一个try...except
块中,这样您就可以捕获和记录(甚至重新引发)所有未捕获的异常。例如:
if __name__ == '__main__':
main()
做这个:
if __name__ == '__main__':
try:
main()
except Exception as e:
logger.exception(e)
raise
尽管@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()
为了回答已接受答案的注释部分中讨论的宙斯先生的问题,我使用它在交互式控制台中记录未捕获的异常(已通过PyCharm 2018-2019测试)。我发现sys.excepthook
它在python shell中不起作用,因此我更深入地研究并发现可以使用它sys.exc_info
。但是,sys.exc_info
不像其他任何参数sys.excepthook
接受3个参数不同。
在这里,我同时使用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的答案中找到。
就我而言,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