Python logger package

1. Background

Everyone should have encountered this scenario, you found a bug but cannot reproduce it. The development said, I will add a log, and then observe it during the test, and tell me in time if there is a problem.

The log is very important for debugging, but the more the better. Key codes generally need to print logs, which can be quickly debugged when problems occur. In daily use, generally focus on the error log. You can also save the info-level log for subsequent analysis when problems occur.

And the log is generally not kept for a long time, only the most recent log is kept.

2. Log level

The lower the level, the higher the level
D (Debug): indicates the log information of the debugging level
I (Information): indicates the log information of the information level
W (Warning): indicates the log information of the warning level
E (Error): indicates the log information of the error level

3. There are two ways to output the log

3.1 Console output

import logging
from logging import Logger


class MyLogger(Logger):

    def __init__(self):
        # 设置日志的名字、日志的收集级别
        super().__init__("test_api", logging.DEBUG)

        # 自定义日志格式(Formatter), 实例化一个日志格式类
        fmt_str = '%(asctime)s  %(levelname)s  %(filename)s : %(funcName)s  [line: %(lineno)s]  %(message)s'
        formatter = logging.Formatter(fmt_str)

        # 实例化控制台渠道(StreamHandle)
        sh = logging.StreamHandler()
        # 设置控制台输出的日志级别
        sh.setLevel(logging.INFO)
        # 设置渠道当中的日志显示格式
        sh.setFormatter(formatter)
        # 将渠道与日志收集器绑定起来
        self.addHandler(sh)
        sh.close()


# 实例化MyLogger对象,在其他文件直接使用log就能调用
log = MyLogger()


if __name__ == '__main__':
    log.error("this is a error log")
    log.info("this is a info log")
    log.debug("this is a debug log")
    log.warning("this is a warning log")

Result:
Since the level of the console input log is set to INFO, INFO and logs with a higher level than INFO will be output on the console.

2023-05-24 15:15:22,445  ERROR  my_logger.py : <module>  [line: 69]  this is a error log
2023-05-24 15:15:22,445  INFO  my_logger.py : <module>  [line: 70]  this is a info log
2023-05-24 15:15:22,445  WARNING  my_logger.py : <module>  [line: 72]  this is a warning log

Process finished with exit code 0

However, in daily use, the general console only outputs logs at the ERROR level. Modify the code here:

sh.setLevel(logging.ERROR)

Result: Execute again, it can be seen that the console only outputs the errorlog

2023-05-24 15:29:20,465  ERROR  stream_logger.py : <module>  [line: 31]  this is a error log

Process finished with exit code 0

3.2 File output

Log output to a file.
The code has been running for a long time, and there will be a lot of logs. At this time, one file is definitely not enough, and it does not make much sense to keep the logs from too long ago. Therefore, when the log reaches the maximum byte length, several log files will be automatically backed up. And when all log files reach the maximum length, only the latest log is kept. [Reduce the byte length of the file to be smaller, and the effect can be seen after multiple executions]

Use RotatingFileHandler() to automatically backup log files

# 当log达到最大字节长度,将自动backup 几个log文件。并且当所有log文件都达到最大长度时,只保留最新的log
fh = handlers.RotatingFileHandler(all_log_path_file, maxBytes=10**2, backupCount=5, encoding="utf-8", mode="a")

code show as below:

import logging
from logging import Logger, handlers


class MyLogger(Logger):

    def __init__(self):

        # 获取日志文件路径
        all_log_path_file = "test.log"

        # 设置日志的名字、日志的收集级别
        super().__init__("test_api", logging.DEBUG)

        # 自定义日志格式(Formatter), 实例化一个日志格式类
        fmt_str = '%(asctime)s  %(levelname)s  %(filename)s : %(funcName)s  [line: %(lineno)s]  %(message)s'
        formatter = logging.Formatter(fmt_str)

        # 实例化文件渠道(FileHandle)
        '''
        创建一个文件实例,如果 api_test.log 文件不存在,就会自动创建;
        mode 参数设置为追加;另外为防止乱码, encoding 参数设置为 utf-8 编码格式
        '''
        # 当log达到最大字节长度,将自动backup5个log文件。当5个log文件都达到最大长度时,将只保留最新的log。
        fh = handlers.RotatingFileHandler(all_log_path_file, maxBytes=10**3, backupCount=5,
                                               encoding="utf-8", mode="a")
        # 设置向文件输出的日志格式
        fh.setLevel(logging.DEBUG)
        # 设置渠道当中的日志显示格式
        fh.setFormatter(formatter)
        # 加载文件实例到 logger 对象中
        self.addHandler(fh)
        # 关闭文件
        fh.close()


# 实例化MyLogger对象,在其他文件直接使用log就能调用
log = MyLogger()


if __name__ == '__main__':
    log.error("this is a error log")
    log.info("this is a info log")
    log.debug("this is a debug log")
    log.warning("this is a warning log")

Result:
Open the log file and you can see the printed log.
insert image description hereIf you want to adjust the level of the file output log, modify here

# 设置向文件输出的日志格式, 修改成error级别
fh.setLevel(logging.ERROR)  

4. Code encapsulation

  • Output ERROR level log on the console
  • Output DEBUG level log in log file
  • Separately output the log of ERROR level in a file
  • When the log reaches the maximum byte length, 5 log files will be automatically backed up. When all 5 log files reach the maximum length, only the latest log will be kept

code show as below:

import logging
import os
from logging import Logger, handlers
from config.settings import get_log_path


class MyLogger(Logger):

    def __init__(self):
        # log_name = '{}.log'.format(time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime()))
        # log_path_file = os.path.join(get_log_path(), log_name)

        # 获取日志文件路径
        all_log_path_file = os.path.join(get_log_path(), "api_test.log")
        error_log_path_file = os.path.join(get_log_path(), "error.log")

        # 设置日志的名字、日志的收集级别
        super().__init__("test_api", logging.DEBUG)

        # 自定义日志格式(Formatter), 实例化一个日志格式类
        fmt_str = '%(asctime)s  %(levelname)s  %(filename)s : %(funcName)s  [line: %(lineno)s]  %(message)s'
        formatter = logging.Formatter(fmt_str)
        # formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

        # 实例化控制台渠道(StreamHandle)
        sh = logging.StreamHandler()
        # 设置控制台输出的日志级别
        sh.setLevel(logging.ERROR)
        # 设置渠道当中的日志显示格式
        sh.setFormatter(formatter)
        # 将渠道与日志收集器绑定起来
        self.addHandler(sh)

        # 实例化文件渠道(FileHandle)
        # fh = logging.FileHandler(log_path_file, mode='a', encoding="utf-8")
        '''
        创建一个文件实例,如果 api_test.log 文件不存在,就会自动创建;
        mode 参数设置为追加;另外为防止乱码, encoding 参数设置为 utf-8 编码格式
        '''
        fh = handlers.RotatingFileHandler(all_log_path_file, maxBytes=10**6, backupCount=5,
                                               encoding="utf-8", mode="a")
        # 设置向文件输出的日志格式
        fh.setLevel(logging.DEBUG)
        # 设置渠道当中的日志显示格式
        fh.setFormatter(formatter)
        # 加载文件实例到 logger 对象中
        self.addHandler(fh)
        # 当log达到最大字节长度,将自动backup5个log文件。当5个log文件都达到最大长度时,将只保留最新的log。
        fh1 = handlers.RotatingFileHandler(error_log_path_file, maxBytes=10 ** 6, backupCount=5,
                                          encoding="utf-8", mode="a")
        # 设置向文件输出的日志格式
        fh1.setLevel(logging.ERROR)
        # 设置渠道当中的日志显示格式
        fh1.setFormatter(formatter)
        # 加载文件实例到 logger 对象中
        self.addHandler(fh1)

        fh.close()
        fh1.close()
        sh.close()


# 实例化MyLogger对象,在其他文件直接使用log就能调用
log = MyLogger()


if __name__ == '__main__':
    log.error("this is a error log")
    log.info("this is a info log")
    log.debug("this is a debug log")
    log.warning("this is a warning log")

Guess you like

Origin blog.csdn.net/weixin_44691253/article/details/130847551