我有一个警报服务,遇到异常会通过警报服务钉钉通知我,因为多层函数嵌套调用,某个子函数可能有异常,但是该异常又不会影响最后的程序执行,我只是希望能得到通知,于是有了下面这样的一个装饰器
def fault_tolerant(error_return: Any = None, send_alert: bool = True):
"""[装饰器]
容错机制,捕获错误,发送警报消息
Args:
func (function): 函数体
"""
def decorator_function(func):
@wraps(func)
def wrapper_function(*args, **kw):
try:
return func(*args, **kw)
except Exception as e:
if send_alert:
alert(e)
return error_return
return wrapper_function
return decorator_function
以上装饰器支持异常捕获,遇到异常可以返回默认值error_return,如果send_alert=True则发出警报通知
然后我的服务如下demo
from util import fault_tolerant
def task(msg: dict = {}):
print(msg)
fun1(msg['a'])
def fun1(a):
print(a)
fun2(a + 1)
def fun2(b):
print(b)
c = foo(1, 0)
print(c)
@fault_tolerant(0)
def foo(x, y):
return x / y
if __name__ == '__main__':
task({'a': 1, 'b': 2})
这个时候从装饰器里想知道最初调用传入的msg是什么该怎么办呢?
仅从装饰器中只能获取到被装饰函数的传参而已,为了获取最初调用的传参我们就要用到traceback来实现,灵感来源于loguru的logger.exception(e),如下图:
那以上这些信息是怎么获取到的呢? 就是利用了traceback来实现的,回溯函数如下:
# loguru/_get_frame.py
import sys
from sys import exc_info
def get_frame_fallback(n):
try:
raise Exception
except Exception:
frame = exc_info()[2].tb_frame.f_back
for _ in range(n):
frame = frame.f_back
return frame
if hasattr(sys, "_getframe"):
get_frame = sys._getframe
else:
get_frame = get_frame_fallback
因为我们只是关注最初调用的task函数中的msg传参,于是我封装了以下函数用于获取该传参
def get_task_msg_fallback(function_name: str = 'task', key: str = 'msg', default: Any = {}) -> Any:
try:
raise Exception
except Exception:
frame = sys.exc_info()[2].tb_frame.f_back
while frame:
if function_name in frame.__str__():
return frame.f_locals.get(key, default)
frame = frame.f_back
return default
finally:
del frame
然后我们的装饰器就可以获取最初传参数据了
def fault_tolerant(error_return: Any = None, send_alert: bool = True):
"""[装饰器]
容错机制,捕获错误,发送警报消息
Args:
func (function): 函数体
"""
def decorator_function(func):
@wraps(func)
def wrapper_function(*args, **kw):
try:
return func(*args, **kw)
except Exception as e:
task_msg = get_task_msg_fallback()
print(task_msg)
if send_alert:
alert(e, task_msg=task_msg)
return error_return
return wrapper_function
return decorator_function
警报消息也带上了task_msg参数信息了