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

Python日志格式能否获取包含类的全限定函数名?

如何让Python logging记录函数的全限定名称(qualname)

这个需求完全可以实现,核心思路是通过自定义日志处理逻辑,在日志记录生成时获取函数的__qualname__并注入到日志记录对象中,之后就可以在格式字符串中使用该属性。

方案一:自定义Logging Filter

通过Filter在日志记录前添加qualname属性,不修改原有日志核心逻辑,灵活性高。

import logging
import inspect

class QualnameFilter(logging.Filter):
    def filter(self, record):
        stack = inspect.stack()
        try:
            # 跳过logging模块自身的栈帧,定位到业务代码的调用位置
            for stack_frame in stack:
                if not stack_frame.filename.endswith('logging/__init__.py'):
                    qualname = stack_frame.function
                    # 处理类方法/类函数的情况,拼接类名
                    if 'self' in stack_frame.frame.f_locals:
                        qualname = f"{stack_frame.frame.f_locals['self'].__class__.__name__}.{qualname}"
                    elif 'cls' in stack_frame.frame.f_locals:
                        qualname = f"{stack_frame.frame.f_locals['cls'].__name__}.{qualname}"
                    record.qualname = qualname
                    break
        except Exception:
            # 异常时回退到默认的funcName
            record.qualname = record.funcName
        finally:
            del stack
        return True

# 配置并使用Filter
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addFilter(QualnameFilter())

# 日志格式中使用%(qualname)s
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(qualname)s: %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)

方案二:自定义LogRecord类

替换logging默认的LogRecord工厂,从根源上给日志记录对象添加qualname属性。

import logging
import inspect

class QualnameLogRecord(logging.LogRecord):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        stack = inspect.stack()
        try:
            for stack_frame in stack:
                if not stack_frame.filename.endswith('logging/__init__.py'):
                    qualname = stack_frame.function
                    if 'self' in stack_frame.frame.f_locals:
                        qualname = f"{stack_frame.frame.f_locals['self'].__class__.__name__}.{qualname}"
                    elif 'cls' in stack_frame.frame.f_locals:
                        qualname = f"{stack_frame.frame.f_locals['cls'].__name__}.{qualname}"
                    self.qualname = qualname
                    break
        except Exception:
            self.qualname = self.funcName
        finally:
            del stack

# 替换默认的LogRecord工厂
logging.setLogRecordFactory(QualnameLogRecord)

# 配置日志
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(qualname)s: %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)

测试验证

编写测试代码验证效果:

class DemoClass:
    def demo_method(self):
        logger.debug('这是类实例方法的日志')
    
    @classmethod
    def demo_classmethod(cls):
        logger.debug('这是类方法的日志')

def demo_function():
    logger.debug('这是普通函数的日志')

DemoClass().demo_method()
DemoClass.demo_classmethod()
demo_function()

输出示例:

2024-05-20 15:30:00,123 - DEBUG - DemoClass.demo_method: 这是类实例方法的日志
2024-05-20 15:30:00,124 - DEBUG - DemoClass.demo_classmethod: 这是类方法的日志
2024-05-20 15:30:00,124 - DEBUG - demo_function: 这是普通函数的日志

注意事项

  1. 栈帧层级:代码中跳过logging模块栈帧的逻辑,可能需要根据Python版本或logging的具体实现微调栈帧遍历的判断条件。
  2. 性能影响:inspect.stack()会遍历调用栈,频繁记录日志时可能带来一定性能开销,高并发场景建议评估后使用。

内容的提问来源于stack exchange,提问作者Troels Blum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:42:04