如何在持续运行的Python应用中动态修改Logging level?
如何在运行时修改Python应用的日志级别
嘿,我来帮你搞定这个运行时修改日志级别的问题!先看看你的现有代码里需要调整的地方,再一步步给出可行的解决方案:
现有代码的小问题
- 你的
run循环每次都会调用getLogger,虽然做了if not len(logger.handlers)的判断,但一旦logger初始化完成,后续调用就不会再更新日志级别了——因为setLevel只在第一次创建handler时执行。 - 目前没有结合
ConfigParser实现配置的动态读取,没法自动感知配置文件的修改。
解决方案拆解
1. 实现配置的动态读取
首先用RawConfigParser加载你的配置文件,然后在循环里定时重新读取,这样就能实时获取最新的myLoggingLevel值。
2. 实时更新已存在Logger的级别
拿到新的日志级别后,不仅要更新logger本身的级别,还要更新它所有handler的级别——因为每个handler也有自己的级别过滤规则,只更新logger的话可能不会生效。
3. 优化Logger的创建逻辑
避免每次循环都尝试创建新Logger,应该复用已经存在的实例,只在配置变化时更新级别,这样也能避免重复添加handler导致日志重复输出。
修改后的完整代码
import time import logging import datetime from configparser import RawConfigParser def getLogger(loggerName='myLoggerName', logLevel='INFO', log_path='C:/logs/'): class Formatter(logging.Formatter): def formatTime(self, record, datefmt=None): return (datetime.datetime.utcnow()).strftime('%H:%M:%S') logLevel = logLevel.upper() levels = { 'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR, 'CRITICAL': logging.CRITICAL } today = datetime.datetime.utcnow().strftime('%Y-%m-%d') logger_key = f"{loggerName}.{today}" logger = logging.getLogger(logger_key) if not len(logger.handlers): # 第一次初始化时设置基础配置 logger.setLevel(levels[logLevel]) logger.propagate = False # 文件Handler配置 full_log_path = log_path + '%s.%s.log' % (loggerName, today) fh = logging.FileHandler(full_log_path) formatter = Formatter('%(asctime)s.%(msecs)03d | %(message)s', datefmt='%H:%M:%S') fh.setFormatter(formatter) fh.setLevel(levels[logLevel]) # 给文件Handler也设置对应级别 logger.addHandler(fh) # 控制台Handler配置(这里固定为ERROR级别,需要的话也可以从配置读取) ch = logging.StreamHandler() ch.setLevel(logging.ERROR) ch.setFormatter(formatter) logger.addHandler(ch) logger.info(f'Logger initialized: {loggerName}, current level: {logLevel}') return logger def update_logger_level(logger, new_level): """更新已存在Logger的级别及其Handler的级别""" levels = { 'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR, 'CRITICAL': logging.CRITICAL } new_level_upper = new_level.upper() # 处理无效的日志级别 if new_level_upper not in levels: logger.warning(f"Invalid log level '{new_level}' detected, falling back to INFO") new_level_upper = 'INFO' # 更新Logger本身的级别 logger.setLevel(levels[new_level_upper]) # 更新所有文件Handler的级别(控制台Handler如果需要跟随变化可以去掉判断) for handler in logger.handlers: if isinstance(handler, logging.FileHandler): handler.setLevel(levels[new_level_upper]) logger.info(f"Successfully updated log level to: {new_level_upper}") def load_config(config_path='config.ini'): """从配置文件读取当前的日志级别""" config = RawConfigParser() config.read(config_path) # 假设你的配置文件有[Logging] section,key为myLoggingLevel return config.get('Logging', 'myLoggingLevel', fallback='INFO') def run(): # 初始化配置和Logger current_level = load_config() log = getLogger(loggerName='testLogLevel', logLevel=current_level) while True: # 每次循环读取最新配置,检查是否有变化 new_level = load_config() if new_level != current_level: current_level = new_level update_logger_level(log, current_level) # 业务日志输出示例 log.debug('I am in debug') log.info('I am in info') time.sleep(3) if __name__ == '__main__': run()
配套的配置文件示例(config.ini)
[Logging] myLoggingLevel = DEBUG
关键细节说明
- 动态感知配置变化:每次循环都会重新读取配置文件,只要你修改
config.ini里的myLoggingLevel,应用会在3秒内感知到并更新级别。 - 完整的级别更新:
update_logger_level函数同时更新logger和handler的级别,确保新的过滤规则立即生效。 - Logger复用:只在第一天启动时创建handler,后续复用同一个logger实例,避免日志重复输出的问题。
- 容错处理:如果配置里写了无效的日志级别,会自动 fallback 到INFO,避免应用崩溃。
内容的提问来源于stack exchange,提问作者mcsy
相关产品推荐
相关产品推荐

