When I use logger in the standard way I am able to capture the module name and it appears correctly in my logs. But if I implement an adapter (again, in the standard way) it seems like the module name keeps getting overwritten at initialization and my logs only ever show the same, last module initialized.
To be specific, in a logger.py
module that sets up my logging, I also have:
def get_logger(module_name):
return logging.getLogger(APP_LOGGER_NAME).getChild(module_name)
and in each module I do:
import logger
LOGGER = logger.get_logger(__name__)
That works fine - I get the correct module name wherever a log message is generated.
But if I use an adapter in my logger.py
module like so:
class AppLogger(logging.LoggerAdapter):
""" Adapter to add log entry header """
_instance = None
def __init__(self, logger, hdr):
super(AppLogger, self).__init__(logger, extra={'hdr': hdr})
self.hdr = hdr
def __new__(cls, logger, hdr):
""" Use singleton pattern so we don't lose the saved hdr from module to module """
if cls._instance is None:
cls._instance = super(AppLogger, cls).__new__(cls)
cls._instance.hdr = hdr
return cls._instance
def set_header(self, hdr):
self.hdr = hdr
def process(self, msg, kwargs):
if 'extra' not in kwargs:
return msg, {'extra': {'hdr': self.hdr}}
else:
return msg, kwargs
and change my get_logger
function like so:
def get_logger(module_name):
return AppLogger(logging.getLogger(APP_LOGGER_NAME).getChild(module_name),hdr='')
I no longer get different module names in my log entries. They're all the same, apparently the last module initialized (but I'm not sure on that).
I've verified with print
statements that the get_logger
module is getting the various module names correctly, by doing the following:
def get_logger(module_name):
#return logging.getLogger(APP_LOGGER_NAME).getChild(module_name)
print(f'{module_name=}')
foo= logging.getLogger(APP_LOGGER_NAME).getChild(module_name)
print(f'{foo.name=}')
bar= AppLogger(foo,hdr='')
print(f'{bar.name=}')
return bar
I can even see that the correct name is available in bar as each module is loaded.
Any ideas, anyone?
I figured this out. Because I was combining my need to keep a header string across modules with the adapter class, I wound up making the adapter a singleton. That's why my module name kept getting replaced. I changed the code to separate the header singleton as its own class and made the adapter a regular class. Now the correct module name appears in the logs and my header string is persistent until reset. It looks like this:
"""logger.py"""
class AppLogger(logging.LoggerAdapter):
""" Adapter to add log entry header """
def __init__(self, logger, extra=None):
super().__init__(logger, extra)
self.hdr = Log_Header()
def process(self, msg, kwargs):
if 'extra' not in kwargs:
return msg, {'extra': {'hdr': self.hdr.hdr}}
else:
return msg, kwargs
class Log_Header:
""" Singleton to hold the header value passed to AppLogger adapter so we maintain across modules """
_instance = None
def __init__(self):
super(Log_Header, self).__init__()
self.hdr = ''
def __new__(cls):
if cls._instance is None:
cls._instance = super(Log_Header, cls).__new__(cls)
return cls._instance
def set_header(self, hdr):
self.hdr = hdr
def get_logger(module_name):
return AppLogger(logging.getLogger(APP_LOGGER_NAME).getChild(module_name))
And in use:
import logger
app_logger = logger.get_logger(__name__)
...
log_header = 'something'
_ = logger.Log_Header().set_header(log_header)