I have some logger for multiple programs each with multiple handlers configured in a "__main__" logger and another package that creates a "__main__.package" logger so it uses that configuration for its messages. I dont have control of the loggers for the programs, only my package. My package should use that loggers settings. I can assume one is configured for every program using my package.
I want to inherit the formatter from main but modify the message/format before it gets sent to that formatter. Something very similar to this question except I am working with a child logger not the parent logger directly.
example:
main.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()
My attempt to add this info was to modify the message by adding a filter that modified the message before it was output:
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}")
output:
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]
my desired output is
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]
I think that this is being duplicated because I keep adding filters to the same logging object. I see in the logging cookbook there is an example that creates context vars to handle two instances of some class, but when I look up documentation on what a contextvar is it appears to be more related to threading. I'm not sure if it is a "good idea" to be creating them to solve this issue.
Another option I guess I can do is wrap every single input to the log message like logging.info(self.format_message_instance(msg)) but I figure there is a known best practice
__main__logger, but just documenting the logger(s) it does use for the importing application to configure (further) if it wants to.__main__that your example creates is not the same as the root logger, which all loggers inherit from. Don't bake dependencies on any particular user into your package design.