跳到主要内容Python 日志模块(logging)全解析 | 极客日志Python
Python 日志模块(logging)全解析
Python 日志模块 logging 是处理日志的核心工具,具备可配置级别、持久化存储等优势。四大组件(Logger、Handler、Formatter、Filter)、日志级别、基础与进阶配置、日志轮转、异常记录、多模块管理及结构化日志输出。涵盖常见问题解决方案及最佳实践,对比原生库与第三方库(如 loguru),帮助开发者构建可追溯、可分析的日志系统。
时间旅人20 浏览 一、核心概念(四大组件)
logging 模块的设计遵循模块化思想,核心由 4 个组件构成,组件间的关系:日志器(Logger)可添加多个处理器(Handler),每个处理器可绑定一个格式化器(Formatter)和多个过滤器(Filter)。
| 组件 | 作用 |
|---|
| Logger(日志器) | 程序直接调用的入口,负责生成日志记录(如 logger.debug()),可设置日志级别。 |
| Handler(处理器) | 决定日志的输出目标(控制台、文件、网络等),每个 Logger 可绑定多个 Handler。 |
| Formatter(格式化器) | 定义日志的输出格式(如包含时间、级别、模块名等)。 |
| Filter(过滤器) | 精细化控制日志的输出(如只允许特定模块 / 级别 / 关键词的日志通过)。 |
二、日志级别
日志级别用于区分日志的重要性,级别越高,记录的信息越关键。logging 定义了 5 个内置级别(可自定义),级别优先级:DEBUG < INFO < WARNING < ERROR < CRITICAL。
| 级别 | 数值 | 适用场景 |
|---|
| DEBUG | 10 | 调试信息(如变量值、函数执行步骤),仅开发 / 测试环境使用。 |
| INFO | 20 | 正常运行信息(如程序启动、关键操作完成),生产环境常用。 |
| WARNING | 30 | 警告信息(如配置缺失、资源不足,程序仍可运行)。 |
| ERROR | 40 | 错误信息(如函数调用失败、文件读取错误,部分功能受影响)。 |
| CRITICAL | 50 | 严重错误(如数据库连接失败、内存耗尽,程序无法继续运行)。 |
核心规则:只有日志的级别 ≥ 日志器 / 处理器设置的级别时,日志才会被输出。默认级别为 WARNING(即默认只输出 WARNING 及以上级别)。
三、基础使用
3.1 最简用法(默认配置)
直接调用 logging 模块的便捷方法,默认输出到控制台,格式为 级别:日志器名称:消息。
import logging
logging.debug("调试信息:变量 x=10")
logging.info("程序启动成功")
logging.warning("警告:配置文件未找到,使用默认配置")
logging.error("错误:读取文件失败")
logging.critical("严重错误:数据库连接中断")
3.2 基本配置(logging.basicConfig)
通过 basicConfig 快速配置日志(单例模式,仅第一次调用生效),支持设置级别、输出文件、格式等。
| 参数 | 作用 |
|---|
| level | 设置日志器级别(如 logging.INFO)。 |
| filename | 日志输出到文件(指定路径),若不设置则输出到控制台。 |
| filemode | 文件打开模式:a(追加,默认)、w(覆盖)。 |
| format | 日志格式(支持占位符,见下文)。 |
| datefmt | 时间格式(如 %Y-%m-%d %H:%M:%S) |
| encoding | 文件编码(如 utf-8,解决中文乱码)。 |
| style | 格式字符串风格:%(默认)、{}、$。 |
import logging
logging.basicConfig(
level=logging.DEBUG,
filename="app.log",
filemode="a",
encoding="utf-8",
format="%(asctime)s - %(name)s - %(levelname)s - %(lineno)d - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logging.debug("调试:用户 ID=1001")
logging.info("信息:接口请求成功")
logging.warning("警告:磁盘空间不足 80%")
logging.error("错误:接口返回码 500")
logging.critical("严重:服务器内存耗尽")
| 占位符 | 含义 |
|---|
| %(asctime)s | 日志记录时间(人性化格式) |
| %(name)s | 日志器名称 |
| %(levelname)s | 日志级别名称(大写) |
| %(levelno)s | 日志级别数值 |
| %(lineno)d | 日志调用行号 |
| %(module)s | 日志所在模块名 |
| %(funcName)s | 日志所在函数名 |
| %(process)d | 进程 ID |
| %(thread)d | 线程 ID |
| %(message)s | 日志消息内容 |
四、进阶配置(手动构建组件)
basicConfig 仅适用于简单场景,复杂场景(多输出目标、自定义格式 / 过滤)需手动创建 Logger、Handler、Formatter。
4.1 手动配置流程
- 获取 Logger 实例(推荐用 name 作为日志器名,便于区分模块);
- 设置 Logger 级别;
- 创建 Handler(如控制台 Handler、文件 Handler),设置 Handler 级别;
- 创建 Formatter,绑定到 Handler;
- 将 Handler 添加到 Logger;
- (可选)添加 Filter 过滤日志。
4.2 示例:多输出目标(控制台 + 文件)
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.propagate = False
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
file_handler = logging.FileHandler("advanced.log", encoding="utf-8")
file_handler.setLevel(logging.DEBUG)
console_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
file_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s")
console_handler.setFormatter(console_formatter)
file_handler.setFormatter(file_formatter)
logger.addHandler(console_handler)
logger.addHandler(file_handler)
logger.debug("调试:数据库查询 SQL=SELECT * FROM users")
logger.info("信息:用户登录成功,ID=1001")
logger.error("错误:数据库查询超时")
4.3 过滤器(Filter)
自定义 Filter 可精细化控制日志输出,例如:只允许特定模块的日志通过、过滤包含敏感信息的日志。
示例:过滤特定模块的日志
import logging
class ApiFilter(logging.Filter):
def filter(self, record):
return "api" in record.name
logger = logging.getLogger("api.user")
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.addFilter(ApiFilter())
logger.addHandler(console_handler)
logger.info("API 请求:/user/info")
logger2 = logging.getLogger("db")
logger2.addHandler(console_handler)
logger2.info("数据库连接成功")
五、日志轮转(解决日志文件过大问题)
当日志文件持续写入时,文件体积会不断增大,logging.handlers 提供了轮转处理器,自动分割日志文件。
5.1 按大小轮转(RotatingFileHandler)
当文件达到指定大小后,自动创建新文件,保留指定数量的备份。
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
rotating_handler = RotatingFileHandler(
"app_rotating.log",
maxBytes=10*1024*1024,
backupCount=5,
encoding="utf-8"
)
rotating_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(rotating_handler)
for i in range(10000):
logger.debug(f"测试轮转日志:{i}")
5.2 按时间轮转(TimedRotatingFileHandler)
按时间间隔(时 / 天 / 周)分割日志文件,适合按周期归档。
import logging
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
time_handler = TimedRotatingFileHandler(
"app_timed.log",
when="D",
interval=1,
backupCount=7,
encoding="utf-8",
suffix="%Y-%m-%d.log"
)
time_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(time_handler)
logger.info("按时间轮转的日志测试")
六、异常日志记录
记录异常信息是日志的核心场景,logging 提供了便捷方式:
6.1 logging.exception ()(推荐)
在 except 块中使用,自动记录异常堆栈信息,无需手动传参,级别为 ERROR。
import logging
logging.basicConfig(level=logging.ERROR, format="%(asctime)s - %(levelname)s - %(message)s")
try:
1/0
except Exception:
logging.exception("除零错误发生")
6.2 exc_info 参数
在 debug/info/error 等方法中设置 exc_info=True,手动记录异常堆栈。
try:
open("nonexist.txt", "r")
except FileNotFoundError as e:
logging.error("文件读取失败", exc_info=True)
七、多模块日志管理
大型项目通常分多个模块,需保证日志配置统一且可区分模块来源:
7.1 核心原则
- 每个模块通过 logging.getLogger(name) 获取 Logger(name 为模块名,如 api.user);
- 根日志器(logging.getLogger())统一配置,子日志器继承根日志器的配置(无需重复配置);
- 避免重复添加 Handler(可通过判断 logger.handlers 是否为空)。
7.2 示例:多模块日志
import logging
import module_a
def setup_logging():
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG)
if not root_logger.handlers:
console_handler = logging.StreamHandler()
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
console_handler.setFormatter(formatter)
root_logger.addHandler(console_handler)
if __name__ == "__main__":
setup_logging()
logging.info("主程序启动")
module_a.do_something()
import logging
logger = logging.getLogger(__name__)
def do_something():
logger.debug("子模块调试信息")
logger.info("子模块执行操作")
八、结构化日志(JSON 格式)
默认日志为文本格式,不利于日志分析工具(如 ELK、Splunk)解析,可输出 JSON 格式日志:
8.1 自定义 Formatter 实现
import logging
import json
from datetime import datetime
class JsonFormatter(logging.Formatter):
def format(self, record):
log_data = {
"time": datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S"),
"logger": record.name,
"level": record.levelname,
"message": record.getMessage(),
"lineno": record.lineno
}
if record.exc_info:
log_data["exc_info"] = self.formatException(record.exc_info)
return json.dumps(log_data, ensure_ascii=False)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)
logger.info("用户登录", extra={"user_id": 1001})
logger.error("接口调用失败", exc_info=True)
8.2 第三方库(推荐)
使用 python-json-logger 简化配置:
pip install python-json-logger
import logging
from pythonjsonlogger import jsonlogger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter("%(asctime)s %(name)s %(levelname)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("结构化日志测试", extra={"request_id": "abc123"})
九、自定义扩展
9.1 自定义 Handler(如发送日志到钉钉 / 邮件)
import logging
import requests
class DingTalkHandler(logging.Handler):
def __init__(self, webhook_url):
super().__init__()
self.webhook_url = webhook_url
self.setLevel(logging.ERROR)
def emit(self, record):
log_msg = self.format(record)
data = {
"msgtype": "text",
"text": {"content": f"【系统错误】\n{log_msg}"}
}
requests.post(self.webhook_url, json=data)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(console_handler)
ding_handler = DingTalkHandler("https://oapi.dingtalk.com/robot/send?access_token=你的 token")
ding_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(message)s"))
logger.addHandler(ding_handler)
logger.error("数据库连接失败,系统无法运行")
9.2 LoggerAdapter(添加上下文信息)
为日志添加全局上下文(如用户 ID、请求 ID),无需每次手动传 extra:
import logging
class ContextLoggerAdapter(logging.LoggerAdapter):
def process(self, msg, kwargs):
return f"[用户 ID:{self.extra['user_id']}] {msg}", kwargs
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
adapter = ContextLoggerAdapter(logger, {"user_id": 1001})
adapter.info("登录成功")
adapter.error("下单失败")
十、常见问题与解决方案
10.1 日志不输出
- 原因 1:日志器 / 处理器级别设置过高(如 Logger 级别为 INFO,调用 debug());
- 原因 2:basicConfig 调用时机晚于第一次日志调用(basicConfig 仅第一次生效);
- 原因 3:Logger 没有绑定 Handler(如手动创建 Logger 但未添加 Handler);
- 解决方案:检查级别配置、确保 basicConfig 先调用、为 Logger 添加 Handler。
10.2 日志重复输出
- 原因 1:多次添加同一个 Handler(如配置函数被多次调用);
- 原因 2:子日志器向上传递到根日志器,根日志器和子日志器都有 Handler;
- 解决方案:
if not logger.handlers:
logger.addHandler(handler)
logger.propagate = False
10.3 中文乱码
- 原因:FileHandler 未指定编码为 utf-8;
- 解决方案:
logging.FileHandler("app.log", encoding="utf-8")
10.4 级别不生效
- 原因:Logger 和 Handler 都有级别,取更严格的(如 Logger 级别 DEBUG,Handler 级别 WARNING,最终只输出 WARNING+);
- 解决方案:确保 Handler 级别 ≤ Logger 级别。
十一、最佳实践
- 统一配置:项目入口处统一配置根日志器,子模块通过 name 获取 Logger,避免重复配置;
- 级别区分环境:开发环境用 DEBUG,测试 / 预发用 INFO,生产环境用 WARNING/ERROR;
- 日志格式规范:包含时间、级别、模块名、行号、消息,异常日志必须包含堆栈;
- 日志轮转:生产环境必须启用日志轮转(按大小 / 时间),避免日志文件过大;
- 敏感信息脱敏:日志中不记录密码、token、手机号等敏感信息;
- 结构化输出:生产环境优先使用 JSON 格式日志,便于日志分析;
- 避免使用 root Logger:尽量使用模块名 Logger,便于区分日志来源;
- 异常必记录:所有 except 块必须记录异常日志(使用 exception() 或 exc_info=True)。
十二、第三方日志库(补充)
原生 logging 配置稍繁琐,可使用第三方库简化:
12.1 loguru(推荐)
开箱即用,无需复杂配置,支持自动轮转、结构化、异常捕获等:
from loguru import logger
logger.debug("调试信息")
logger.info("正常信息")
logger.error("错误信息", exc_info=True)
logger.add("app.log", rotation="10 MB", retention="7 days", encoding="utf-8")
12.2 structlog
专注结构化日志,与 logging 兼容,支持多处理器、上下文绑定:
总结
Python 日志的核心是理解 Logger/Handler/Formatter/Filter 四大组件的协作关系,掌握级别控制、多输出目标、日志轮转、异常记录等核心功能。生产环境中需结合项目特点,选择原生 logging(灵活)或第三方库(便捷),确保日志可追溯、可分析、无敏感信息。
相关免费在线工具
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online