对于Flask web框架,大家可能会遇到一些问题,运行的服务层函数如果有异常,不会直接返回具体错误,而是返回框架通用错误,这给开发人员排查问题带来很大困难,今天我就给大家带来一个简单的通用异常检测装饰器,让开发人员直击问题根源,提高开发效率,使bug无处可藏,另外还可以记录每个函数运行时间和展示几十种常用的异常描述。
#!/usr/bin/env python3
# coding=utf-8
__Author__ = 'Hanson'
import time
import traceback
from functools import wraps
from flask import Flask, make_response, jsonify, request
from loguru import logger
logger.add("logs/loguru.log", rotation="100KB", compression="zip", encoding="utf-8")
app = Flask(__name__)
exception_dict = {
"AssertionError": "断言语句(assert)失败",
"AttributeError": "尝试访问未知的对象属性",
"EOFError": "用户输入文件末尾标志EOF(Ctrl+d)",
"FloatingPointError": "浮点计算错误",
"GeneratorExit": "generator.close()方法被调用的时候",
"ImportError": "导入模块失败的时候",
"IndexError": "索引超出序列的范围",
"KeyError": "字典中查找一个不存在的关键字",
"KeyboardInterrupt": "用户输入中断键(Ctrl+c",
"MemoryError": "内存溢出(可通过删除对象释放内存)",
"NameError": "尝试访问一个不存在的变量",
"NotImplementedError": "尚未实现的方法",
"OSError": "操作系统产生的异常(例如打开一个不存在的文件)",
"OverflowError": "数值运算超出最大限制",
"ReferenceError": "弱引用(weak reference)试图访问一个已经被垃圾回收机制回收了的对象",
"RuntimeError": "一般的运行时错误",
"StopIteration": "迭代器没有更多的值",
"SyntaxError": "Python的语法错误",
"IndentationError": "缩进错误",
"TabError": "Tab和空格混合使用",
"SystemError": "Python编译器系统错误",
"SystemExit": "Python编译器进程被关闭",
"TypeError": "不同类型间的无效操作",
"UnboundLocalError": "访问一个未初始化的本地变量(NameError的子类)",
"UnicodeError": "Unicode相关的错误(ValueError的子类)",
"UnicodeEncodeError": "Unicode编码时的错误(UnicodeError的子类)",
"UnicodeDecodeError": "Unicode解码时的错误(UnicodeError的子类)",
"UnicodeTranslateError": "Unicode转换时的错误(UnicodeError的子类)",
"ValueError": "传入无效的参数",
"ZeroDivisionError": "除数为零",
}
def get_res_message(message, status_code, content=None):
# 异常处理信息函数,默认content为空
res_data = {
"code": status_code,
"message": message,
"data": content,
}
res = jsonify(res_data)
res.headers['date'] = time.strftime("%Y-%m-%d %H:%M:%S %a UTF", time.localtime())
return res
# 异常检测和程序运行时间记录
def check_exception_time(f):
@wraps(f)
def check(*args, **kwargs):
try:
# return f(*args, **kwargs)
timestamp_start = float(time.time())
res = f(*args, **kwargs)
timestamp_end = float(time.time())
cost_timestamp = timestamp_end - timestamp_start
# cost_time = get_duration(cost_timestamp)
logger.info("[%s]运行时间:[%s]秒" % (f.__name__, round(cost_timestamp, 2)))
return res
except Exception as e:
# 以下是异常简要
logger.info("未知异常简要:" + str(e))
# 此处需要用到traceback模块捕获具体异常,以便展示具体的错误位置。以下是格式化后的具体异常
exception_desc = "未知异常"
for i in exception_dict:
# print(i, exception_dict[i])
if i in str(traceback.format_exc()):
exception_desc = "参考异常:" + exception_dict[i]
logger.info("未知异常具体:" + str(traceback.format_exc()))
# 以下字典格式不能http返回,所以需要转换成str,提前将traceback.format_exc()也转换成str,否则返回字符串没有引号
res_data = {
"code": 400,
"message": exception_desc,
"data": str(traceback.format_exc()),
}
return make_response(str(res_data))
return check
@app.route('/test', methods=['GET'])
# @check_exception_time
def test():
a = request.args.get('a', 1)
b = request.args.get('b', 2)
c = (int(a) + int(b)) / int(b)
list1 = [1, 2, 3]
dict1 = {"name": "hason"}
print(list1[2])
print(dict1['name'])
return get_res_message("返回信息", 200, c)
if __name__ == '__main__':
app.run()
用postman做测试:
就返回500错误,具体哪里错误不知道?
查看一下控制台输出:
此处可以看到异常,但是对于一些flask封装的restplus api的框架,这些错误控制台也不能返回,这就给后端api开发带来了很大麻烦。
将注释去掉:# @check_exception_time,加上本人写的装饰器,查看效果:
看到了吧?直击代码有错误的部分,进行错误展示,如果异常错误类型在异常描述字典当中,还会展示简要异常,方便吧?
另外,只要稍加修改,django,tornado等web框架和其他非web框架的python项目也适用。