[Python第三方库系列] - Better Exceptions
Better Exceptions是最近一期的Python Weekly 和Pycoders Weekly上都推荐的一个库,用处是展示更友好的异常信息。
无论在什么语言里遇到异常是很正常的,遇到线上异常也是常有的。本地异常的话通过pdb调试/print输出关键信息是可行的,但是对于线上异常的话,只能从日志里查看,但日志里的信息可能只是提示你:ValueError: need more than 3 values to unpack
或者是 KeyError: 'a'
,遇到这种问题,一般的做法是本地启动项目,尝试重现,这样你才能知道上下文是什么。但,往往很难复现,因为从日志里你看不到用户的输入是什么?如果你没有手动捕获异常,并把造成异常的数据写入log。(当然,最好的做法其实是使用Sentry)
上面的错误可以简化为这种形式:
# coding:utf-8
request = "test test test" # 从request中获取到数据
a, b, c, d = request.split() # 处理数据
# 运行后:
Traceback (most recent call last):
File "exc.py", line 8, in <module>
a, b, c, d = request.split()
ValueError: need more than 3 values to unpack
或者这种
# coding:utf-8
# 前端ajax传过来数据,后端处理
request_dict = {}
user_name = request_dict['user_name']
# 运行后:
Traceback (most recent call last):
File "exc.py", line 5, in <module>
user_name = request_dict['user_name']
KeyError: 'user_name'
后一种情况一个简单的场景就是,按照之前的逻辑,代码没问题,但是上线后,一直会报这个错误,为啥呢?谁也搞不清楚,只能去看用户到底发了什么数据,怎么看呢。把用户端发过来的数据打印出来?在生产环境这么做吗?(当然,最好的方法还是使用Sentry)
好了,这就是Better Exceptions出现的场景了。同样是上面的代码,增加 import better_exceptions
之后就是这样:
# coding:utf-8
import better_exceptions
request = "test test test" # 从request中获取到数据
a, b, c, d = request.split() # 处理数据
# 运行后:
Traceback (most recent call last):
File "exc.py", line 8, in <module>
a, b, c, d = request.split()
└ 'test test test' # 增加了内容
ValueError: need more than 3 values to unpack
或者这种
# coding:utf-8
import better_exceptions
# 前端ajax传过来数据,后端处理
request_dict = {}
user_name = request_dict['user_name']
# 运行后:
Traceback (most recent call last):
File "exc.py", line 5, in <module>
user_name = request_dict['user_name']
└ {}
KeyError: 'user_name'
Better Exceptions是怎么做到的呢
从上面的应用场景描述,以及最终呈现的结果来看,似乎是有用的。但是,对于Django和Tornado这样的Web框架,没啥用。为什么呢?
来看Better Exceptions的实现:
better_exceptions/__init__.py 最后几行代码
def excepthook(exc, value, tb):
formatted, colored_source = format_traceback(tb)
if not str(value) and exc is AssertionError:
value.args = (colored_source,)
title = traceback.format_exception_only(exc, value)
full_trace = u'Traceback (most recent call last):\n{}{}\n'.format(formatted, title[0].strip())
write_stream(full_trace)
sys.excepthook = excepthook # 关键是这个
主要是用了sys模块的excepthook方法,这个方法的描述如下:
ref: https://docs.python.org/2/library/sys.html#sys.excepthook
This function prints out a given traceback and exception to sys.stderr.
When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.
大概意思就是,如果系统抛出一个未捕获的异常,那么解释器就会调用sys.excepthook方法,同时传递三个参数:异常类(ValueError或者KeyError之类的),异常实例,和traceback对象。
这意味着,你可以通过重写这个方法来处理系统未捕获的异常处理。但,在框架中(我只看了Django代码,测试了Tornado)会自己处理异常,所以这种hook的方式不会被触发。
看到这里,可能会觉得,那这玩意有啥用呢?我能想到的场景是你自己写的爬虫,定时执行可以用的上,另外介绍这个库的另一个作用就是可以用来学习下Python中的几个模块,比如sys.excepthook, inspect, ast, TraceBack等模块的使用。
另外我上面也提到了两次Sentry,通过这个也可以看下,Sentry的Python客户端(Raven-Python)是怎么处理异常的。
这个库的安装直接pip就行:
pip install better-exceptions
- from the5fire.com微信公众号:Python程序员杂谈