DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

Python 再次调用 logging.basicConfig 后,日志仍写到原来的位置

root 已有 handler 时,basicConfig 默认不会覆盖配置。先确认配置来源,优先在启动阶段只配置一次;需要重配时明确 force 的版本与关闭旧 handler 的影响。

程序启动时把日志写到终端,读取命令行参数后再调用 logging.basicConfig(filename="app.log"),日志却没有切到文件。若 root logger 已经安装 handler,普通的第二次调用不会重新配置它。

下面针对 Python 标准库 logging。重新配置示例要求 Python 3.8 及以上;如果日志由 Web 框架、测试框架或其他宿主程序管理,应先使用宿主提供的配置入口。

确认 root logger 是否已经配置

在第二次配置之前,用普通打印观察状态,避免诊断输出本身先触发日志初始化:

import logging

root = logging.getLogger()
print("root level:", logging.getLevelName(root.level))
for handler in root.handlers:
    print(type(handler).__name__, logging.getLevelName(handler.level))

只要列表中已有 handler,未指定强制重配的 basicConfig() 就不会覆盖它。已有配置可能来自第一次显式调用,也可能来自导入模块执行的日志代码:模块级 logging.info() 等便捷函数在 root 没有 handler 时,会自动调用基础配置。

当前文件只调用一次,不代表整个进程此前没有配置日志。

能调整启动流程时,只配置一次

更稳妥的方式是先读取参数,再决定输出到终端还是文件,然后在启动工作线程前配置 logging。下面是独立脚本的示例:

import argparse
import logging

parser = argparse.ArgumentParser()
parser.add_argument("--log-file")
args = parser.parse_args()

options = {
    "level": logging.INFO,
    "format": "%(levelname)s:%(name)s:%(message)s",
}
if args.log_file:
    options["filename"] = args.log_file

logging.basicConfig(**options)
logging.getLogger("app").info("startup complete")

未指定文件时使用默认终端输出;指定文件时使用文件 handler。此路径以启动前没有其他代码抢先配置 root 为前提。文件路径是使用者传入的目标,需确认父目录存在且进程可写。

不要同时传入 filename 和 stream。当前 Python 的这两个参数互斥,同时提供会抛出 ValueError,不是后一个被静默忽略。

确实需要切换时,Python 3.8+ 可显式重配

如果应用自己拥有整个 root 日志配置,可以在确定切换的时点执行:

import logging

logging.basicConfig(
    filename="app.log",
    level=logging.INFO,
    format="%(levelname)s:%(name)s:%(message)s",
    force=True,
)
logging.getLogger("app").info("file logging enabled")

force=True 会移除并关闭 root 上已有的 handler,再应用新配置。因此它会影响进程里依赖这些 handler 的其他代码。不要把它放进通用库的导入路径,也不要在框架接管日志的进程里不加判断地使用。

默认文件模式是追加,不必为了验证配置而删除旧日志。若只是想修改级别,可以针对实际 logger 或 handler 调用 setLevel(),不需要重建输出位置。两层级别都会影响输出,子 logger 自己的级别和传播设置也不能靠重配 root 一并解决。

检查切换结果并保留恢复方式

在开发环境写入一条可识别的 INFO 消息,检查目标文件出现该消息,并确认原输出位置是否符合切换预期。若文件里仍没有记录,检查命名 logger 的有效级别、它自己的 handler 以及 propagate,再检查目录权限;这些属于配置成功之后的过滤或写入问题。

如果同时出现 Logging error 和 TypeError: not all arguments converted during string formatting,还需检查日志消息的占位符与参数。重设 handler 不会修正消息格式。

强制重配会关闭旧 handler,不能指望删掉这一行就恢复当前进程已经关闭的输出对象。修改前保留完整日志配置,需要回退时重新应用原配置,或通过正常启动流程重启开发实例。Python 3.8 之前没有 force,优先整理为一次初始化,或使用应用既有的完整日志配置机制。