我有一些用于多个程序的记录器,每个程序在
"__main__"
记录器中配置了多个处理程序,另一个包创建了 "__main__.package"
记录器,因此它使用该配置来处理其消息。我无法控制程序的记录器,只能控制我的包。我的包应该使用该记录器设置。我可以假设使用我的包为每个程序配置了一个。
我想从 main 继承格式化程序,但在消息/格式发送到该格式化程序之前修改消息/格式。与 this 问题非常相似,除了我正在使用子记录器而不是直接使用父记录器。
示例:
主.py
import logging
import mypackage
logger = logging.getLogger("__main__")
logger.setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_formatter = logging.Formatter('%(levelname)s:%(filename)s:%(lineno)d:%(message)s')
stream_handler.setFormatter(stream_formatter)
logger.addHandler(stream_handler)
def main():
a=1
logger.info("message from main")
b = mypackage.MyClass("EXTRA INSTANCE INFO")
b.mymodule(a)
c = mypackage.MyClass("EXTRA INSTANCE INFO2")
c.mymodule(a+1)
main()
我添加此信息的尝试是通过添加在输出之前修改消息的过滤器来修改消息:
mypackage.py
import logging
logger = logging.getLogger(f"__main__.{__name__}")
class _PackageContextFilter(logging.Filter):
def __init__(self, needed_info_from_my_package):
self.needed_info_from_my_package=needed_info_from_my_package
def filter(self, record):
record.msg = f"({self.needed_info_from_my_package}) " + record.msg
return True
class MyClass():
def __init__(self,init_cond) -> None:
self.data=[]
self.needed_info_from_my_package = [init_cond]
# I think the issue arises here because a single logger object gets multiple filters
logger.addFilter(_PackageContextFilter(self.needed_info_from_my_package))
def mymodule(self,data):
self.data.append(data)
logger.info(f"message from mymodule about {self.data}")
输出:
INFO:main.py:13:message from main
INFO:mypackage.py:24:(['EXTRA INSTANCE INFO']) message from mymodule about [1]
INFO:mypackage.py:24:(['EXTRA INSTANCE INFO2']) (['EXTRA INSTANCE INFO']) message from mymodule about [2]
我想要的输出是
INFO:main.py:13:message from main
INFO:mypackage.py:24:(['EXTRA INSTANCE INFO']) message from mymodule about [1]
INFO:mypackage.py:24:(['EXTRA INSTANCE INFO2']) message from mymodule about [2]
我认为这是重复的,因为我不断向同一个日志记录对象添加过滤器。我在logging Cookbook中看到一个示例,它创建上下文变量来处理某个类的两个实例,但是当我查找有关上下文变量是什么的文档时,它似乎与线程更相关。我不确定创建它们来解决这个问题是否是一个“好主意”。
我想我可以做的另一个选择是将每个输入包装到日志消息中,例如
logging.info(self.format_message_instance(msg))
,但我认为有一个已知的最佳实践
通常过滤器不会引用调用它的
MyClass
实例,因此它不知道要添加到消息中的信息。如何使用每个实例唯一的适配器(类似于 cookbook 示例):
class CustomAdapter(logging.LoggerAdapter):
def process(self, msg, kwargs):
return f"({self.extra['info']}) {msg}", kwargs
class MyClass():
def __init__(self,init_cond) -> None:
self.data=[]
self.needed_info_from_my_package = [init_cond]
# here we initialize based on the main logger
self.logger = CustomAdapter(logger, {'info': self.needed_info_from_my_package})
def mymodule(self,data):
self.data.append(data)
self.logger.info(f"message from mymodule about {self.data}")