1. 问题现象
在 Python 中我们常用
来初始化日志。例如:
logging.basicConfig()
import logging
log_file = "save/logging/app.log"
logging.basicConfig(
filename=log_file,
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
filemode="w"
)
logging.info("程序启动")
正常情况下,执行后会在
目录下生成
save/logging/
文件,并写入日志内容。
app.log
但在实际开发中,可能会遇到这样的问题:
没有报错
目录存在
调用了
等方法
logging.info()
结果就是
文件没有生成
.log
这就让人很困惑:明明代码和之前一样,为什么突然就不写日志了?
2. 原因分析:
logging.basicConfig()
的机制
logging.basicConfig()
要理解这个现象,需要知道
的内部机制。
basicConfig()
2.1
basicConfig()
只生效一次
basicConfig()
的作用是为 root logger 配置默认的 handler 和 formatter。
logging.basicConfig()
但它有一个“保护机制”:只有在 root logger 上没有任何 handler 时才会生效。
一旦 root logger 已经存在 handler,再调用
就会 直接返回,不做任何事。
basicConfig()
2.2 为什么会“突然不生效”?
在很多项目里,可能你在调用
之前,某个第三方库已经用过 logging 了。
basicConfig()
常见的库比如:
requests
matplotlib
flask
sqlalchemy
……
这些库在 import 或使用时,可能会触发 logging,从而自动给 root logger 添加一个默认的
(输出到控制台)。
StreamHandler
这样一来:
root logger 上已经有 handler 了
再调用
就会被忽略
basicConfig(filename=...)
结果就是:日志既不会写入文件,也不会报错
2.3 为什么没有报错?
logging 的设计理念是“尽量不中断业务逻辑”。
因此,当
被忽略时,它不会提示你,也不会抛出异常,只是悄悄不生效。
basicConfig()
这就是为什么会出现「没有文件、也没有报错」的现象。
3. 解决方法
既然问题的根源是:
只有在 root logger 没有 handler 时才生效,那对应的解决方法就很明确了。
basicConfig()
方法一:在调用
basicConfig()
前清理 handler
basicConfig()
import logging, os
log_file = "save/logging/app.log"
os.makedirs(os.path.dirname(log_file), exist_ok=True)
# 清理 root logger 已有的 handler
for h in logging.root.handlers[:]:
logging.root.removeHandler(h)
logging.basicConfig(
filename=log_file,
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
filemode="w"
)
logging.info("日志系统启动成功")
这样可以确保
一定会生效。
basicConfig()
方法二:不用
basicConfig
,手动添加
FileHandler
basicConfig
FileHandler
在生产代码中,更推荐直接手动配置 logger,而不是依赖
:
basicConfig
import logging, os
log_file = "save/logging/app.log"
os.makedirs(os.path.dirname(log_file), exist_ok=True)
logger = logging.getLogger() # 获取 root logger
logger.setLevel(logging.INFO)
# 避免重复添加 handler
if not any(isinstance(h, logging.FileHandler) for h in logger.handlers):
fh = logging.FileHandler(log_file, mode="w", encoding="utf-8")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.info("日志系统启动成功")
这种方式更可控,避免了
的“只生效一次”陷阱。
basicConfig()
4. 总结
只在 root logger 没有 handler 时才会生效
logging.basicConfig()
如果你发现配置了
却没有生成日志文件,大概率是因为 某个库先触发了 logging,导致 root logger 已经有 handler
filename
解决方法有两个:
清理 root logger 的 handler 后再调用
basicConfig()
不依赖
,直接手动配置
basicConfig()
FileHandler
👉 在实际项目中,推荐 方法二:手动添加
,可控性更高,也能避免掉这个常见坑。
FileHandler
暂无评论内容