Python:logging.Adapter的stacklevel参数无法正常工作问题
问题:logging.Adapter的stacklevel参数无法生效
经测试发现,stacklevel参数在logging.LoggerAdapter中无法正常工作:直接使用logger实例时,该参数能按预期调整日志显示的调用位置;但通过自定义的LoggingAdapter调用日志方法时,stacklevel参数完全失效,日志显示的调用位置不符合预期。
测试代码
import logging import traceback # 初始化基础logger logging.basicConfig( format='%(asctime)s[%(levelname)s] - %(name)s - %(filename)s - %(funcName)s() - %(message)s', level=logging.DEBUG ) logger = logging.getLogger('dev') class LoggingAdapter(logging.LoggerAdapter): def process(self, msg, kwargs): loggingTag = kwargs.pop('loggingTag', self.extra['loggingTag']) return '[%s] %s' % (loggingTag, msg), kwargs loggerAdapter = LoggingAdapter(logger, {'loggingTag': 'sometag'}) traceback.print_stack() # 直接使用logger测试 logger.debug("Hello - Debug", stacklevel=3) logger.info("Hello - Info", stacklevel=3) logger.exception("Hello - Exception", stacklevel=4) # 使用Adapter测试 loggerAdapter.debug("Hello - Debug", stacklevel=3, loggingTag='NewTag') loggerAdapter.info("Hello - Info", stacklevel=3, loggingTag='NewTag') loggerAdapter.exception("Hello - Exception", stacklevel=4, loggingTag='NewTag')
输出结果
File "loggerframework/testLoggerUtil.py", line 27, in <module> newAddition("Hello,", " good morning!") File "loggerframework/<...>/common_lib/logging_lib/logger.py", line 100, in __call__ return wrapper(*args, **kwargs) File "loggerframework/<..>/common_lib/logging_lib/logger.py", line 72, in wrapper traceback.print_stack() 2022-11-22 11:26:17,277[DEBUG] - dev - testLoggerUtil.py - <module>() - Hello - Debug 2022-11-22 11:26:17,277[INFO] - dev - testLoggerUtil.py - <module>() - Hello - Info 2022-11-22 11:26:17,277[ERROR] - dev - testLoggerUtil.py - <module>() - Hello - Exception NoneType: None 2022-11-22 11:26:17,277[DEBUG] - dev - logger.py - wrapper() - [NewTag] Hello - Debug 2022-11-22 11:26:17,277[INFO] - dev - logger.py - wrapper() - [NewTag] Hello - Info 2022-11-22 11:26:17,277[ERROR] - dev - logger.py - wrapper() - [NewTag] Hello - Exception NoneType: None
问题分析
直接调用logger的方法时,stacklevel参数会让日志框架跳过指定层数的栈帧,显示正确的调用位置(此处为testLoggerUtil.py的<module>());但通过LoggingAdapter调用时,Adapter的方法本身会增加一层栈帧,而默认的LoggerAdapter实现没有对stacklevel参数做调整,导致底层logger计算调用位置时,没有跳过Adapter这一层,最终显示的是Adapter内部或包装函数的位置(logger.py的wrapper())。
解决方案
重写LoggingAdapter的log方法(或对应级别的方法),将stacklevel参数加1后传递给父类方法,以此抵消Adapter本身带来的额外栈帧:
通用方案(重写log方法)
class LoggingAdapter(logging.LoggerAdapter): def process(self, msg, kwargs): loggingTag = kwargs.pop('loggingTag', self.extra['loggingTag']) return '[%s] %s' % (loggingTag, msg), kwargs def log(self, level, msg, args=(), exc_info=None, extra=None, stacklevel=1): # 增加stacklevel以跳过Adapter的log方法这一层栈帧 super().log(level, msg, args, exc_info, extra, stacklevel + 1)
针对特定级别方法的方案
如果只需要调整部分日志级别的行为,可以单独重写对应方法:
class LoggingAdapter(logging.LoggerAdapter): def process(self, msg, kwargs): loggingTag = kwargs.pop('loggingTag', self.extra['loggingTag']) return '[%s] %s' % (loggingTag, msg), kwargs def debug(self, msg, *args, **kwargs): stacklevel = kwargs.pop('stacklevel', 1) super().debug(msg, *args, stacklevel=stacklevel + 1, **kwargs) def info(self, msg, *args, **kwargs): stacklevel = kwargs.pop('stacklevel', 1) super().info(msg, *args, stacklevel=stacklevel + 1, **kwargs) def exception(self, msg, *args, **kwargs): stacklevel = kwargs.pop('stacklevel', 1) super().exception(msg, *args, stacklevel=stacklevel + 1, **kwargs)
修改后再次运行测试代码,Adapter的日志调用会显示和直接使用logger时一致的正确调用位置。
内容的提问来源于stack exchange,提问作者user20569209
相关产品推荐
相关产品推荐

