应显式创建模块级 logger(如 logging.getlogger("order.service")),避免使用 root logger;通过多 handler 分流日志(debug 到文件、业务日志到 kafka),logger 级别设为 debug,过滤逻辑由 handler 承担;extra 字段需加前缀防冲突;框架中间件日志应单独降级或截断;trace_id 应自动注入而非手动传参。

为什么 logging.getLogger() 不能直接用默认名
默认调用 logging.getLogger() 返回的是 root logger,所有日志——包括你写的业务逻辑、第三方库的调试输出、甚至 requests 底层的连接重试信息——都会往里塞。一旦开了 DEBUG 级别,日志瞬间被噪音淹没,根本找不到自己要查的那条订单创建失败记录。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 每个模块/服务显式创建独立 logger:
logging.getLogger("order.service")或logging.getLogger(__name__) - 避免在多个地方重复调用
logging.basicConfig(),它只生效一次,后面调用直接忽略,容易误以为配置成功了 - root logger 留给框架或运维统一管控,业务代码绝不直接操作它
怎么让 debug 日志进文件、业务日志进 Kafka
靠 handler 分流,不是靠 level 判断。一个 logger 可以挂多个 handler,各自决定“往哪写”和“写什么级别”,但过滤逻辑必须落在 handler 上,否则日志还没到 handler 就被 logger 的 level 挡掉了。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 给 debug 日志 handler 设
setLevel(logging.DEBUG),再加Filter子类判断是否含"debug"标签(比如日志 record 的extra字典) - 业务日志 handler 设
setLevel(logging.INFO),并绑定专门的KafkaHandler(需自研或用python-logstash类库) - 切记:logger 自身 level 要设成
DEBUG,否则低于 INFO 的日志连 handler 的门都摸不到
extra 字段传参时常见的 key 冲突
很多人习惯用 extra={"user_id": uid} 打点,结果发现日志里 user_id 有时是空、有时是旧值——因为 extra 会透传给所有下游 handler,而某些 handler(比如 RotatingFileHandler)内部也用 user_id 做轮转标识,发生覆盖。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 所有业务字段加统一前缀,例如
extra={"biz_user_id": uid, "biz_order_no": no} - 避免使用 logging 内置字段名:
asctime、levelname、funcName、lineno、module、name等,否则会被静默丢弃 - 在 formatter 的
format()里用%(biz_user_id)s引用,别依赖str(record.__dict__)直接打印
Django/Flask 中如何不污染中间件日志
Web 框架的中间件(如 Django 的 CommonMiddleware、Flask 的 RequestID 插件)往往自带 logger,它们和你的业务 logger 共享同一个命名空间,一开 DEBUG 就全炸出来。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在配置中单独禁用框架 debug 日志:
logging.getLogger("django.middleware").setLevel(logging.WARNING) - 用
NullHandler截断你不关心的 logger:logging.getLogger("urllib3").addHandler(logging.NullHandler()) - 不要在中间件里用
print()或裸logging.info(),统一走你定义好的getLogger("web.request")
logger.info() 里手动传——得靠 LoggerAdapter 或上下文变量自动注入,否则漏一条就断链。这点很多人拖到线上排查时才意识到。










