Python logging:如何动态修改日志文件名?清除处理器为何无效?
代码结构
. ├── scripts │ └── test.py └── utils ├── __init__.py └── lib.py
原日志配置及使用
在utils/__init__.py中初始化了模块级别的日志:
import logging logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) ch = logging.FileHandler("logfile", mode="w") ch.setLevel(logging.DEBUG) formatter = logging.Formatter( "%(levelname)s:%(name)s:[%(funcName)s]:%(message)s", "%m/%d/%Y %I:%M:%S %p" ) ch.setFormatter(formatter) logger.addHandler(ch)
utils/lib.py中使用日志:
LOGGER = logging.getLogger(__name__) def a_function(): LOGGER.info('This works')
需求与问题
需要每次运行不同脚本时使用不同的日志文件名,避免覆盖或追加到同一日志。尝试在scripts/test.py中修改日志配置:
import logging from utils.lib import a_function def main(): # rename handler fh = logging.FileHandler("logfile2", "w") formatter = logging.Formatter( "%(levelname)s:%(name)s:[%(funcName)s]:%(message)s", "%m/%d/%Y %I:%M:%S %p" ) fh.setFormatter(formatter) log = logging.getLogger() log.handlers.clear() log.addHandler(fh) a_function() if __name__ == '__main__': main()
执行后logfile2会生成并写入日志,但原logfile中依然会有日志输出,log.handlers.clear()并未清除原处理器。
错误原因
你操作的是根logger(logging.getLogger()不带参数返回根实例),但原日志配置是绑定在utils模块的logger(logging.getLogger(__name__),__name__的值为utils)上的。logging的logger是层级结构,根logger和utils模块logger是相互独立的实例,清空根logger的handlers不会影响utils logger已添加的handler,所以原logfile仍会被写入。
最佳解决方法
方法1:封装日志配置函数(推荐)
将日志配置逻辑封装成可传入文件名的函数,替代utils/__init__.py中的直接初始化:
修改utils/__init__.py:
import logging logger = logging.getLogger(__name__) def setup_logger(log_filename): logger.setLevel(logging.DEBUG) # 先清空已有handler,避免重复添加 logger.handlers.clear() ch = logging.FileHandler(log_filename, mode="w") ch.setLevel(logging.DEBUG) formatter = logging.Formatter( "%(levelname)s:%(name)s:[%(funcName)s]:%(message)s", "%m/%d/%Y %I:%M:%S %p" ) ch.setFormatter(formatter) logger.addHandler(ch)
utils/lib.py无需修改,因为logging.getLogger(__name__)会继承utils模块logger的配置。
在scripts/test.py中调用配置函数:
from utils import setup_logger from utils.lib import a_function def main(): setup_logger("logfile2") a_function() if __name__ == '__main__': main()
这种方式灵活性高,每个脚本都可以传入不同的日志文件名,同时避免重复添加handler。
方法2:直接修改utils模块的logger
如果不想修改原有初始化逻辑,也可以直接获取utils模块的logger并修改其handler:
修改scripts/test.py:
import logging from utils.lib import a_function def main(): # 获取utils模块的logger实例 utils_logger = logging.getLogger("utils") # 清空它的所有handler utils_logger.handlers.clear() # 添加新的文件handler fh = logging.FileHandler("logfile2", "w") formatter = logging.Formatter( "%(levelname)s:%(name)s:[%(funcName)s]:%(message)s", "%m/%d/%Y %I:%M:%S %p" ) fh.setFormatter(formatter) utils_logger.addHandler(fh) a_function() if __name__ == '__main__': main()
内容的提问来源于stack exchange,提问作者gammapoint

