简单学习下 logging 模块的使用,以后不用手动 print 来显示 debug 信息了。
日志级别
logging 中的日志等级如下:
实际上,这一分级在所有语言中都是一致的(大概……)。
工作流程
这部分直接引用另一篇文章的内容。
logging 模块有几种类:
Logger:日志,暴露函数给应用程序,基于日志记录器和过滤器级别决定哪些日志有效。
LogRecord:日志记录器,将日志传到相应的处理器处理。
Handler:处理器, 将(日志记录器产生的)日志记录发送至合适的目的地。
Filter:过滤器, 提供了更好的粒度控制,它可以决定输出哪些日志记录。
Formatter:格式化器, 指明了最终输出中日志记录的布局。
工作流程如下图:
实际使用中,我们用 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 可以有不同的日志级别,以此实现不同粒度的日志记录 。例如,将 logger
和 file_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。
Comments NOTHING