Python 内置 logging 模块使用详解:从基础配置到四大组件
本文详细介绍 Python 内置 logging 模块的使用方法。涵盖日志的五个等级(CRITICAL 至 DEBUG)及其默认行为,解析 logging.basicConfig 的基础配置参数。深入讲解 Logger、Handler、Filter 和 Formatter 四大核心组件的功能与交互,演示如何通过 StreamHandler、FileHandler 等将日志输出至不同目标,并提供 conf 和 yaml 两种配置文件的使用示例,对比 logging 与 print 的优势。
logging 模块的核心价值
该模块提供了记录日志的接口及多种处理模块,支持用户以不同格式存储日志数据,从而辅助程序调试或记录运行过程中的关键信息输出。
logging 日志等级体系
logging 模块定义了五个日志等级,其优先级由高到低排列如下:
**CRITICAL**:表示程序发生严重错误。
**ERROR**:表示程序出现错误或部分功能失效。
**WARNING**:提示程序存在发生错误的可能性。
**INFO**:记录程序正常运行时的常规信息。
**DEBUG**:用于程序调试阶段的详细信息。
系统默认的日志记录等级为 WARNING,这意味着只有当日志等级大于或等于 WARNING 时,日志才会被记录。
在实际开发中,INFO 等级最为常用,主要用于记录程序正常运行的相关信息,其作用类似于 print 语句。
当日志等级达到 WARNING 及以上级别时,通常表明程序当前无法正常运行。
logging 基础函数用法
logging.basicConfig(**kwargs)若未显式创建记录器(logger),系统将默认创建一个 root logger。通过调用 logging.basicConfig(**kwargs) 可以创建带有默认 Formatter 的 StreamHandler,并将其添加到根日志记录器中,从而完成基础配置初始化。
例如:
import logging
logging.debug('Debug code!')
logging.info('Run code!')
logging.warning('Watch out!')
logging.error('This is an error')
logging.critical('This is a ciritical')在上述代码中,logging 并未显式调用 logging.getLogger 创建 logger,而是直接使用 debug()、info()、warning()、error()、critical() 等方法。这些方法会自动使用默认的 root logger,并自动调用自定义或默认的 logging.basicConfig(**kwargs) 来初始化 root logger。
自定义 logging.basicConfig(**kwargs) 的主要参数选项包括:
| 参数 | 功能 |
|---|---|
| filename | 指定保存日志的文件名,用指定文件名创建一个FileHandler,记录的日志会保存到该文件中 |
| format | 指定输出的格式和内容,默认是以冒号分割的levalname、name 和 message |
| datefmt | 使用指定的日期/时间格式,与 time.strftime() 所接受的格式相同。 |
| level | 指定根日志记录器级别,默认为 logging.WARNING |
| stream | 指定日志的输出流,可以指定输出到sys.stderr,std.stdout 或 文件,默认输出到sys.stderr。使用指定的流初始化StramHandler,注意:stream和filename参数不兼容,如果两者同时使用,则会引发ValueError 错误 |
例如,通过自定义 logging.basicConfig(**kwargs) 初始化 root logger,可以获取 DEBUG 级别及以上的日志记录,并将其保存到 log.txt 文件中。
import logging
logging.basicConfig(filename='./log.txt',
format='%(asctime)s-%(name)s-%(levelname)s-%(message)s-%(funcName)s:%(lineno)d',
level=logging.DEBUG)
logging.debug('Debug code!')
logging.info('Run code!')
logging.warning('Watch out!')
logging.error('This is an error')
logging.critical('This is a ciritical')logging 的四大核心组件(类)
Logger
除了根记录器(root logger)之外,用户还可以自定义创建日志记录器。
通过模块级别的函数 logging.getLogger(name) 即可实例化记录器。
默认情况下,记录器采用层级结构,通过 . 来区分不同的层级。例如,若存在一个名为 foo 的记录器,则 foo.a 和 foo.b 均为 foo 的子级记录器。最顶层的记录器即为 root logger。若 name=None,则构建的是 root logger。
可以直接使用当前模块的名称作为记录器的名字,即 logging.getLogger(__name__)。
子级记录器通常无需单独设置日志级别及 Handler。若子级记录器未单独设置,其行为将委托给父级。例如,记录器 foo 的级别设为 INFO,而 foo.a 和 foo.b 均未设置日志级别。此时,foo.a 和 foo.b 将遵循 foo 的级别设置,即仅记录大于等于 INFO 级别的日志;若 foo 也未设置,则会向上查找至根记录器 root logger,其默认级别为 WARNING。
logger 类常用方法
| 方法 | 功能描述 |
|---|---|
| Logger.setLevel() | 设置日志器(Logger)将会处理的日志消息级别 |
| Logger.addHandler() | 添加一个handler对象 |
| Logger.removeHandler() | 移除一个handler对象 |
| Logger.addFilter() | 添加一个filter对象 |
| Logger.removeFilter() | 移除一个filter对象 |
| Logger.debug() | 设置DEBUG级别的日志记录 |
| Logger.info() | 设置INFO级别的日志记录 |
| Logger.warning() | 设置WARNING级别的日志记录 |
| Logger.error() | 设置ERROR级别的日志记录 |
| Logger.critical() | 设置CRITICAL级别的日志记录 |
| Logger.exception() | 输出堆栈追踪信息 |
| Logger.log() | 设置一个自定义的level参数来创建一个日志记录 |
logger 结合后续介绍的其余三个组件,可实现以下功能:
- Logger 需通过 handler 将日志信息输出至目标位置,目标位置可以是 sys.stdout 或文件等(这与 logging.basicConfig(**kwargs) 的设置略有不同)。
- 一个 Logger 可配置多个不同的 Handler,不同的 Handler 可将日志输出至不同位置(如不同的日志文件)。每个 Handler 均可设置自己的 filter 以实现日志过滤,保留项目中所需的日志。同时,每个 Handler 可设置不同的 Formatter,使同一条日志以不同格式输出至不同地方。
Handler
处理器(Handler)控制日志输出的目标位置(如标准输出、文件等),并可添加过滤器(filter)和格式控制器(formatter)来控制输出的内容和格式。
常见的处理器类型包括:
- logging.StreamHandler 标准流处理器,将消息发送至标准输出流或错误流。例如 logging.StreamHandler(sys.stdout),其中 sys.stdout 指向控制台(标准输出)。在 Python 中调用 print(obj) 时,实际上执行了 sys.stdout.write(obj+'\n')。
- print 函数将所需内容打印至控制台,并自动追加换行符。
- logging.FileHandler 文件处理器,将消息发送至指定文件,例如 logging.FileHandler(log_path)。
- logging.RotatingFileHandler 文件处理器,当文件达到指定大小时,自动启用新文件存储日志。
- logging.TimedRotatingFileHandler 文件处理器,按特定时间间隔轮换日志文件。
handle 类常用方法
| Handler.setLevel() | 设置处理器将会处理的日志消息的最低严重级别 |
| Handler.setFormatter() | 为处理器设置一个格式对象 |
| Handler.addFilter() | 为处理器添加一个过滤器对象 |
| Handler.removeFilter() | 为处理器删除一个过滤器对象 |
| logging.StramHandler() | 将日志消息发送到输出Stream,如std.out,std.err |
| logging.FilterHandler() | 将日志消息发送到磁盘文件,默认情况文件大小会无线增长 |
| RotationFileHandler() | 将日志消息发送到磁盘文件,支持日志文件按大小切割 |
| TimeRotatingFileHandler() | 将日志消息发送到磁盘文件,并支持日志文件按时间切割 |
| logging.handers.HTTPHandler() | 将日志消息通过GET或POST的方式发送给一个HTTP服务器 |
| logging.handlers.SMTPHandler() | 将日志消息发送email地址 |
Filter
filter 组件用于过滤 logger 对象。filter 可直接添加至 logger 对象或 handler 对象。若在 logger 和 handler 中均设置了 filter,日志将先通过 logger 的 filter,再通过 handler 的 filter。由于所有信息均可经过 filter,因此 filter 不仅可过滤信息,还可增加信息。
Filter 类的实例化对象可通过 logging.Filter(name) 创建,其中 name 为记录器名称。若未创建过该名称的记录器,则不会输出任何日志:
filter = logging.Filter("foo.a")
基本过滤器类仅允许低于指定日志记录器层级结构中特定层级的事件。例如,使用 foo.a 初始化的过滤器,则 foo.a.b 和 foo.a.c 等记录器记录的日志可通过过滤器,而 foo.c 和 a.foo 等则无法通过。若 name 为空字符串,则所有日志均可通过。
Filter 类包含三个方法:
- addFilter(filter):为 logger(logger..addFilter(filter))或 handler(handler..addFilter(filter))增加过滤器。
- removeFilter(filter):为 logger 或 handler 删除一个过滤器。
- filter(record):判断是否记录指定记录?返回零表示否,非零表示是。通常需继承 Filter 基类并重写 filter 方法以实现自定义过滤。
Formatter
用于格式化日志输出。实例化方式:formatter = logging.Formatter(fmt=None,datefmt=None)。若未指定 fmt,默认使用 ‘%(message)s’;若未指定 datefmt,默认使用 ISO8601 日期格式。
其中 fmt 参数包含以下选项:
| %(name)s | Logger的名字 |
|---|---|
| %(levelno)s | 数字形式的日志级别 |
| %(levelname)s | 文本形式的日志级别;如果是logger.debug则它是DEBUG,如果是logger.error则它是ERROR |
| %(pathname)s | 调用日志输出函数的模块的完整路径名,可能没有 |
| %(filename)s | 调用日志输出函数的模块的文件名 |
| %(module)s | 调用日志输出函数的模块名 |
| %(funcName)s | 调用日志输出函数的函数名 |
| %(lineno)d | 调用日志输出函数的语句所在的代码行 |
| %(created)f | 当前时间,用UNIX标准的表示时间的浮 点数表示 |
| %(relativeCreated)d | 输出日志信息时的,自Logger创建以 来的毫秒数 |
| %(asctime)s | 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 |
| %(thread)d | 线程ID。可能没有 |
| %(threadName)s | 线程名。可能没有 |
| %(process)d | 进程ID。可能没有 |
| %(message)s | 用户输出的消息; 假如有logger.warning("NO Good"),则在%(message)s位置上是字符串NO Good |
例如:
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s') # -表示右对齐 8表示取8位
handler.formatter = formatter
datefmt 参数包含以下选项:
| 参数 | 含义 |
|---|---|
| %y | 两位数的年份表示(00-99) |
| %Y | 四位数的年份表示(000-9999) |
| %m | 月份(01-12) |
| %d | 月内中的一天(0-31) |
| %H | 24小时制小时数(0-23) |
| %I | 12小时制小时数(01-12) |
| %M | 分钟数(00=59) |
| %S 秒 | (00-59) |
例如:
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s') # -表示右对齐 8表示取8位
handler.formatter = formatter
datefmt 参数包含以下选项:
| 参数 | 含义 |
|---|---|
| %y | 两位数的年份表示(00-99) |
| %Y | 四位数的年份表示(000-9999) |
| %m | 月份(01-12) |
| %d | 月内中的一天(0-31) |
| %H | 24小时制小时数(0-23) |
| %I | 12小时制小时数(01-12) |
| %M | 分钟数(00=59) |
| %S 秒 | (00-59) |
例子:
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s","%Y%m%d-%H:%M:%S")
handler.formatter = formatter
logging 的配置方式
- conf 形式的配置
在 loguser.conf 文件中写入相关信息。
[loggers]
keys=root,fileLogger,rotatingFileLogger
[handlers]
keys=consoleHandler,fileHandler,rotatingFileHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=INFO
handlers=consoleHandler
[logger_fileLogger]
level=INFO
handlers=fileHandler
qualname=fileLogger
propagate=0
[logger_rotatingFileLogger]
level=INFO
handlers=consoleHandler,rotatingFileHandler
qualname=rotatingFileLogger
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=INFO
formatter=simpleFormatter
args=("logs/fileHandler_test.log", "a")
[handler_rotatingFileHandler]
class=handlers.RotatingFileHandler
level=WARNING
formatter=simpleFormatter
args=("logs/rotatingFileHandler.log", "a", 10*1024*1024, 50)
[formatter_simpleFormatter]
format=%(asctime)s - %(module)s - %(levelname)s -%(thread)d : %(message)s
datefmt=%Y-%m-%d %H:%M:%S
- 使用 logger 时,直接导入配置文件即可。
from logging import config
with open('./loguser.conf', 'r', encoding='utf-8') as f:
## 加载配置
config.fileConfig(f)
## 创建同名Logger,其按照配置文件的handle,formatter,filter方法初始化
logger = logging.getLogger(name="fileLogger")- yaml 形式配置文件
在 loguser.yaml 文件中配置相关信息。
version: 1
disable_existing_loggers: False
# formatters配置了日志输出时的样式
# formatters定义了一组formatID,有不同的格式;
formatters:
brief:
format: "%(asctime)s - %(message)s"
simple:
format: "%(asctime)s - [%(name)s] - [%(levelname)s] :%(levelno)s: %(message)s"
datefmt: '%F %T'
# handlers配置了需要处理的日志信息,logging模块的handler只有streamhandler和filehandler
handlers:
console:
class : logging.StreamHandler
formatter: brief
level : DEBUG
stream : ext://sys.stdout
info_file_handler:
class : logging.FileHandler
formatter: simple
level: ERROR
filename: ./logs/debug_test.log
error_file_handler:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: simple
filename: ./logs/errors.log
maxBytes: 10485760 # 10MB #1024*1024*10
backupCount: 50
encoding: utf8
loggers:
#fileLogger, 就是在代码中通过logger = logging.getLogger("fileLogger")来获得该类型的logger
my_testyaml:
level: DEBUG
handlers: [console, info_file_handler,error_file_handler]
# root为默认情况下的输出配置, 当logging.getLogger("fileLoggername")里面的fileLoggername没有传值的时候,
# 就是用的这个默认的root,如logging.getLogger(__name__)或logging.getLogger()
root:
level: DEBUG
handlers: [console]
同样可通过导入 yaml 文件加载配置。
with open('./loguser.yaml', 'r', encoding='utf-8') as f:
yaml_config = yaml.load(stream=f, Loader=yaml.FullLoader)
config.dictConfig(config=yaml_config)
root = logging.getLogger()
# 子记录器的名字与配置文件中loggers字段内的保持一致
# loggers:
# my_testyaml:
# level: DEBUG
# handlers: [console, info_file_handler,error_file_handler]
my_testyaml = logging.getLogger("my_testyaml")
logging 与 print 的区别
虽然 logging 比 print 复杂得多,但在项目中推荐使用 logging 记录日志而非使用 print 输出程序信息,原因如下。
相比 print,logging 具有以下优点:
- 可通过设置不同的日志等级,在 release 版本中仅输出重要信息,而无需显示大量调试信息。
- print 将所有信息输出至标准输出,严重影响开发者从标准输出中查看其他数据;logging 则允许开发者决定将信息输出至何处以及如何输出。
- 与 print 相比,logging 是线程安全的(Python 3 中 print 也是线程安全的,而 Python 2 中的 print 不是)。线程安全指在多线程环境下程序不会运行混乱。Python 2 中的 print 分两步打印信息:先打印字符串,再打印换行符。若在此中间发生线程切换,便会产生输出混乱。因此,Python 2 中的 print 不是原子操作,即非线程安全。
主要参考资料
https://blog.csdn.net/weixin_41010198/article/details/89356417
https://www.cnblogs.com/chenyibai/p/10676574.html
到此这篇关于 Python 内置 logging 使用详细讲解的文章就介绍到这了,更多相关 Python 内置 logging 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









