Python3 logging 模块
在 Python 开发中,
logging 模块是官方标准库提供的日志记录工具,相比 print 语句,它支持日志分级、多目的地输出(控制台 / 文件 / 网络)、格式化配置、日志轮转等高级功能,是开发、调试、运维阶段不可或缺的核心工具。本文将从基础概念到实战场景,全面解析 logging 模块的使用方法。一、核心概念:日志的核心组件与级别
1. 四大核心组件(工作流程)
logging 模块的日志处理流程依赖四大组件,各司其职且层层关联:- Logger(日志器):日志的入口,供开发者调用(如
logger.info()),负责收集日志请求并传递给 Handler。 - Handler(处理器):决定日志的输出目的地(控制台、文件、邮件等),一个 Logger 可绑定多个 Handler(如同时输出到控制台和文件)。
- Formatter(格式化器):定义日志的输出格式(如包含时间、模块、级别、消息内容)。
- Filter(过滤器):对日志进行二次筛选(如仅记录特定模块、特定关键字的日志),可选组件。
工作流程:开发者调用
logger 方法 → Logger 接收请求并判断级别 → 符合级别要求的日志通过 Filter → 传递给所有绑定的 Handler → Handler 按 Formatter 格式输出到目标位置。2. 日志级别(优先级从低到高)
logging 定义了 5 个标准级别,默认仅输出 WARNING 及以上级别的日志(可自定义阈值):| 级别名称 | 数值 | 用途说明 |
|---|---|---|
DEBUG |
10 | 调试信息(如变量值、函数执行流程),仅开发阶段使用。 |
INFO |
20 | 正常运行信息(如程序启动、服务连接成功),记录关键流程节点。 |
WARNING |
30 | 警告信息(如参数不匹配、资源不足),程序仍可运行但需关注。 |
ERROR |
40 | 错误信息(如函数调用失败、文件读取异常),部分功能无法正常执行。 |
CRITICAL |
50 | 严重错误(如数据库连接失败、内存耗尽),程序即将崩溃。 |
扩展说明:支持自定义日志级别,但不推荐(破坏通用性);日志级别可通过数值比较(如
level >= WARNING 即输出)。二、基础使用:快速上手
1. 最简单的日志输出(直接使用模块级函数)
logging 模块提供了与日志级别对应的模块级函数(debug()、info()、warning() 等),无需复杂配置即可快速输出日志:import logging
# 直接调用模块级函数(默认输出到控制台,格式为:WARNING:root:警告信息)
logging.debug("这是 DEBUG 级日志(默认不输出)")
logging.info("这是 INFO 级日志(默认不输出)")
logging.warning("这是 WARNING 级日志(默认输出)")
logging.error("这是 ERROR 级日志")
logging.critical("这是 CRITICAL 级日志")
输出结果:
WARNING:root:这是 WARNING 级日志(默认输出)
ERROR:root:这是 ERROR 级日志
CRITICAL:root:这是 CRITICAL 级日志
2. 基础配置(basicConfig())
通过
logging.basicConfig() 可快速配置日志的输出格式、级别、目的地等,仅需调用一次(通常在程序入口):import logging
# 基础配置(必须在首次调用日志函数前执行)
logging.basicConfig(
level=logging.DEBUG, # 输出所有级别 >= DEBUG 的日志
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", # 日志格式
datefmt="%Y-%m-%d %H:%M:%S", # 时间格式
handlers=[
logging.StreamHandler(), # 输出到控制台(默认)
logging.FileHandler("app.log", encoding="utf-8") # 输出到文件(UTF-8 编码避免中文乱码)
]
)
# 测试日志输出
logging.debug("调试:用户请求参数为 {'id': 1}")
logging.info("信息:服务启动成功,监听端口 8080")
logging.warning("警告:磁盘空间剩余 10%,请及时清理")
logging.error("错误:数据库查询失败,SQL: SELECT * FROM user")
logging.critical("严重:数据库连接超时,程序即将退出")
关键参数说明:
level:设置日志阈值,低于该级别的日志不输出。format:日志格式占位符(常用):%(asctime)s:日志产生时间。%(name)s:日志器名称(默认root)。%(levelname)s:日志级别名称(大写)。%(message)s:日志内容。%(filename)s:产生日志的文件名。%(lineno)d:产生日志的代码行号。
handlers:指定日志处理器(可同时输出到控制台和文件)。encoding:文件输出时指定编码(避免中文乱码)。
三、进阶配置:自定义 Logger 与 Handler
模块级函数本质是使用默认的
root Logger,实际开发中推荐自定义 Logger(避免模块间日志冲突),灵活组合 Handler、Formatter 和 Filter。1. 自定义 Logger 示例(控制台 + 文件双输出)
import logging
def setup_custom_logger():
# 1. 创建 Logger(名称建议用模块名 __name__,避免冲突)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # Logger 级别(需低于 Handler 级别才会生效)
logger.propagate = False # 禁止传递给父 Logger(避免重复输出)
# 2. 创建控制台 Handler(输出到终端)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # 控制台仅输出 >= INFO 的日志
# 3. 创建文件 Handler(输出到日志文件)
file_handler = logging.FileHandler("custom.log", encoding="utf-8")
file_handler.setLevel(logging.DEBUG) # 文件输出 >= DEBUG 的日志(包含调试信息)
# 4. 创建 Formatter(定义日志格式)
console_formatter = logging.Formatter(
"%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
file_formatter = logging.Formatter(
"%(asctime)s - %(filename)s:%(lineno)d - %(name)s - %(levelname)s - %(message)s"
)
# 5. 给 Handler 绑定 Formatter
console_handler.setFormatter(console_formatter)
file_handler.setFormatter(file_formatter)
# 6. 给 Logger 绑定 Handler
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
# 使用自定义 Logger
logger = setup_custom_logger()
logger.debug("调试:进入用户登录函数")
logger.info("信息:用户 'admin' 登录成功")
logger.warning("警告:用户密码复杂度不足")
logger.error("错误:用户登录失败,密码错误")
logger.critical("严重:连续 5 次登录失败,账户锁定")
核心逻辑:
- Logger 级别是 “入口阈值”,Handler 级别是 “出口阈值”,需同时满足
Logger 级别 <= 日志级别 <= Handler 级别才会输出。 logger.propagate = False避免日志被传递到rootLogger,导致重复输出。
2. 日志轮转(避免日志文件过大)
当程序长期运行时,单一日志文件会越来越大,
logging.handlers 提供了两种轮转机制:(1)按文件大小轮转(RotatingFileHandler)
import logging
from logging.handlers import RotatingFileHandler
# 创建按大小轮转的 Handler
# maxBytes=1024*1024:单个文件最大 1MB;backupCount=5:保留最近 5 个日志文件
rotating_handler = RotatingFileHandler(
"size_rotated.log",
maxBytes=1024 * 1024, # 1MB
backupCount=5,
encoding="utf-8"
)
# 绑定到 Logger
logger = logging.getLogger("size_logger")
logger.setLevel(logging.INFO)
logger.addHandler(rotating_handler)
# 测试:循环输出日志,触发轮转
for i in range(10000):
logger.info(f"按大小轮转测试 - 第 {i} 条日志")
(2)按时间轮转(TimedRotatingFileHandler)
import logging
from logging.handlers import TimedRotatingFileHandler
# 创建按时间轮转的 Handler
# when="D":按天轮转;interval=1:每天 1 次;backupCount=7:保留最近 7 天日志
timed_handler = TimedRotatingFileHandler(
"time_rotated.log",
when="D", # 轮转单位:S(秒)、M(分)、H(时)、D(天)、W0-W6(星期)、midnight(午夜)
interval=1, # 间隔时间(结合 when)
backupCount=7, # 保留日志文件数
encoding="utf-8",
utc=True # 是否使用 UTC 时间(默认本地时间)
)
# 绑定到 Logger
logger = logging.getLogger("time_logger")
logger.setLevel(logging.INFO)
logger.addHandler(timed_handler)
# 测试
logger.info("按时间轮转测试 - 每日生成新日志文件")
3. 异常日志记录(关键!)
logging 支持自动记录异常堆栈信息,只需在 except 块中调用 logger.exception() 或传入 exc_info=True:import logging
logging.basicConfig(
level=logging.ERROR,
filename="error.log",
format="%(asctime)s - %(levelname)s - %(message)s",
encoding="utf-8"
)
try:
# 模拟异常
result = 10 / 0
except Exception as e:
# 方式 1:logger.exception() 自动记录堆栈信息
logging.exception("除法运算异常:")
# 方式 2:传入 exc_info=True
# logging.error(f"除法运算异常:{e}", exc_info=True)
日志文件输出(包含堆栈):
2024-05-20 15:30:00,123 - ERROR - 除法运算异常:
Traceback (most recent call last):
File "test.py", line 10, in <module>
result = 10 / 0
ZeroDivisionError: division by zero
四、实战场景:多模块日志配置
大型项目通常包含多个模块,推荐使用 “配置文件” 或 “字典配置” 统一管理日志,避免重复代码。
1. 字典配置(推荐,无需配置文件)
import logging
from logging.config import dictConfig
# 日志配置字典
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False, # 不禁用已存在的 Logger
"formatters": {
"simple": {
"format": "%(asctime)s - %(levelname)s - %(message)s"
},
"detailed": {
"format": "%(asctime)s - %(name)s - %(filename)s:%(lineno)d - %(levelname)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "simple"
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"level": "DEBUG",
"formatter": "detailed",
"filename": "project.log",
"maxBytes": 1024 * 1024 * 5, # 5MB
"backupCount": 10,
"encoding": "utf-8"
}
},
"loggers": {
# 自定义 Logger:匹配模块名 "my_project" 及其子模块
"my_project": {
"level": "DEBUG",
"handlers": ["console", "file"],
"propagate": False
},
# 默认 root Logger
"root": {
"level": "WARNING",
"handlers": ["console"]
}
}
}
# 加载配置
dictConfig(LOGGING_CONFIG)
# 模块 A 中使用
logger_a = logging.getLogger("my_project.module_a")
logger_a.debug("模块 A 调试信息")
logger_a.info("模块 A 启动成功")
# 模块 B 中使用
logger_b = logging.getLogger("my_project.module_b")
logger_b.error("模块 B 执行失败")
2. 配置文件(.conf 格式)
创建
logging.conf 文件:[loggers]
keys=root,my_project
[logger_root]
level=WARNING
handlers=console
[logger_my_project]
level=DEBUG
handlers=console,file
propagate=0
qualname=my_project
[handlers]
keys=console,file
[handler_console]
class=StreamHandler
level=INFO
formatter=simple
args=(sys.stdout,)
[handler_file]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=detailed
args=("project.log", "a", 5*1024*1024, 10, "utf-8")
[formatters]
keys=simple,detailed
[formatter_simple]
format=%(asctime)s - %(levelname)s - %(message)s
[formatter_detailed]
format=%(asctime)s - %(name)s - %(filename)s:%(lineno)d - %(levelname)s - %(message)s
加载配置文件:
import logging
from logging.config import fileConfig
# 加载配置文件
fileConfig("logging.conf")
# 使用 Logger
logger = logging.getLogger("my_project")
logger.info("从配置文件加载日志配置成功")
五、注意事项与最佳实践
1. 常见误区
- 重复输出日志:未设置
logger.propagate = False,导致日志同时被自定义 Logger 和rootLogger 输出,需禁用传播或调整父 Logger 配置。 - 中文乱码:文件 Handler 未指定
encoding="utf-8",需在创建FileHandler时显式设置编码。 - 日志级别失效:Logger 级别高于 Handler 级别(如 Logger 设为
INFO,Handler 设为DEBUG),此时 Handler 无法接收DEBUG级日志,需确保 Logger 级别 <= Handler 级别。 - 模块级 Logger 冲突:多个模块使用相同名称的 Logger,导致配置相互影响,推荐使用
__name__作为 Logger 名称(如logging.getLogger(__name__)),自动区分模块。
2. 最佳实践
- 统一配置:大型项目使用字典配置或配置文件,集中管理日志格式、级别、输出目的地。
- 合理分级:开发环境输出
DEBUG级日志,生产环境仅输出WARNING及以上级别(减少日志量)。 - 日志轮转:生产环境必须配置日志轮转(按大小 / 时间),避免日志文件占用过多磁盘空间。
- 包含关键信息:日志格式应包含时间、模块、级别、行号(便于问题定位)。
- 异常必记录堆栈:捕获异常时使用
logger.exception()或exc_info=True,保留堆栈信息便于调试。 - 避免日志泄露敏感信息:不记录密码、Token 等敏感数据,必要时脱敏(如
password: ****)。
六、总结
logging 模块是 Python 日志管理的标准解决方案,核心优势在于灵活的配置和丰富的功能。从基础的模块级函数到自定义 Logger、日志轮转、多模块配置,可满足从小型脚本到大型项目的所有日志需求。使用时需牢记:
- 日志级别决定输出范围,合理设置避免日志冗余或缺失。
- 四大组件(Logger/Handler/Formatter/Filter)的协作关系是配置核心。
- 生产环境务必配置日志轮转和编码,避免中文乱码和磁盘占满。
浙公网安备 33010602011771号