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?
Looking at the source code we can see the following:
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.RootLoggerobject regardless of any call tologging.setLoggerClass.Regarding the logger class being used we can see:
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 bylogging.getLogger), we can see this:That is, if
self.loggerClassisn’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.RootLoggerobject and the other logger objects are created based on the configuration at that time.