python 日志模块定制化

  • 实现效果
  • 内容说明
  • 全部代码内容

实现效果

在这里插入图片描述

内容说明

  1. 解决问题:在自动化测试中,涉及的不仅仅是UI测试,还有接口测试、性能测试,当每个模块中内容多了的时候,想查看日志发现记录的内容什么都有翻起来太麻烦。所以对日志进行模块化分类就有一定的必要,模块化管理提高问题定位的速度。
  2. 当前实现的日志功能有:
    a. 解决了在django中或tkinter中日志被写入被占用的问题
    b. 日志按天分割保存
    c. 在终端打印日志
    d. 允许按模块存放日志

全部代码内容

#  _*_ coding:utf-8 _*_
import inspect
import logging
import os
from logging.handlers import TimedRotatingFileHandler


__all__ = ['log']

LOG_HANDLER = True


class BasePath:
    LOG_DIR = r'D:\Log'		# 存放日志的目录


class ReTimedRotatingFileHandler(TimedRotatingFileHandler):
    """重写一下日志回滚"""
    def rotate(self, source, destination):
        if not callable(self.rotator):
            self.new_rename(destination, source)
        else:
            self.rotator(source, destination)

    @staticmethod
    def new_rename(destination, source):
        """rename 当前日志文件时,老是被占用,改为手动创建新文件,然后再把原日志文件置空"""
        if os.path.exists(source):
            with open(destination, "wb+") as fw:
                with open(source, "rb+") as fr:
                    fw.write(fr.read())
            with open(source, "w+") as fw:
                fw.write("")


def log_init(level=logging.INFO, name=None, filename: str = 'log.log', out_console: bool = True):
    """
    每日生成一个日志文件
    :param level: 日志等级
    :param name: 模块名称
    :param filename: 日志文件名
    :param out_console: 是否在终端打印日志
    :return:

    :Usage:
        log = log_init()
        log.info('日志内容')

    :其他配置:
        ReTimedRotatingFileHandler(log_file_path, when='MIDNIGHT', backupCount=10, encoding="UTF-8")
        :when: 日志轮转的时间间隔,可选值为 ‘S’、‘M’、‘H’、‘D’、‘W’ 和 ‘midnight’,分别表示秒、分、时、天、周和每天的午夜;默认值为 ‘midnight’,即每天的午夜轮转,值不区分大小写
        :backupCount: 备份文件数目;当生成的日志文件数量超过该数目时,会自动删除旧的备份日志文件;默认值为 0,表示不备份
    """
    log_file_path = os.path.join(BasePath.LOG_DIR, filename)
    log_dir = os.path.dirname(log_file_path)
    if not os.path.exists(log_dir):
        os.makedirs(log_dir, exist_ok=True)
    logger = logging.getLogger(name)
    logger.setLevel(level)
    # 日志输出格式
    fmt = '%(levelname)s %(name)s %(asctime)s %(filename)s[%(lineno)d] \t%(message)s'
    formatter = logging.Formatter(fmt)
    # 输出到控制台
    if out_console:
        sh = logging.StreamHandler()
        sh.setFormatter(formatter)
        global LOG_HANDLER
        if LOG_HANDLER:
            logger.addHandler(sh)
            LOG_HANDLER = False
    # 输出到文件,每日一个文件
    fh = ReTimedRotatingFileHandler(log_file_path, when='MIDNIGHT', backupCount=10, encoding="UTF-8")
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    return logger


class Log:
    """
    日志分模块记录
    :Usage:
        log = Log()
        log.system.info('hello world!')
    :拓展示例: 新增类属性,指定唯一模块名称,例如:system
        @property
        def system(self, log_name='system') -> logging:
            return self.__log_construct(log_name)

        template:
            @property
            def 模块名(self, log_name='模块名') -> logging:
                return self.__log_construct(log_name)
    :日志模式:
        1、模式:总-分,,即:所有日志(含模块日志)都会记录在 /Log/log.log文件中,并且其他模块日志分开记录 /Log/system/log.log
            初始化方法:
                log = Log()
                log.info('')
        2、模式:分-分,即:所有日志记录在对应模块中,不进行日志汇总
            初始化方法:
                log = Log()
                log.system.info('')     system 这个可以是任意模块
    """
    _LOGBOX = {}

    def __init__(self):
        self.class_methods = [name for name, value in inspect.getmembers(Log) if not name.startswith('_')]

    @property
    def system(self, log_name='system') -> logging:
        return self.__log_construct(log_name)

    @property
    def ui(self, log_name='ui') -> logging:
        return self.__log_construct(log_name)

    @property
    def api(self, log_name='api') -> logging:
        return self.__log_construct(log_name)

    def __log_construct(self, log_name: str = None) -> logging:
        log_obj = self._LOGBOX.get(log_name)
        if not log_obj:
            log_obj = self._LOGBOX[log_name] = log_init(name=log_name,
                                                        filename=os.path.join(BasePath.LOG_DIR, f'{log_name}/log.log'))
        return log_obj

    def __getattr__(self, item, log_name='log'):
        if item not in self.class_methods:
            log_obj = self._LOGBOX.get(log_name)
            if not log_obj:
                log_obj = self._LOGBOX[log_name] = log_init()
            return getattr(log_obj, item)


log = Log()


if __name__ == '__main__':
    log.system.info('hello world')

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐