Python Logging module: custom loggers
Question: I was trying to create a custom attribute for logging (caller's class name,
module name, etc.) and got stuck with a strange exception telling me that the
LogRecord instance created in the process did not have the necessary
attributes. After a bit of testing I ended up with this:

    
    
    import logging
    
    class MyLogger(logging.getLoggerClass()):
        value = None
    
    logging.setLoggerClass(MyLogger)
    
    loggers = [
        logging.getLogger(),
        logging.getLogger(""),
        logging.getLogger("Name")
    ]
    
    for logger in loggers:
        print(isinstance(logger, MyLogger), hasattr(logger, "value"))
    

This seemingly correct piece of code yields:

    
    
    False False
    False False
    True True
    

Bug or feature?


Answer: Looking at the source code we can see the following:

    
    
    root = RootLogger(WARNING)
    def getLogger(name=None):
        if name:
            return Logger.manager.getLogger(name)
        else:
            return root
    

That is, a root logger is created by default when the module is imported.
Hence, every time you look for the root looger (passing a false value such as
the empty string), you're going to get a `logging.RootLogger` object
regardless of any call to `logging.setLoggerClass`.

Regarding the logger class being used we can see:

    
    
    _loggerClass = None
    def setLoggerClass(klass):
        ...
        _loggerClass = klass
    

This means that a global variable holds the logger class to be used in the
future.

In addition to this, looking at `logging.Manager` (used by
`logging.getLogger`), we can see this:

    
    
    def getLogger(self, name):
        ...
                rv = (self.loggerClass or _loggerClass)(name)
    

That is, if `self.loggerClass` isn't set (which won't be unless you've
explicitly set it), the class from the global variable is used.

Hence, it's a feature. The root logger is always a `logging.RootLogger` object
and the other logger objects are created based on the configuration at that
time.

