Search code examples
pythonpython-logging

Logging with adapter losing __name__ information


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?


Solution

  • 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)