Python封装Logger后filename始终显示logger.py,如何修改或调整设计?
解决Python日志filename显示错误的问题
问题根源
你遇到的情况是因为Python的logging模块默认记录的filename是日志记录器类(MyLogger)所在的文件路径(也就是logger.py),而不是调用日志方法(比如debug)的文件路径。这是模块的默认行为,和你当前的依赖注入、面向对象设计本身无关,不需要调整核心结构。
可行解决方案
方法1:用inspect获取调用者上下文,自定义日志字段
通过inspect模块获取调用栈的上一层帧信息,拿到实际调用日志方法的文件名和行号,然后通过extra参数传递给日志记录器,最后在日志格式中使用自定义字段替代默认的filename。
示例代码:
# logger.py import logging import inspect import os class MyLogger: def __init__(self): self.logger = logging.getLogger("my_app") # 配置日志格式,使用自定义的caller_filename和caller_lineno formatter = logging.Formatter( "%(asctime)s - %(caller_filename)s:%(caller_lineno)s - %(levelname)s - %(message)s" ) handler = logging.FileHandler("app.log") handler.setFormatter(formatter) self.logger.addHandler(handler) self.logger.setLevel(logging.DEBUG) def _get_caller_info(self): # 跳过当前方法的栈帧,获取调用者的帧信息 caller_frame = inspect.currentframe().f_back.f_back return { "caller_filename": os.path.basename(caller_frame.f_code.co_filename), # 只保留文件名 "caller_lineno": caller_frame.f_lineno } def debug(self, msg, *args, **kwargs): caller_info = self._get_caller_info() self.logger.debug(msg, *args, extra=caller_info, **kwargs) # 对info/warn/error等方法做同样处理 def info(self, msg, *args, **kwargs): caller_info = self._get_caller_info() self.logger.info(msg, *args, extra=caller_info, **kwargs)
在Client类中正常注入使用:
# client.py class Client: def __init__(self, logger): self.logger = logger def run(self): self.logger.debug("Client执行了run方法")
方法2:使用LoggerAdapter绑定调用者上下文
如果希望每个注入的类自动绑定自身的文件信息,可以用logging.LoggerAdapter为每个类生成带上下文的日志适配器,减少重复代码。
示例代码:
# logger.py import logging import inspect import os class MyLogger: def __init__(self): self.base_logger = logging.getLogger("my_app") formatter = logging.Formatter( "%(asctime)s - %(caller_filename)s:%(caller_lineno)s - %(levelname)s - %(message)s" ) handler = logging.FileHandler("app.log") handler.setFormatter(formatter) self.base_logger.addHandler(handler) self.base_logger.setLevel(logging.DEBUG) def get_contextual_logger(self, caller_file): caller_filename = os.path.basename(caller_file) # 自定义Adapter,动态获取调用行号 class ContextualAdapter(logging.LoggerAdapter): def process(self, msg, kwargs): # 跳过Adapter和调用方法的栈帧,拿到实际业务代码的帧 caller_frame = inspect.currentframe().f_back.f_back kwargs["extra"] = { "caller_filename": caller_filename, "caller_lineno": caller_frame.f_lineno } return msg, kwargs return ContextualAdapter(self.base_logger, {})
在Client类中初始化上下文日志器:
# client.py class Client: def __init__(self, logger): self.logger = logger.get_contextual_logger(__file__) def run(self): self.logger.debug("Client执行了run方法")
注意事项
- 不要试图直接修改
logging模块默认的filename字段,它是模块内部基于日志记录器定义位置生成的,无法直接覆盖,只能通过自定义字段替代。 - 两种方法都不需要改变你现有的依赖注入设计,只需要扩展MyLogger的功能即可。
- 如果需要显示完整文件路径,去掉
os.path.basename()处理即可。
内容的提问来源于stack exchange,提问作者arpymastro
相关产品推荐
相关产品推荐

