python——记录一次制作日志装饰器

前言

  由于每次写代码的时候都需要写日志记录,就想着把它封装起来,做成装饰器,这样用着也方便,代码也简洁,所以就进入对python装饰器的研究中。

一、python装饰器的基本知识

  在这里也不进行太多概念的描述了,有问题可以直接百度查找相关概念。
  装饰器说白了就是将一个函数包装起来,在该函数的外面嵌套一层函数,将该函数装饰起来,做一些操作之后再返回该函数的运行结果,也就是将函数当参数传入到另一个函数中。如下代码可以很直观得看出,在目标函数之前打印”新添加的功能“

#最简单的装饰器了
def outer(func):
	def inner(*args, **kwargs):
		#do something
		print('新添加的功能')
		return func(*args, **kwargs)
	return inner	

  python装饰器分几种,分别是函数装饰器和类装饰器,再分为装饰类和装饰函数,在此也不多赘述,可以自行百度,本次只是想简单做一个记录而已。

二、逐步进行

第一步:制作一个在函数执行错误时,自动将错误写入文件保存起来

import time
import os
import logging
from functools import wraps

#先制作了一个带参数的日志logger函数
def setExceptionLogger(name):
    logger = logging.getLogger('exception')
    logger.setLevel(logging.INFO)
    if not os.path.exists('logs'):
        os.makedirs('logs')
    fh = logging.FileHandler(os.path.realpath(os.path.join('logs', 'log_{}_{}.log'.format(name, time.strftime('%Y%m%d')))))
    fmt = "\n[%(asctime)s-%(name)s-%(levelname)s]: %(message)s"
    formatter = logging.Formatter(fmt)
    fh.setFormatter(formatter)
    logger.addHandler(fh) 
    return logger
    
def outer(func):
	@wraps(func)
	def inner(*args, **kwargs):
		try:
			func(*args, **kwargs)
		except Exception as e:
			print(e)
			# 在此将错误写入文件中
			setExceptionLogger('请随意就是文件名而已').exception("[Error in {}] msg: {}".format(__name__, str(e)))
	return inner
@outer()
def test():
	...

  虽然这样可以勉强满足要求,但是这样每个函数都给添加这个装饰器,而且对于文件的句柄没有关闭有可能会出现问题,而且除了满足对错误的记录,还应该在运行过程中对一些代码执行的记录。

第二步:给代码类添加类装饰器

  正常的代码都是用类封装的,所以通过给目标类的所有函数添加如上的错误记录装饰器,同时还要添加一个logger属性,可以让人手动将代码的某一个记录代码进度写入日志文件中,其作用跟print(),控制台打印差不多。
  想法1:可以用函数装饰器来装饰类,但是这样一来就显得有点low,毕竟封装成一个函数肯定没有封装成一个类来的舒服。
  想法2:将目标类传入类装饰器,添加一个logger属性倒是没什么问题,但是给目标类的所有方法都添加装饰器就需要用到__getattribute__函数,这样写的话代码又长又不好看,嵌套的层数太多,那就干脆新建一个子类,继承目标类的所有属性和方法,再将其返回,这样一来最终使用的将是这个子类。
  想法3:如果同时新建多个实例化的话,那如果写入同一个文件的话,有可能会造成日志文件的资源抢占问题,因此需要再返回一个文件流句柄,添加一个关闭文件流的方法。
不多说,直接上代码

import time
import os
import logging
from functools import wraps

class YoungerLogDecorator:
    def __init__(self,logPath=None,logName=None):
        #wraps(cls)(self)
        self.logPath=logPath if logPath else './'
        if not os.path.exists(self.logPath):
            os.makedirs(self.logPath)
        self.logName=logName
    def __call__(self,cls):
        if not self.logName:
            self.logName = cls.__name__
        @wraps(cls)
        def inner(*args, **kwargs):
            if not hasattr(cls, 'log'):
                logger,filehandler = self.getLogger()
                setattr(cls, 'log', logger)
                setattr(cls,'handler',filehandler)
                #ChildCls = self.retChildCls(cls)
            return self.retChildCls(cls)(*args, **kwargs)
        return inner

    
    def getLogger(self):
        file = os.path.realpath(os.path.join(self.logPath,f'{self.logName}.log'))
        logger = logging.getLogger(self.logName)
        #设置日志等级
        logger.setLevel(logging.INFO)
        #添加文件输出流
        filehandler = logging.FileHandler(file, mode='a',encoding='utf8')
        #设置日志输出格式
        formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(name)s - %(levelname)s - %(message)s')
        filehandler.setLevel(logging.INFO)
        filehandler.setFormatter(formatter)
        #添加文件流到logger
        logger.addHandler(filehandler)
        return logger,filehandler
    
    def retChildCls(self,cls):
        """新建一个子类并返回,继承父类所有属性,同时给所有方法都添加装饰器"""
        class ChildCls(cls):
            def __init__(self,*args, **kwargs):
                super().__init__(*args, **kwargs)
            def __getattribute__(self,item):
                attrs = super().__getattribute__(item)
                if str(type(attrs)) == "<class 'method'>":
                    def decorator(*args, **kwargs):
                        try:
                            res = attrs(*args, **kwargs)
                            #self.closeHandler()
                            return res
                        except Exception as e:
                            print(e)
                            self.log.exception(e)
                            #self.closeHandler()
                    return decorator
                else:
                    return attrs
                
            def closeHandler(self):
                self.handler.close()
        return ChildCls
@YoungerLogDecorator()
class Test:
    def __init__(self):
        self.log.info('使用__init__函数')
    def func1(self):
        self.log.info('使用func1函数')
        
    def func2(self):
        self.log.info('使用func2函数')
        
    def func(self):
        self.log.warning('hsdfsdf')
        self.log.info('this is info msg')
        
    def func3(self):
        self.log.info('使用func3函数')
    def func4(self):
        self.log.info('使用func4函数,关闭文件')
        
    
t = Test()
t.func()
t.closeHandler()
t1 = Test()
t1.func2()
t1.closeHandler()

总结

  虽然写的不是很好,但是基本可以满足要求,能用就行,后期如果有新的需求,那就继续研究。有缘人看到的话,也可以指正我的写法,帮助我改进,加深理解。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值