logging.debug() 不输出是因为默认日志级别为 warning,debug 和 info 级别日志被层级过滤机制静默丢弃;必须在首次 logging.debug() 前调用 logging.basicconfig(level=logging.debug) 或显式设置 logger 级别。

直接用 logging.debug() 替代 print() 不起作用,是因为默认日志级别是 WARNING,debug() 和 info() 被静默丢弃了。
为什么 logging.debug() 什么也不输出?
Python 的 logging 模块有层级过滤机制:默认根 logger 的 level 是 WARNING,所有低于该级别的日志(如 DEBUG、INFO)直接被忽略,根本不会格式化或输出。
- 必须显式调用
logging.basicConfig(level=logging.DEBUG)才能让debug()生效 - 这个调用必须在**任何
logging.debug()或logging.info()之前执行**,否则无效 - 如果项目中已导入其他模块并触发了 logging 初始化(比如某些框架会自动配置),
basicConfig()将不再生效 —— 此时得改用logging.getLogger().setLevel(logging.DEBUG)
如何让调试日志带文件名、行号和时间?
靠 basicConfig() 的 format 参数就能控制输出样式,不用写 handler 或 formatter 对象。
- 推荐调试用格式:
logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(message)s', level=logging.DEBUG) -
%(asctime)s自动补时间,%(filename)s:%(lineno)d精确定位到哪一行,比手写print(f"line 42: x={x}")靠谱得多 - 注意:
%(asctime)s默认不带毫秒,加datefmt='%Y-%m-%d %H:%M:%S.%f'可补全(%f是微秒,截取前3位就是毫秒)
怎样避免上线后还打印调试日志?
靠环境变量或启动参数动态控制日志级别,而不是删代码或注释 logging.debug()。
- 典型做法:
import os; level = getattr(logging, os.getenv('LOG_LEVEL', 'INFO').upper(), logging.INFO); logging.basicConfig(level=level) - 这样开发时跑
LOG_LEVEL=DEBUG python main.py,生产环境不设变量就自动回落到INFO,debug()依然存在但不触发 - 更稳妥的做法是给每个模块获取独立 logger:
logger = logging.getLogger(__name__),然后单独设级或加 filter,避免全局basicConfig()被覆盖
为什么有时看到重复日志?
常见于多次 import 同一模块、或反复调用 basicConfig(),导致 root logger 添加了多个 handler。
- 根本原因:每次
basicConfig()都会往 root logger 的 handlers 列表里追加一个 StreamHandler,但不会清空旧的 - 验证方式:
print(logging.getLogger().handlers),能看到多个<streamhandler ...></streamhandler> - 解决办法:只在主模块入口(如
if __name__ == '__main__':块内)调用一次basicConfig();或者启动前手动清空:logging.getLogger().handlers.clear()
最易被忽略的是 logger 层级和 handler 层级的双重过滤——既要看 logger 自身的 level,也要看它所绑定的 handler 的 level(后者默认继承前者,但可单独设)。调试时建议统一用 basicConfig() 初始化,别过早碰 addHandler(),否则容易绕进双层过滤的坑里。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











