Python 日志-装饰器

参考文章:程序员小谭-自动化项目实战08:日志

插入脚本展示

在这里插入图片描述

代码实现

def write_case_log():
    def wrapper_func(func):
        @wraps(func) # 防止函数名称与注释文档被重写
        def inner_func(*args,**kwargs):
            try:
                logger1.info("{}开始执行".format(func.__name__))
                func(*args,**kwargs)
                logger1.info("{}执行中".format(func.__name__))
                logger1.info("{}结束执行".format(func.__name__))
            except Exception as msg:
                logger1.error('%s 执行失败' % (func.__name__))
                logger1.error(msg)
                logger1.error(traceback.format_exc())
        return inner_func
    return wrapper_func

实现结果

  • 控制台输出
  • 文件输出
  • 异常写入日志
  • 装饰器调用

在这里插入图片描述

疑问点

为何装饰器输入的时候,需要带入write_case_log(),使用的是函数返回结果,而不是write_case_log函数?
当将括号去掉,会提示:此函数不需要位置参数,但给了一个位置参数。
在这里插入图片描述
其实write_case_log()等同于直接使用warpper_func,此处只是原作者将其多嵌套了一层进行封装。
装饰器的执行,就是将目标函数名带入到装饰器函数中执行。

知识巩固

菜鸟教程-装饰器
1、@wraps(func) 具备复制函数名,注释文档等功能,防止原函数名被重写,可使用print(func_XX.__name__)检查
2、可通过多嵌套一层,实现带参数的装饰器

from functools import wraps
def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile,并写入内容
            with open(logfile, 'a') as opened_file:
                # 现在将日志打到指定的logfile
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
@logit(logfile='func2.log')
def myfunc2():
    pass
myfunc2()

3、装饰器类

装饰器类
现在我们有了能用于正式环境的logit装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。
幸运的是,类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建logit。

from functools import wraps
class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
 
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile并写入
            with open(self.logfile, 'a') as opened_file:
                # 现在将日志打到指定的文件
                opened_file.write(log_string + '\n')
            # 现在,发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不做别的
        pass
这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:

@logit()
def myfunc1():
    pass
现在,我们给 logit 创建子类,来添加 email 的功能(虽然 email 这个话题不会在这里展开)class email_logit(logit):
    '''
    一个logit的实现版本,可以在函数调用时发送email给管理员
    '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)
 
    def notify(self):
        # 发送一封email到self.email
        # 这里就不做实现了
        pass
从现在起,@email_logit 将会和 @logit 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。

完整脚本

import logging
from common import configContral
import time
import traceback
from functools import wraps

logConfig = configContral.logConfig
logName_logger = logConfig.logName_configContral
levels_logger = logConfig.levels_configContral
# 第一步,创建一个logger
logger1 = logging.getLogger()
if levels_logger.upper() == "DEBUG":
    logger1.setLevel(logging.DEBUG)
elif levels_logger.upper() == "INFO":
    logger1.setLevel(logging.INFO)
elif levels_logger.upper() == "WARNING":
    logger1.setLevel(logging.WARNING)
elif levels_logger.upper() == "ERROR":
    logger1.setLevel(logging.ERROR)
else:
    logger1.setLevel(logging.CRITICAL)

# 第二步,创建一个handler,用于写入日志文件
now = time.strftime("%Y-%m-%d", time.localtime(time.time()))
# 打开一个文件,将result写入此file中
logfile = logConfig.logPath_configContral + now + logName_logger

fileHandler = logging.FileHandler(logfile, encoding='utf-8')

# 第三步,创建一个handler,用于输出控制台
outputFile_handler = logging.StreamHandler()

# 第四步,定义handler的输出格式
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s %(message)s")
fileHandler.setFormatter(formatter)
outputFile_handler.setFormatter(formatter)

# 第五步,将logger添加到handler里面
logger1.addHandler(fileHandler)
logger1.addHandler(outputFile_handler)


def write_case_log():

    def wrapper_func(func):
        @wraps(func) # 防止函数名称与注释文档被重写
        def inner_func(*args,**kwargs):
            try:
                logger1.info("{}开始执行".format(func.__name__))
                func(*args,**kwargs)
                logger1.info("{}执行中".format(func.__name__))
                logger1.info("{}结束执行".format(func.__name__))
            except Exception as msg:
                logger1.error('%s 执行失败' % (func.__name__))
                logger1.error(msg)
                logger1.error(traceback.format_exc())
        return inner_func
    return wrapper_func

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值