案例:
实现一个能将函数调用信息记录到日志的装饰器
需求:
- 把每次函数的调用时间,执行时间,调用次数写入日志
- 可以对被装饰函数分组,调用信息记录到不同日志
- 动态修改参数,比如日志格式
- 动态打开关闭日志输出功能
如何解决这个问题?
为了装饰器的灵活性,定义一个装饰类,把这个类的实例方法当做装饰器,在类中装饰器方法持有实例对象,便于修改属性和扩展功能
#!/usr/bin/python3 import logging from time import time, strftime, localtime, sleep from random import choice from functools import wraps class ToLog(): def __init__(self, name): log = logging.getLogger(name) log.setLevel(logging.INFO) # 日志保存文件名字 file_name = logging.FileHandler(name + '.log') # 添加日志文件 log.addHandler(file_name) # 日志格式 log.info('start'.center(50, '-')) self.log = log self.temp = '%(func)s -> [%(start_time)s - %(used_time)s - %(naclls)s]' def go_log(self, func): @wraps(func) def wrapper(*args, **kwargs): # 函数每调用一次加1 wrapper.naclls += 1 start_time = time() res = func(*args, **kwargs) used_time = time() - start_time info = {} info['func'] = func.__name__ info['start_time'] = start_time info['used_time'] = used_time info['naclls'] = wrapper.naclls msg = self.temp % info # 把日志按格式写入文件 self.log.info(msg) return res # 初始化调用次数参数 wrapper.naclls = 0 return wrapper # 重新定义日志记录模版 def set_log_temp(self, temp): self.temp = temp # 关闭日志功能 def log_off(self): self.log.setLevel(logging.WARN) # 打开日志功能 def log_on(self): self.log.setLevel(logging.INFO) # 实例化出两个装饰器对象 log_one = ToLog('one') log_two = ToLog('two') # 修改实例2的日志模版,去掉执行时间 log_two.set_log_temp('%(func)s -> [%(start_time)s - %(naclls)s]') # 关闭log_two中记录日志功能 log_two.log_off() @log_one.go_log def func_one(): print('one') @log_one.go_log def func_two(): print('two') @log_two.go_log def func_three(): print('three') if __name__ == '__main__': for _ in range(50): choice([func_one, func_two, func_three])() sleep(choice([0.5, 1, 1.5]))