如何让Python所有日志调用继承调用模块的名称?
问题描述
我正在构建一系列相互关联的应用,基类定义在base.py文件中,子类定义在class.py中,代码示例如下:
# base.py logger = logging.getLogger(__name__) class Base(): def __init__(self): logger.info("Parent success") # class.py logger = logging.getLogger(__name__) class Child(Base): def __init__(self): super().__init__() logger.info("Child success") # main.py this = Child()
需求:希望所有日志都显示调用类的模块名称(即父类和子类的日志都显示来自main.py),且不能通过__init__参数传递logger,求更优雅的解决方案。
优雅解决方案
方法1:自定义日志过滤器改写模块名
无需修改原有类的代码,只需在入口模块配置一个日志过滤器,自动将日志记录的模块名替换为实际调用者的模块名。
# 自定义过滤器 import logging import inspect class CallerModuleFilter(logging.Filter): def filter(self, record): try: # 回溯调用栈,跳过日志内部帧,定位业务代码调用帧 for frame_info in inspect.stack()[1:]: frame = frame_info[0] module = inspect.getmodule(frame) if module and not module.__name__.startswith("logging"): record.name = module.__name__ record.module = module.__name__ break except Exception: pass return True
在入口模块(比如main.py)中配置日志:
# main.py import logging from class import Child if __name__ == "__main__": # 配置日志并添加过滤器 logging.basicConfig(format="%(name)s - %(levelname)s - %(message)s") root_logger = logging.getLogger() root_logger.addFilter(CallerModuleFilter()) this = Child()
这种方式对原有代码侵入性极低,适合多模块复用的场景,所有日志都会自动显示调用者的模块名称。
方法2:通过调用栈动态获取调用者的Logger
在基类和子类的__init__中,利用inspect模块获取创建实例的调用者模块,并使用该模块的Logger。
修改base.py:
# base.py import logging import inspect class Base(): def __init__(self): # 获取调用栈中创建实例的帧 caller_frame = inspect.stack()[1] caller_module = inspect.getmodule(caller_frame[0]) self.logger = logging.getLogger(caller_module.__name__) self.logger.info("Parent success")
修改class.py:
# class.py import logging import inspect from base import Base class Child(Base): def __init__(self): super().__init__() caller_frame = inspect.stack()[1] caller_module = inspect.getmodule(caller_frame[0]) self.logger = logging.getLogger(caller_module.__name__) self.logger.info("Child success")
这种方式直接在类内部获取调用者的Logger,逻辑直观,但inspect.stack()会有轻微性能开销,高并发场景需权衡。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

