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

Python日志Mixin:如何让日志前缀显示来源类而非启动类

问题原因

你的问题出在多继承中实例的self.__class__指向最终实例化的子类(Wrangling),导致LogMixin中基于self.__class__.__name__创建的logger,所有父类(Query、Processing)的日志都会复用这个子类的logger,从而前缀统一显示为Wrangling。

解决方法

让每个类(Query、Processing、Wrangling)拥有独立的类级别logger,而非依赖实例的self.__class__。可以利用Python 3.6+的__init_subclass__特性,自动为每个子类初始化专属logger,然后在各方法中直接使用类的logger输出日志。

完整修改代码示例

import logging

class LogMixin:
    @classmethod
    def __init_subclass__(cls):
        # 为每个子类创建专属logger,名称为类名
        cls.logger = logging.getLogger(cls.__name__)
        # 配置日志格式,确保前缀显示类名
        formatter = logging.Formatter('%(name)s - %(levelname)s: %(message)s')
        handler = logging.StreamHandler()
        handler.setFormatter(formatter)
        # 避免重复添加handler(防止日志重复输出)
        if not cls.logger.handlers:
            cls.logger.addHandler(handler)
            cls.logger.setLevel(logging.INFO)

class Database:
    def __init__(self, db_url):
        self.db_url = db_url
        # 数据库初始化逻辑(示例)
        print(f"Connected to database: {db_url}")

class Query(LogMixin, Database):
    def __init__(self, db_url):
        super().__init__(db_url)
    
    def fetch_data(self):
        # 使用Query类的logger输出日志
        Query.logger.info("Executing data query")
        # 模拟查询逻辑
        return {"raw_data": [1, 2, 3]}

class Processing(LogMixin, Database):
    def __init__(self, db_url):
        super().__init__(db_url)
    
    def process_data(self, data):
        # 使用Processing类的logger输出日志
        Processing.logger.info("Processing raw data")
        # 模拟处理逻辑
        return {"processed_data": [x*2 for x in data["raw_data"]]}

class Wrangling(Query, Processing):
    def __init__(self, db_url):
        super().__init__(db_url)
    
    def wrangle(self):
        data = self.fetch_data()
        processed_data = self.process_data(data)
        # 使用Wrangling类的logger输出日志
        Wrangling.logger.info("Data wrangling finished")
        return processed_data

# 测试代码
if __name__ == "__main__":
    wrangler = Wrangling("sqlite:///test.db")
    result = wrangler.wrangle()
    print(result)

输出效果

运行后日志会显示:

Connected to database: sqlite:///test.db
Query - INFO: Executing data query
Processing - INFO: Processing raw data
Wrangling - INFO: Data wrangling finished
{'processed_data': [2, 4, 6]}

可以看到,每个日志的前缀都正确显示了日志来源的具体类名(Query、Processing、Wrangling),而非统一显示为Wrangling。

关键说明

  1. __init_subclass__会在每个子类定义时自动执行,为每个子类创建独立的logger,名称就是类名。
  2. 在各方法中直接使用类名.logger输出日志,确保日志绑定到当前方法所属的类,而非实例化的子类。
  3. 添加if not cls.logger.handlers判断,避免重复添加日志处理器,防止同一条日志被多次输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:41:27