一个FileHandler无法同时输出到控制台和文件,因为日志器需显式添加StreamHandler和FileHandler两个处理器;正确做法是清空原有handlers后分别添加并设置级别与格式化器。

为什么只用一个 FileHandler 无法同时输出到控制台和文件
因为 logging.Logger 默认不带任何处理器(Handler),即使你调用了 basicConfig(),它也只在根 logger 没有 handler 时生效一次。一旦你手动添加了 FileHandler,再想让日志也刷到控制台,就必须显式加一个 StreamHandler——不是“自动多路”,而是“得自己配齐”。
如何正确添加两个 Handler:控制台 + 文件
核心是:获取 logger 实例 → 清空默认 handler(避免重复)→ 分别添加 StreamHandler 和 FileHandler → 统一设置 level 和 formatter。
- 务必调用
logger.handlers.clear(),否则可能因残留 handler 导致日志重复打印 -
StreamHandler默认输出到sys.stderr,如需输出到 stdout,要显式传入sys.stdout -
FileHandler的mode='a'是默认值,但建议写明,避免误用'w'清空历史日志 - 两个 handler 必须各自调用
setFormatter(),formatter 不会自动继承
import logging
import sys
<p>logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG)</p><h1>清掉可能存在的旧 handler</h1><p>logger.handlers.clear()</p><h1>控制台输出</h1><p>console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)</p><h1>文件输出</h1><p>file_handler = logging.FileHandler('app.log', mode='a', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter) # 复用同一个 formatter
logger.addHandler(file_handler)
常见错误:日志重复出现两次
典型现象是同一行日志在终端或文件里打两遍,根源几乎都是 handler 被重复添加。比如:
立即学习“Python免费学习笔记(深入)”;
- 在模块里多次执行
logger.addHandler(...)(例如被 import 多次或函数反复调用) - 没清空
logger.handlers,而代码又运行了两次 - 既用了
basicConfig()又手动 addHandler,导致根 logger 和自定义 logger 各有一套
验证方式:打印 len(logger.handlers),正常应为 2;若为 4 或更多,说明重复注册了。
进阶注意:不同 handler 设置不同 level 的实际效果
可以且应该这么做——比如让文件记录全部 DEBUG 日志,但控制台只显示 WARNING 及以上。关键点在于:每个 handler 自己的 setLevel() 独立生效,互不影响,但都受 logger 自身 level 约束(即 logger.level 是第一道闸门)。
- 若
logger.setLevel(logging.WARNING),那就算FileHandler设了DEBUG,也收不到低于 WARNING 的日志 - 推荐设 logger level 为最低需求(如
DEBUG),再靠各 handler 的 level 做分流 - 注意
StreamHandler输出到终端时,INFO/DEBUG 级别不会自动高亮,如需颜色可配合colorama或自定义 formatter
复杂点在于 formatter 中的 %(pathname)s、%(lineno)d 等字段,在多线程环境下仍准确,但如果你用了异步日志(如 concurrent_log_handler),就得确认其是否兼容 threading.local 上下文。


















