Python logging 模块详解:从入门配置到生产级 JSON 输出
Python 内置 logging 模块怎么用?本文系统讲解 Logger/Handler/Formatter 三要素、级别体系、dictConfig 集中配置、python-json-logger 结构化输出、异常堆栈与未捕获异常处理,并给出接入观测云集中管理的路径。
Python logging 是 Python 标准库内置的日志模块,通过 Logger(记录器)、Handler(输出目标)、Formatter(格式)三要素的组合,提供不输任何第三方框架的完整日志能力。 它最大的好处是零依赖、生态默认——绝大多数第三方库都基于它打日志,学会它等于掌握了 Python 日志的"通用语言"。
核心要点速览
- 用法红线:别用
logging.info()操作 root logger,用logging.getLogger(__name__)创建模块级 logger; - 默认级别是 WARNING——DEBUG/INFO 不输出不是 bug,是级别没配;
- 生产输出 JSON 用 python-json-logger,文本格式只适合开发;
- 记录异常务必带
exc_info=True或用logger.exception(),否则堆栈丢失; - 集中化推荐"应用写 stdout/文件 + DataKit 采集上报观测云"。
快速上手与默认行为
import logging
logging.debug("调试信息")
logging.info("一般信息")
logging.warning("警告信息")
输出只有 WARNING:root:警告信息——默认 logger 名为 root,默认级别 WARNING,低于它的 DEBUG/INFO 被丢弃。正确姿势是创建模块级 logger:
logger = logging.getLogger(__name__)
用 __name__ 命名会自动形成"包.模块"的层级,便于按模块控制级别。
三要素:Logger、Handler、Formatter
import sys
import logging
logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)
stdout_handler = logging.StreamHandler(stream=sys.stdout)
stdout_handler.setLevel(logging.DEBUG)
err_handler = logging.FileHandler("error.log")
err_handler.setLevel(logging.ERROR) # 只收 ERROR 及以上
fmt = logging.Formatter("%(asctime)s | %(levelname)s | %(name)s | %(filename)s:%(lineno)s | %(message)s")
stdout_handler.setFormatter(fmt)
err_handler.setFormatter(fmt)
logger.addHandler(stdout_handler)
logger.addHandler(err_handler)
要点:级别可以分别设在 logger 和 handler 上——logger 决定"收不收",handler 决定"发不发",两个阀门串联。
dictConfig:集中式配置
项目变大后,推荐用 logging.config.dictConfig 把配置集中到一个字典(或单独模块)中,应用启动时一次性加载:
from logging.config import dictConfig
dictConfig({
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {"format": "%(asctime)s | %(levelname)s | %(name)s | %(message)s"},
},
"handlers": {
"console": {"class": "logging.StreamHandler", "stream": "ext://sys.stdout", "formatter": "default"},
},
"root": {"level": "INFO", "handlers": ["console"]},
})
生产化:JSON 结构化输出
标准库本身不支持 JSON,装 python-json-logger 即可:
pip install python-json-logger
from pythonjsonlogger import jsonlogger
json_fmt = jsonlogger.JsonFormatter(
"%(name)s %(asctime)s %(levelname)s %(filename)s %(lineno)d %(message)s",
rename_fields={"levelname": "level", "asctime": "time"},
)
stdout_handler.setFormatter(json_fmt)
输出:
{"name": "myapp", "time": "2026-08-25 14:39:03,265", "level": "INFO", "filename": "main.py", "lineno": 31, "message": "服务启动于 8080 端口"}
附加上下文字段用 extra 参数(注意字段名不能与 LogRecord 内置属性冲突,否则抛 KeyError):
logger.info("订单创建成功", extra={"order_id": "ORD-123", "user_id": "USR-9"})
异常与未捕获异常
try:
1 / 0
except ZeroDivisionError:
logger.exception("计算失败") # 等价于 error(..., exc_info=True),自动带堆栈
未捕获异常用 sys.excepthook 兜底,以 CRITICAL 记录后退出:
import sys
def handle_exception(exc_type, exc_value, exc_tb):
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_tb)
return
logger.critical("未捕获异常", exc_info=(exc_type, exc_value, exc_tb))
sys.excepthook = handle_exception
文件轮转
FileHandler 不带轮转,用 RotatingFileHandler(按大小)或 TimedRotatingFileHandler(按时间):
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler("app.log", maxBytes=10_000_000, backupCount=5)
生产环境更推荐交给 logrotate(见本系列《Logrotate 日志轮转实战》),Python 内置轮转在高并发多进程写同一文件时并不可靠。
接入观测云
观测云落地:应用以 JSON 格式输出到 stdout(容器)或文件(虚拟机),DataKit 负责采集上报——容器 stdout 采集或磁盘文件采集(logging.conf 配 source/service)。JSON 自动解析出字段,Pipeline 中把 level 映射为标准 status、确认 time 提取 。之后在日志查看器按字段检索、聚类分析报错模式 ;**监控器(日志检测)**配置"CRITICAL/ERROR 出现即告警",经告警策略推送钉钉/企业微信/飞书 。Python 应用还可用观测云 APM(OpenTelemetry/ddtrace 接入)并把 trace_id 注入日志,实现链路-日志双向跳转 。
总结
标准库 logging 的要点串起来就是:模块级 logger、dictConfig 集中配置、python-json-logger 出 JSON、exception 带堆栈、轮转交给 logrotate、采集交给 DataKit。掌握这套,再看 Loguru/Structlog 只是"换一种更省事的写法"。
常见问题(FAQ)
Q:为什么我的 INFO 日志不输出?
logging 默认级别是 WARNING。需要 logger.setLevel(logging.DEBUG/INFO) 或在 dictConfig 中显式配置级别;注意 logger 和 handler 两级都要放行。
Q:getLogger(name) 和 getLogger("myapp") 有什么区别?
__name__ 自动取模块全名形成层级(如 myapp.services.order),可继承父 logger 配置,适合中大型项目;固定字符串适合小项目或想统一命名的场景。
Q:同一个 logger 为什么打印了两遍?
logger 默认向 root logger 传播(propagate=True),root 又配了 handler 就会重复输出。给自定义 logger 设 logger.propagate = False 即可。
Q:标准库、Loguru、Structlog 怎么选?
写库/需要最大兼容性用标准库;求省心好用选 Loguru;要强结构化与上下文绑定选 Structlog。详细对比见本系列《Python 日志库六款对比》。
系列阅读:Loguru 实战指南 | Python 日志最佳实践十条