如何使用 Python 的 logging 库

发布于 2023-07-29  146 次阅读


简单学习下 logging 模块的使用,以后不用手动 print 来显示 debug 信息了。

日志级别

logging 中的日志等级如下:

file

实际上,这一分级在所有语言中都是一致的(大概……)。

工作流程

这部分直接引用另一篇文章的内容。

logging 模块有几种类:

Logger:日志,暴露函数给应用程序,基于日志记录器和过滤器级别决定哪些日志有效。
LogRecord:日志记录器,将日志传到相应的处理器处理。
Handler:处理器, 将(日志记录器产生的)日志记录发送至合适的目的地。
Filter:过滤器, 提供了更好的粒度控制,它可以决定输出哪些日志记录。
Formatter:格式化器, 指明了最终输出中日志记录的布局。

工作流程如下图:

file

实际使用中,我们用 Formatter 设置日志格式,Handler 处理日志(打印还是输出到文件),Filter 过滤。一个 Logger 可能有不同的 Handler,比如一个 Handler 打印,一个 Handler 输出到文件,这样的话一个 Logger 就可以同时打印和输出到文件。

使用方式

基本方式

直接使用默认的 logging

先用 logging.basicConfig() 去配置,然后直接 logging.info()logging.warn() 等使用。

具体可配置项见函数定义。

自定义 Logger

为了一些多样化需求,我们可以定义自己的 Logger。

事实上,虽然可以存在多个 Logger,最终输出信息的,还是 root Logger。

一个 Logger 可以有多个 Handler 和多个 Filter,一个 Handler 又有自己的 Formatter。

Formatter 的可用变量请参见官方文档。

此外,Logger 和 Handler 可以有不同的日志级别,以此实现不同粒度的日志记录 。例如,将 loggerfile_handler 设置为 DEBUG 级别,将 stream_handler 设置为 WARN 级别,这样文件中会有更多信息,而控制台打印的信息会少一些。

Logger 配置

我们可以使用字典来方便地配置 Logger,这也意味着你可以把配置写入文件,无论是 .py 的字典,或者 .yaml.ini 的配置文件,只要能解析成 Python 字典即可。

假设已经有了一个字典类型的配置 config ,那么使用 logging.config.dictConfig(config),即可完成配置。

下面给出一个示例:

from datetime import datetime

cur_date = datetime.now().strftime('%Y-%m-%d')
cur_time = datetime.now().strftime('%H-%M-%S')

logger_config = {
    'version': 1,
    'formatters': {
        'simple': {
            'format': f"%(asctime)s %(name)s %(levelname)s: %(message)s",
            'datefmt': '%Y-%m-%d %H:%M:%S',
        },
        # 其他的 formatter
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'simple',
            'encoding': 'utf-8'
        },
        'file': {
            'class': 'logging.FileHandler',
            'filename': f"./log/{cur_date}/{cur_time}.log",
            'filemode': 'w',
            'level': 'DEBUG',
            'formatter': 'simple',
            'encoding': 'utf-8'
        },
        # 其他的 handler
    },
    'loggers':{
        # 仅输出到控制台,使用 StreamLogger
        # 输出到控制台,同时写入文件,使用FileLogger
        'StreamLogger': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
        'FileLogger': {
            # 既有 console Handler,还有 file Handler
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
        },
        # 其他的 Logger
    }
}

使用时只需要:

import logging
import logging.config

logging.config.dictConfig(logger_config)
logger = logging.getLogger("FileLogger")

logger.info("This is an info.")

十分方便。

但是后续还要学习下多进程、多线程环境下的使用。

多文件调用

一个常见的场景,是 main 函数调用另一个模块中的函数 A,如何保证 A 中的 logger 与 main 中一致呢?其实 logging 模块已经考虑到到了这一点。只要在 main 函数中配置好了 config,那么不论是哪个模块中,使用 logging.getLogger(logger_name) 都会返回同一个 logger。

参考