Python日志系统logging实战配置
日志是排查线上问题时最重要的信息来源之一。Python 标准库自带 logging 模块,功能足够支撑大部分脚本、Web 服务和后台任务。很多项目一开始直接使用 print,等任务跑到服务器上才发现无法区分级别、没有时间、没有模块名,也不好重定向到文件。
本文介绍一套适合中小型 Python 项目的 logging 配置方式,包括控制台输出、文件轮转、模块 logger、异常堆栈和常见坑。
logging 的基本概念
logging 主要包含四个对象:
- Logger:代码中使用的日志入口。
- Handler:决定日志输出到哪里,例如控制台、文件、网络。
- Formatter:决定日志格式。
- Level:决定哪些日志会被输出,例如 DEBUG、INFO、WARNING、ERROR。
在业务代码中不要直接操作 root logger,推荐为每个模块创建自己的 logger:
1 | import logging |
__name__ 会使用当前模块路径作为 logger 名称,排查时能看出日志来自哪个模块。
最小可用配置
对于简单脚本,可以使用 basicConfig:
1 | import logging |
输出类似:
1 | 2026-06-05 09:00:00,123 INFO [__main__] hello logging |
这比 print 多了时间、级别和模块名。脚本排查问题时,这些字段非常关键。
同时输出到控制台和文件
服务端程序通常希望控制台输出给容器日志系统,同时保留本地文件。可以手动配置 handler:
1 | import logging |
RotatingFileHandler 会在文件超过 20MB 后自动轮转,并保留 5 个历史文件,避免日志无限增长占满磁盘。
在入口文件中调用:
1 | def main(): |
记录异常堆栈
捕获异常时,不要只记录 str(e),否则缺少堆栈信息。推荐使用 logger.exception:
1 | try: |
logger.exception 只能在 except 块中使用,它会自动附加当前异常堆栈。如果不在 except 块中,可以使用:
1 | logger.error("request failed", exc_info=True) |
堆栈信息对定位文件行号、调用链和真实异常类型非常重要。
日志级别如何选择
常见级别可以这样划分:
- DEBUG:开发调试信息,例如变量值、分支选择、SQL 参数。
- INFO:正常业务流程,例如服务启动、任务完成、关键状态变化。
- WARNING:非预期但还能继续运行,例如重试、配置缺省、缓存失效。
- ERROR:当前操作失败,需要人工关注或上层处理。
- CRITICAL:程序可能无法继续运行,例如核心依赖不可用。
生产环境通常使用 INFO 或 WARNING。DEBUG 日志可能包含大量细节,既影响性能,也可能暴露敏感信息。
在模块中使用 logger
业务模块只需要获取 logger,不应该重复配置 handler:
1 | import logging |
注意这里使用 %s 参数化,而不是 f-string:
1 | logger.info("create order started order_id=%s", order_id) |
当日志级别未开启时,logging 可以避免不必要的字符串格式化开销。对于普通项目影响不大,但这是更稳妥的习惯。
避免重复日志
如果日志出现重复输出,通常是因为多次调用配置函数,或者子 logger 和 root logger 都绑定了 handler。入口配置时可以使用:
1 | root.handlers.clear() |
另外,库代码不应该主动调用 basicConfig,否则会影响宿主程序的日志行为。库只创建 logger,把配置权交给应用入口。
结构化字段
如果暂时没有接入 JSON 日志系统,也可以用固定格式记录关键字段:
1 | logger.info( |
字段名保持一致,后续使用 grep、日志平台或正则解析都更方便。不要只写“处理失败”“请求异常”这类没有上下文的日志。
小结
Python 的 logging 标准库已经能满足大多数项目需求。推荐在程序入口统一配置 handler 和 formatter,在业务模块中通过 logging.getLogger(__name__) 获取 logger。异常使用 logger.exception,文件输出使用轮转,生产环境控制 DEBUG 日志。只要日志格式稳定、字段充分,排查问题的效率会明显提升。







