python 自定义日志器封装(一)

思路:使用自定义封装日志器进行日志的输出:按单个文件,按文件大小,按时间。如果初始化异常则使用默认日志器

步骤如下:
1. 默认日志器,默认日志器使用按天分割日志
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Author  : holidaylzz
# Time    : 2020/5/10

import time
import yaml.scanner
import logging
import logging.config
from logging.handlers import TimedRotatingFileHandler
from core.util.pathUtil import PathUtil


def basic_logger():
    """
    默认日志器
    :return:
    """
    path_util = PathUtil()
    # 在项目中创建日志文件路径,已创建则忽略
    path_util.makedirs('log')
    # 日志格式
    fmt = logging.Formatter('%(asctime)s - %(thread)s - [%(levelname)s] %(pathname)s - %(funcName)s  : %(message)s')
    # 日志等级
    level = logging.INFO
    # 日志器
    logger = logging.getLogger()
    logger.setLevel(level)

    # 使用多个不同等级的handler输出到不同日志文件
    #info handler
    info_filename = path_util.get_project_file_path('log/apiTest.log')
    tft_info_handler = TimedRotatingFileHandler(filename=info_filename, when='midnight', backupCount=10, encoding='UTF-8')
    tft_info_handler.setLevel(logging.INFO)
    tft_info_handler.setFormatter(fmt)
    logger.addHandler(tft_info_handler)

    # error handler
    error_filename = path_util.get_project_file_path('log/apiTest_error.log')
    tft_error_handler = TimedRotatingFileHandler(filename=error_filename, when='midnight', backupCount=10, encoding='UTF-8')
    tft_error_handler.setLevel(logging.ERROR)
    tft_error_handler.setFormatter(fmt)
    logger.addHandler(tft_error_handler)

    # warn handler
    warn_filename = path_util.get_project_file_path('log/apiTest_warn.log')
    tft_warn_handler = TimedRotatingFileHandler(filename=warn_filename, when='midnight', backupCount=10, encoding='UTF-8')
    tft_warn_handler.setLevel(logging.WARN)
    tft_warn_handler.setFormatter(fmt)
    logger.addHandler(tft_warn_handler)

    return logger
2.自定义日志器封装

1.自定义日志器封装类

#  和上面的代码放在同一个文件logger.py
class Logger(object):

    def __init__(self, logger_name="timedRotatingFile_module"):
        """
        日志器初始化
        :param logger_name:默认使用timedRotatingFile_module
        """
        log_conf = 'logging.yml'
        try:
            # read logging.yml
            with open(log_conf, 'rt') as f:
                config = yaml.safe_load(f.read())

            logging.config.dictConfig(config)
            self.__logger = logging.getLogger(logger_name)
        # 异常处理,出现异常使用默认日志器,进行日志输出
        except FileNotFoundError as e:
            self.__logger = basic_logger()
            self.__logger.exception(e)
        except yaml.scanner.ScannerError as e:
            self.__logger = basic_logger()
            self.__logger.exception(e)
        except (ValueError, TypeError, AttributeError, ImportError) as e:
            self.__logger = basic_logger()
            self.__logger.error('logger config error!')
            self.__logger.exception(e)
        except Exception as e:
            self.__logger = basic_logger()
            self.__logger.exception(e)

    @property
    def logger(self):
        """
        返回日志器
        :return:
        """
        return self.__logger

2.logging.yml配置文件

# version must be 1
# logger配置文件
#########################################logging yaml profile##################################################

version: 1
disable_existing_loggers: False
formatters:
        simple:
            format: "%(asctime)s - %(thread)s - [%(levelname)s] %(pathname)s - %(funcName)s  : %(message)s"
#filters:
handlers:
    console:
            class: logging.StreamHandler
            level: DEBUG
            formatter: simple
            stream: ext://sys.stdout
    info_file_handler:
            class: logging.FileHandler
            level: INFO
            formatter: simple
            filename: ../../../log/apiTest.log
            encoding: utf8
    error_file_handler:
            class: logging.FileHandler
            level: ERROR
            formatter: simple
            filename: ../../../log/apiTest_error.log
            encoding: utf8
    warn_file_handler:
            class: logging.FileHandler
            level: WARN
            formatter: simple
            filename: ../../../log/apiTest_warn.log
            encoding: utf8
    info_file_timedRotatingFileHandler:
            class: logging.handlers.TimedRotatingFileHandler
            level: INFO
            formatter: simple
            filename: ../../../log/apiTest.log
            when: 'midnight'
            backupCount: 20
            encoding: utf8
    error_file_timedRotatingFileHandler:
            class: logging.handlers.TimedRotatingFileHandler
            level: ERROR
            formatter: simple
            filename: ../../../log/apiTest_error.log
            when: 'midnight'
            backupCount: 20
            encoding: utf8
    warn_file_timedRotatingFileHandler:
            class: logging.handlers.TimedRotatingFileHandler
            level: WARN
            formatter: simple
            filename: ../../../log/apiTest_warn.log
            when: 'midnight'
            backupCount: 20
            encoding: utf8
    info_file_rotatingFileHandler:
            class: logging.handlers.RotatingFileHandler
            level: INFO
            formatter: simple
            filename: ../../../log/apiTest.log
            maxBytes: 2147483648
            backupCount: 20
            encoding: utf8
    error_file_rotatingFileHandler:
            class: logging.handlers.RotatingFileHandler
            level: ERROR
            formatter: simple
            filename: ../../../log/apiTest_error.log
            maxBytes: 2147483648
            backupCount: 20
            encoding: utf8
    warn_file_rotatingFileHandler:
            class: logging.handlers.RotatingFileHandler
            level: WARN
            formatter: simple
            filename: ../../../log/apiTest_warn.log
            maxBytes: 2147483648
            backupCount: 20
            encoding: utf8
loggers:
    file_module:
            level: INFO
            handlers: [info_file_handler,error_file_handler,warn_file_handler]
            propagate: no
    timedRotatingFile_module:
            level: INFO
            handlers: [info_file_timedRotatingFileHandler,error_file_timedRotatingFileHandler,warn_file_timedRotatingFileHandler]
            propagate: no
    rotatingFile_module:
            level: INFO
            handlers: [info_file_rotatingFileHandler,error_file_rotatingFileHandler,warn_file_rotatingFileHandler]
            propagate: no
root:
    level: INFO
    handlers: [console]


3. 自测
def func(num):
    try:
        ret = num+1/num
        return ret
    except Exception as e:
        logger.exception(e)


if __name__ == '__main__':
    logger = Logger().logger
    try:
        for i in range(10):
            data = func(i)
            logger.info(data)
    except Exception as e:
        logger.exception(e)

输出结果:

生成3个日志文件:info,error,warn

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

其中为了适应项目部署,减少绝对路径配置的麻烦,使用自己写的路径处理工具类,使用项目根目录与日志目录拼接做为日志的绝对路径。

import sys
import os


class PathUtil(object):
    """路径处理工具类"""

    def __init__(self):
        # 判断调试模式
        # Python获得一些有关系统的各种信息的时候就不得不想到os的environ()
        debug_vars = dict((a, b) for a, b in os.environ.items() if a.find('IPYTHONENABLE') >= 0)

        # 根据不同场景获取根目录
        if len(debug_vars) > 0:
            # 当前为debug运行时
            self.rootPath = sys.path[2]
        elif getattr(sys, 'frozen', False):
            # 当前为exe运行时
            self.rootPath = os.getcwd()
        else:
            # 正常执行
            self.rootPath = sys.path[1]

        # 替换斜杠
        self.rootPath = self.rootPath.replace("\\", "/")

    def get_project_file_path(self, filename):
        """
        项目根目录拼接文件项目路径 ,如 logs/default.log
        :param filename: 文件项目路径, logs/default.log
        :return:
        """
        filePath = '%s/%s' % (self.rootPath, filename)
        return filePath

    def makedirs(self, directory):
        """
        在项目中创建文件路径,已创建则忽略
        :param directory:项目文件目录
        :return:
        """
        realPath = '%s/%s' % (self.rootPath, directory)
        os.makedirs(realPath, exist_ok=True)

写的不好和有问题的地方请多多指教!
笔芯~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值