Python 日志级别设计的最佳实践

1次阅读

Logging.basicconfig() 仅在 root logger 未配置时生效,若第三方库或提前日志调用已初始化 handler,则失效;需确保其为 import logging 后首条日志相关语句,或显式清空 handlers。

Python 日志级别设计的最佳实践

logging.basicConfig() 为什么总不生效

因为 basicConfig() 只在 root logger 尚未配置时才起作用。一旦你或某个第三方库(比如 requestsurllib3)提前调用了 logging.debug() 或初始化了 handler,它就彻底失效了。

  • 检查是否在 import logging 后立刻调用 basicConfig(),且没有任何其他日志调用穿插其中
  • 如果用了 pytest 或某些框架(如 flask),它们可能已预设 root logger,此时必须显式获取并重置:logging.getLogger().handlers.clear()
  • basicConfig() 对子 logger(如 logging.getLogger("my.module"))无影响,子 logger 默认会向 root 传递日志——这是设计,不是 bug

DEBUG/INFO/WARNING/Error/CRITICAL 级别怎么选才不翻车

级别不是按“重要性”分的,而是按“事件发生时开发者需要介入的紧急程度”分的。INFO 不是“随便记点啥”,CRITICAL 也不等于“程序快崩了”。

  • DEBUG:仅开发/调试期启用,含变量值、函数入参、循环索引等临时信息;上线必须关闭(靠 setLevel(logging.INFO) 控制)
  • INFO:用户可感知的正常流程节点,如 "User 'alice' logged in""Starting backup job #123";避免写成 "Got request" 这种无意义废话
  • WARNING:非错误但需关注的情况,比如配置项缺失用默认值、API 返回了 404 但逻辑可兜底;不是“提醒你注意”,而是“这事本不该发生但程序还能跑”
  • ERROR:发生了明确异常且无法恢复,比如数据库连接失败、文件读取权限被拒;要附带 exc_info=True 或手动捕获 traceback.format_exc()
  • CRITICAL:系统级故障,如磁盘满导致日志写入失败、核心配置加载失败致服务无法启动;极少用,慎用

Handler 写文件 vs 输出到 stdout 的真实取舍

本地开发用 StreamHandler(sys.stdout) 没问题,但一上容器或 systemd 就容易丢日志——stdout 被重定向、缓冲区未 flush、日志轮转缺失,全都会让你查不到东西。

  • 生产环境优先用 RotatingFileHandlerTimedRotatingFileHandler,并设置 backupCount=7maxBytes=10485760(10MB)防磁盘打满
  • 容器中若坚持输出 stdout,请确保 StreamHandlerstreamsys.stdout(不是 sys.stderr),且 python 启动加 -u 参数强制无缓冲
  • 别在代码里硬编码路径如 "/var/log/myapp/app.log";用 os.getenv("LOG_DIR", "/tmp") + os.path.join(),方便测试和容器挂载

logger = logging.getLogger(__name__) 中 __name__ 的坑

__name__ 看似省事,但模块名层级一深,比如 myproject.utils.db.helpers,日志前缀就会又长又难读,而且不同模块同名(如多个 utils.py)会导致 logger 实例混用。

立即学习Python免费学习笔记(深入)”;

  • 如果模块结构扁平(如 main.pyapi.pyworker.py),直接用 __name__ 安全
  • 若存在嵌套包(src/ 下多层目录),建议统一用短标识:logging.getLogger("db")logging.getLogger("cache"),并在各模块开头显式定义常量 LOGGER = logging.getLogger("db")
  • 切勿在函数内反复调用 getLogger()——它是轻量缓存的,但重复写降低可读性;也别用字符串拼接构造 logger 名,比如 getLogger(f"{prefix}.worker"),会让日志归类和过滤变复杂

事情说清了就结束。最常被忽略的是:日志级别控制不能只靠 basicConfig(),而 handler 的生命周期(比如没 close)、formatter 的时间格式(默认不带毫秒)、以及多进程下 RotatingFileHandler 的竞态问题——这些都不在基础配置里,得单独处理。

text=ZqhQzanResources