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
D (Debug): Indicates the log information of the debug level
I (Information): Indicates log information at 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 output methods of log

3.1 Console output

import logging
from logging import Logger


class MyLogger(Logger):

    def __init__(self):
        # Set the name of the log and the collection level of the log
        super().__init__("test_api", logging. DEBUG)

        # Customize the log format (Formatter), instantiate a log format class
        fmt_str = '%(asctime)s %(levelname)s %(filename)s : %(funcName)s [line: %(lineno)s] %(message)s'
        formatter = logging. Formatter(fmt_str)

        # Instantiate the console channel (StreamHandle)
        sh = logging. StreamHandler()
        # Set the log level for console output
        sh. setLevel(logging. INFO)
        # Set the log display format in the channel
        sh. setFormatter(formatter)
        # Bind the channel to the log collector
        self. addHandler(sh)
        sh. close()


# Instantiate the MyLogger object, which can be called directly by using log in other files
log = MyLogger()


if __name__ == '__main__':
    log. error("this is an error log")
    log.info("this is an 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 an 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 an 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 log 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

# 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
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):

        # get log file path
        all_log_path_file = "test.log"

        # Set the name of the log and the collection level of the log
        super().__init__("test_api", logging. DEBUG)

        # Customize the log format (Formatter), instantiate a log format class
        fmt_str = '%(asctime)s %(levelname)s %(filename)s : %(funcName)s [line: %(lineno)s] %(message)s'
        formatter = logging. Formatter(fmt_str)

        # Instantiate the file channel (FileHandle)
        '''
        Create a file instance, if the api_test.log file does not exist, it will be created automatically;
        The mode parameter is set to append; in addition, to prevent garbled characters, the encoding parameter is set to utf-8 encoding format
        '''
        # When the log reaches the maximum byte length, 5 log files will be automatically backed up. When all five log files reach the maximum length, only the latest log will be kept.
        fh = handlers.RotatingFileHandler(all_log_path_file, maxBytes=10**3, backupCount=5,
                                               encoding="utf-8", mode="a")
        # Set the log format output to the file
        fh. setLevel(logging. DEBUG)
        # Set the log display format in the channel
        fh. setFormatter(formatter)
        # Load the file instance into the logger object
        self. addHandler(fh)
        # close the file
        fh. close()


# Instantiate the MyLogger object, which can be called directly by using log in other files
log = MyLogger()


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

Result:
Open the log file, you can see the printed log.
If you want to adjust the level of file output log, modify here

# Set the log format output to the file, modify it to the error level
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)

        # get log file path
        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")

        # Set the name of the log and the collection level of the log
        super().__init__("test_api", logging. DEBUG)

        # Customize the log format (Formatter), instantiate a log format class
        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')

        # Instantiate the console channel (StreamHandle)
        sh = logging. StreamHandler()
        # Set the log level for console output
        sh. setLevel(logging. ERROR)
        # Set the log display format in the channel
        sh. setFormatter(formatter)
        # Bind the channel to the log collector
        self. addHandler(sh)

        # Instantiate the file channel (FileHandle)
        # fh = logging. FileHandler(log_path_file, mode='a', encoding="utf-8")
        '''
        Create a file instance, if the api_test.log file does not exist, it will be created automatically;
        The mode parameter is set to append; in addition, to prevent garbled characters, the encoding parameter is set to utf-8 encoding format
        '''
        fh = handlers.RotatingFileHandler(all_log_path_file, maxBytes=10**6, backupCount=5,
                                               encoding="utf-8", mode="a")
        # Set the log format output to the file
        fh. setLevel(logging. DEBUG)
        # Set the log display format in the channel
        fh. setFormatter(formatter)
        # Load the file instance into the logger object
        self. addHandler(fh)
        # When the log reaches the maximum byte length, 5 log files will be automatically backed up. When all five log files reach the maximum length, only the latest log will be kept.
        fh1 = handlers.RotatingFileHandler(error_log_path_file, maxBytes=10 ** 6, backupCount=5,
                                          encoding="utf-8", mode="a")
        # Set the log format output to the file
        fh1. setLevel(logging. ERROR)
        # Set the log display format in the channel
        fh1. setFormatter(formatter)
        # Load the file instance into the logger object
        self. addHandler(fh1)

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


# Instantiate the MyLogger object, which can be called directly by using log in other files
log = MyLogger()


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