You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 09:05:20