Python logging:无需修改现有日志,如何区分类实例日志?
解决方案:无需修改日志调用即可区分类实例日志
可以实现需求,以下提供两种适配不同场景的方案,均无需修改现有log.debug调用,也无需调整formatter配置:
方案一:调用栈回溯(无业务代码侵入)
通过自定义日志过滤器,在日志记录时自动回溯调用栈,定位到触发日志的类实例,提取其name属性并修改日志消息。这种方式完全不需要改动原有业务代码,适合同步场景。
import logging import inspect class InstanceNameFilter(logging.Filter): def filter(self, record): # 遍历调用栈,找到MyClass的function方法调用 for frame_info in inspect.stack(): frame = frame_info.frame if frame.f_code.co_name == "function": instance = frame.f_locals.get("self") if isinstance(instance, MyClass): # 自动拼接实例名称到日志消息 record.msg = f"{record.name} - {instance.name}: {record.msg}" break return True # 给模块日志器绑定过滤器 log = logging.getLogger("my_module") log.addFilter(InstanceNameFilter()) # 保留原有日志基础配置即可 logging.basicConfig(level=logging.DEBUG) # 原有业务代码完全不变 class MyClass: def __init__(self, name): self.name = name def function(self): log.debug("My log!") c1 = MyClass("first class") c2 = MyClass("second class") c1.function() c2.function()
方案二:ContextVars上下文存储(异步安全,适配aiomisc)
在aiomisc这类异步框架中,调用栈回溯可靠性不足,改用contextvars实现任务级别的上下文存储,配合装饰器自动为类方法注入实例标识,再通过过滤器修改日志消息。这种方式仅需在外部为类方法添加装饰器,无需修改原有业务代码和日志调用。
import logging import contextvars import aiomisc import asyncio from functools import wraps # 定义任务级上下文变量,存储实例名称 instance_name_var = contextvars.ContextVar("instance_name", default="") class InstanceNameFilter(logging.Filter): def filter(self, record): name = instance_name_var.get() if name: record.msg = f"{record.name} - {name}: {record.msg}" return True # 配置日志器 log = logging.getLogger("my_module") log.addFilter(InstanceNameFilter()) # 用aiomisc的日志配置替代原生配置,更好融合框架 from aiomisc.log import basic_config basic_config(level=logging.DEBUG) # 装饰器:自动将实例名称注入上下文 def inject_instance_name(func): if asyncio.iscoroutinefunction(func): @wraps(func) async def wrapper(self, *args, **kwargs): token = instance_name_var.set(self.name) try: return await func(self, *args, **kwargs) finally: instance_name_var.reset(token) return wrapper else: @wraps(func) def wrapper(self, *args, **kwargs): token = instance_name_var.set(self.name) try: return func(self, *args, **kwargs) finally: instance_name_var.reset(token) return wrapper # 原有业务代码完全不变 class MyClass: def __init__(self, name): self.name = name async def function(self): log.debug("My log!") # 为类方法绑定装饰器(无需修改类定义) MyClass.function = inject_instance_name(MyClass.function) async def main(): c1 = MyClass("first class") c2 = MyClass("second class") await asyncio.gather(c1.function(), c2.function()) if __name__ == "__main__": aiomisc.run(main())
关于aiomisc的适配
aiomisc本身没有直接提供此类实例日志标识的配置,但它基于asyncio,完全兼容contextvars的上下文方案。你可以将上述InstanceNameFilter直接集成到aiomisc的日志配置中,如示例中使用aiomisc.log.basic_config添加过滤器,实现与框架日志体系的无缝融合。
内容的提问来源于stack exchange,提问作者Top Topek
相关产品推荐
相关产品推荐

