Python logging.info二次运行生成空日志文件问题求助
这个问题我太熟悉了——本质是logging模块的全局配置特性和Spyder/IPython的内核持久化机制在搞鬼!
问题根源
Spyder的IPython控制台默认会保持Python内核持续运行(除非你手动重启内核或关闭控制台标签)。第一次运行你的代码时,logging.basicConfig()会初始化根日志器,添加你指定的FileHandler和StreamHandler。但后续再次运行代码时,basicConfig()默认不会重新配置已初始化的日志系统(这是logging模块的设计逻辑:避免重复配置导致日志混乱),所以新创建的FileHandler根本没被添加到根日志器里——控制台的StreamHandler还在工作,所以你能看到输出,但新的日志文件因为没有处理器写入,自然是空的。
解决方案
根据你的需求(每次运行生成全新日志文件,不追加),给你几个可行的方案:
方案1:清理已有处理器后重新配置(最兼容你的Python版本)
在调用basicConfig()之前,先移除根日志器的所有已有处理器,确保每次运行都能加载新的配置。修改你的代码如下:
import logging import datetime starting_time = datetime.datetime.now() logfilename = ('SomeLog' + datetime.datetime.now().strftime("%Y-%m-%d-%H-%M") + '.txt') level = logging.INFO format = ' %(message)s' # 关键步骤:清除根日志器的所有已有处理器 root_logger = logging.getLogger() for handler in root_logger.handlers[:]: root_logger.removeHandler(handler) handler.close() # 关闭旧处理器,释放文件资源 handlers = [logging.FileHandler(logfilename, mode='w'), logging.StreamHandler()] logging.basicConfig(level=level, format=format, handlers=handlers) # 你的业务代码... logging.info('Data written succesfully to: sqlite_table') logging.info('Elapsed time to finish SQL write: ' + str((datetime.datetime.now() - starting_time).seconds) + ' seconds') logging.info("Total elapsed time: " + str((datetime.datetime.now() - starting_time).seconds) + ' seconds') logging.shutdown()
这里给FileHandler显式指定mode='w'(虽然默认就是写模式,但明确写出来更清晰),同时关闭旧处理器避免资源泄漏。
方案2:使用force=True参数(仅Python 3.8+适用)
如果你的Python版本是3.8或更高(注意你的Anaconda2019.03自带的是Python3.7,这个方法不适用),可以直接给basicConfig()加force=True参数,强制覆盖已有配置:
handlers = [logging.FileHandler(logfilename, mode='w'), logging.StreamHandler()] logging.basicConfig(level=level, format=format, handlers=handlers, force=True)
方案3:手动创建独立日志器(更灵活的长期方案)
如果以后你的日志需求变复杂,推荐不要依赖根日志器,而是创建自己的日志器,完全控制配置逻辑:
import logging import datetime starting_time = datetime.datetime.now() logfilename = ('SomeLog' + datetime.datetime.now().strftime("%Y-%m-%d-%H-%M") + '.txt') level = logging.INFO format = ' %(message)s' # 创建自己的日志器 logger = logging.getLogger('MyAppLogger') logger.setLevel(level) # 清除已有处理器(防止重复添加) for handler in logger.handlers[:]: logger.removeHandler(handler) handler.close() # 添加文件处理器和控制台处理器 file_handler = logging.FileHandler(logfilename, mode='w') file_handler.setFormatter(logging.Formatter(format)) stream_handler = logging.StreamHandler() stream_handler.setFormatter(logging.Formatter(format)) logger.addHandler(file_handler) logger.addHandler(stream_handler) # 业务代码中用自己的日志器 logger.info('Data written succesfully to: sqlite_table') logger.info('Elapsed time to finish SQL write: ' + str((datetime.datetime.now() - starting_time).seconds) + ' seconds') logger.info("Total elapsed time: " + str((datetime.datetime.now() - starting_time).seconds) + ' seconds') logging.shutdown()
额外提示
如果你不想每次改代码,也可以在Spyder中设置"每次运行前重启内核":点击运行按钮旁边的下拉菜单,选择"Run settings",勾选"Restart kernel before running"。但这样每次运行都会重新加载环境,速度会慢一点,适合简单脚本。
内容的提问来源于stack exchange,提问作者TomH

