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。
关键说明
__init_subclass__会在每个子类定义时自动执行,为每个子类创建独立的logger,名称就是类名。- 在各方法中直接使用
类名.logger输出日志,确保日志绑定到当前方法所属的类,而非实例化的子类。 - 添加
if not cls.logger.handlers判断,避免重复添加日志处理器,防止同一条日志被多次输出。
内容的提问来源于stack exchange,提问作者bangbanggggg
相关产品推荐
相关产品推荐

