I would like to create a Python logging class that can be inherited as a common means of logging configuration, yet seperately control the logging level of the base class from the parent. This is similar to How to use python logging in multiple modules. The answer by Vinay Sajip to use a LogMixin is very close. Below is my slightly modified version.
Most of my classes inherit smaller classes. For example:
filename: LogMixin.py
import logging, logging.config
import yaml
class LogMixin(object):
__loggerConfigured = False
@property
def logger(self):
if not self.__loggerConfigured:
with open('log_config.yaml', 'rt') as f:
config = yaml.load(f.read())
logging.config.dictConfig(config)
self.__loggerConfigured = True
name = '.'.join([self.__class__.__name__])
return logging.getLogger(name)
filename: Base.py
from LogMixin import LogMixin
class Base(LogMixin):
def __init__(self):
self.logger.debug("Debug Base")
def run_base(self):
self.logger.debug("Debug Running Base")
self.logger.info("Info Running Base")
if __name__ == '__main__':
my_base = Base()
my_base.run_base()
filename: Parent.py
from Base import Base
class Parent(Base):
def __init__(self):
self.logger.debug("Debug Parent")
def run_parent(self):
self.logger.debug("Debug Running Parent")
self.logger.info("Info Running Parent")
if __name__ == '__main__':
my_parent = Parent()
my_parent.run_base()
my_parent.run_parent()
filename: log_config.yaml
---
version: 1
disable_existing_loggers: False
# Configuring the default (root) logger is highly recommended
root:
level: WARNING
handlers: [console]
# Configuration for logger set with logging.getLogger(NAME)
loggers:
Base:
level: INFO
handlers: [console]
propagate: no
Parent:
level: DEBUG
handlers: [console]
propagate: no
formatters:
simple:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
handlers:
console:
class: logging.StreamHandler
formatter: simple
stream: ext://sys.stdout
...
I get the benefits of the common logging configuration. However, I'd like independent control of the log levels for both Base and Parent. With the config file above, I get:
$ python Base.py
2015-03-16 00:06:23,716 - Base - INFO - Info Running Base
$ python Parent.py
2015-03-16 00:06:19,682 - Parent - DEBUG - Debug Parent
2015-03-16 00:06:19,682 - Parent - DEBUG - Debug Running Base
2015-03-16 00:06:19,682 - Parent - INFO - Info Running Base
2015-03-16 00:06:19,682 - Parent - DEBUG - Debug Running Parent
2015-03-16 00:06:19,682 - Parent - INFO - Info Running Parent
I understand why I get this, I only have one logger "Parent". However, in general, I'd rather get the following:
$ python Base.py
2015-03-16 00:06:23,716 - Base - INFO - Info Running Base
$ python Parent.py
2015-03-16 00:06:19,682 - Parent - DEBUG - Debug Parent
2015-03-16 00:06:19,682 - Base - INFO - Info Running Base
2015-03-16 00:06:19,682 - Parent - DEBUG - Debug Running Parent
2015-03-16 00:06:19,682 - Parent - INFO - Info Running Parent
(notice no DEBUG related to Base.py).
Or even better:
$ python Base.py
2015-03-16 00:06:23,716 - Base - INFO - Info Running Base
$ python Parent.py
2015-03-16 00:06:19,682 - Parent - DEBUG - Debug Parent
2015-03-16 00:06:19,682 - Parent.Base - INFO - Info Running Base
2015-03-16 00:06:19,682 - Parent - DEBUG - Debug Running Parent
2015-03-16 00:06:19,682 - Parent - INFO - Info Running Parent
(Notice the name is Parent.Base so I can see the inheritance.) Is this possible with a single simple LogMixin class?
A metaclass would be more appropriate. When a class is defined it will get it's own logger. Name mangling ensures each class uses it's own logger.
Results in:
This approach has a few additional benifites over mix in.
I've simplified the example to demonstrate the key concept. Should work across files and with a config file.