Python日志需结构化、可过滤、带上下文、不冗余:按DEBUG/INFO/WARNING/ERROR/CRITICAL真实分级;ERROR必带traceback;用request_id串联链路;JSON格式输出关键字段;敏感信息脱敏;contextvars透传上下文;生产输出stdout,分级保留时长。

Python工程中日志不是“打出来就行”,而是要让排查问题时能快速定位时间、模块、上下文和异常链路。核心是结构化、可过滤、带上下文、不冗余。
日志级别要真实反映问题严重性
DEBUG只用于开发期临时追踪变量;INFO记录关键流程节点(如“订单创建成功,ID=1001”);WARNING表示可能出错但未中断(如配置项缺失,用默认值);ERROR必须对应一次明确的异常或业务失败;CRITICAL仅用于服务不可用级故障(如数据库连接池耗尽)。混用级别会让告警失真——比如把频繁的网络超时记为ERROR,会淹没真正致命的问题。
- 避免在循环里打INFO/DEBUG日志,改用计数聚合或采样(如每100次打一次)
- ERROR日志必须包含完整的异常traceback(用
logger.exception()而非logger.error(str(e))) - 同一业务动作的日志尽量保持相同前缀或request_id,方便grep串联
结构化字段比纯文本更易排查
用structlog或python-json-logger输出JSON格式日志,字段包括:timestamp(ISO8601)、level、logger(模块名)、function、line、request_id(全链路唯一)、user_id(如有)、extra(业务字段,如order_id、payment_method)。文本日志靠正则提取太慢,而结构化日志可直接被ELK或Loki按字段过滤、聚合、告警。
- 不要在message里拼接关键信息(如
"处理订单 %s 失败: %s"),应拆成独立字段 - 敏感字段(如手机号、token)必须脱敏后再写入日志,可用中间件统一处理
- 使用
bind()或push_context()动态注入上下文,避免每个log调用重复传参
上下文绑定比手动传参更可靠
HTTP请求场景下,每个请求应生成唯一request_id,并在整个调用链(视图→服务→DAO→外部API)中透传。用contextvars(Python 3.7+)或threading.local(旧版本)绑定该ID,让所有日志自动携带,无需在每一层函数签名加request_id参数。异步场景必须用contextvars,否则线程局部存储会错乱。
立即学习“Python免费学习笔记(深入)”;
- Django可在中间件中生成并注入;FastAPI用Depends + contextvar;Celery任务用
task_prerun信号初始化 - 避免用全局变量存上下文,会导致并发污染
- 日志处理器中统一添加
request_id字段,而不是靠业务代码每次手动logger.info(..., extra={"request_id": rid})
日志采集与保留要有明确策略
本地开发用文件轮转(RotatingFileHandler);生产环境直接输出到stdout/stderr,由容器或systemd接管收集。日志保留不是越久越好:DEBUG日志保留7天,INFO/WARNING保留30天,ERROR/CRITICAL保留90天以上。高频低价值日志(如健康检查ping)应降级为DEBUG或单独路由丢弃。
- 禁止在日志中写大对象(如整个request.body、resp.data),应记录长度、哈希或关键字段
- 用
LogRecordFactory统一增加主机名、进程ID、部署环境(dev/staging/prod)等字段 - 对慢查询、高频失败接口,配置日志采样率(如ERROR每10次记1次),防止单点打爆磁盘










