python装饰器应用 一行代码为你的函数增加日志服务

1 什么是装饰器?

Python修饰器基于闭包原理,是赋予已存在的函数对象额外定制化功能的设计模式,使得代码简洁且易于维护,常用于插入日志、性能测试、权限校验等需求场景。例如某个具有登录功能的函数,现在希望添加数字签证核验功能,即可使用修饰器处理登录函数而无需变更登录函数的底层。Python语法糖@定义了修饰器

2 最通用的例子

def app(func):
    def warp(*args, **kwargs):
        print("正在执行功能1")
        result = func(*args, **kwargs)
        print("正在执行功能2")
        return result
    return warp

@app
def add(a, b):
    return a + b

print(add(1,3))
# 正在执行功能1
# 正在执行功能2
# 4

*args**kwargs用于接收被修饰函数的入口参数,*args、**kwargs分别用于将参数打包成tuple、dict给函数体调用。从上面可以看出,在不修改底层add函数的情况下,可以定制新功能。

3 实现日志打印修饰器

根据修饰器的原理,对上面的例子做小改动如下:

import logging

def log(func):
    def warp(*args, **kwargs):
        try:
            result = func(*args, **kwargs)
            return result
        except Exception as e:
            logging.error(e)
    return warp

这里使用try-except结构可以即使捕获异常信息输出到日志文件。

在某些应用场景,上面的结构还可以改进,比如Web服务可能要在输出日志后给前端反馈一个404代码,因此在增加一个定制化的返回内容err

def logger(err):
    def log(func):
        def warp(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
                return result
            except Exception as e:
                logging.error(e)
                return err
        return warp
    return log

4 配置logging输出格式

import logging, os

'''
* %(asctime)s   即日志记录时间,精确到毫秒
* %(levelname)s 即此条日志级别 
* %(filename)s  即触发日志记录的python文件名
* %(funcName)s  即触发日志记录的函数名
* %(lineno)s    即触发日志记录代码的行号
* %(message)s   即这项调用中的参数
'''
if not os.path.exists('Log.log'):
    file = open('Log.log','w')

logging.basicConfig(
    filename='Log.log',
    format="%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s"
)

5 测试

@logger('加法环节出错了!')
def add(a, b):
	return a + b

# 正常情况
print(add(1, 2))		# 输出3
# 异常情况
print(add(1, '2'))		# 输出“加法环节出错了!”

# Log.log中的记录
2021-10-22 21:34:54,736 - ERROR - test.py - warp - 51 - unsupported operand type(s) for +: 'int' and 'str'

于是只要把上面说的logger函数封装成单独一个文件,在其他位置import logger并用修饰器修饰你希望增加日志功能的函数即可,非常方便

6 附:完整代码

import logging, os

'''
* %(asctime)s   即日志记录时间,精确到毫秒@breif: 
* %(levelname)s 即此条日志级别@param[in]: 
* %(filename)s  即触发日志记录的python文件名@retval: 
* %(funcName)s  即触发日志记录的函数名
* %(lineno)s    即触发日志记录代码的行号
* %(message)s   即这项调用中的参数
'''
if not os.path.exists('Log.log'):
    file = open('Log.log','w')

logging.basicConfig(
    filename='Log.log',
    format="%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s"
)

'''
* @breif: 日志修饰器,为函数添加日志记录服务
* @param[in]: err -> 发生异常时返回的错误信息
* @retval: 加载日志服务的功能函数
'''
def logger(err):
    def log(func):
        def warp(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
                return result
            except Exception as e:
                logging.error(e)
                return err
        return warp
    return log

@logger('出错了')
def add(a, b):
    return a+b

print(add(1,'2'))

猜你喜欢

转载自blog.csdn.net/FRIGIDWINTER/article/details/120913456