Python Logging with a common combination of logger classes and class inheritance

I would like to create a Python logging class that can be inherited as a regular way to register configuration, but separately control the level of logging of the base class from the parent. This is similar to How to use python logging in multiple modules . Vinay Sajip's answer for using LogMixin is very close. Below is my modified version.

Most of my classes inherit smaller classes. For instance:

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 a general logging configuration. However, I would like to independently control the log levels for both Base and Parent. With the configuration 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 understand this; I have only one Parent registrar. However, in general, I would 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 

(note the DEBUG associated with 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 

(Note that the name is Parent.Base so that I see inheritance.) Is this possible with one simple LogMixin class?

+6
source share
1 answer

metaclass would be more appropriate. When a class is defined, it will receive its own logger. Name transfer ensures that each class uses its own registrar.

 import logging import sys logging.basicConfig(stream=sys.stdout) class MetaBase(type): def __init__(cls, *args): super().__init__(*args) # Explicit name mangling logger_attribute_name = '_' + cls.__name__ + '__logger' # Logger name derived accounting for inheritance for the bonus marks logger_name = '.'.join([c.__name__ for c in cls.mro()[-2::-1]]) setattr(cls, logger_attribute_name, logging.getLogger(logger_name)) class Base(metaclass=MetaBase): def __init__(self): self.__logger.error('init base') def func_base(self): self.__logger.error('func base') class Parent(Base): def func_parent(self): self.__logger.error('func parent') p = Parent() p.func_base() p.func_parent() 

Results:

 ERROR:Base:init base ERROR:Base:func base ERROR:Base.Parent:func parent 

There are several additional benefits to this approach.

  • The class registrar is created when the class is defined and is accessible through a direct link to the attribute. Prevent property and getLogger call
  • Subclasses should only inherit the base, no need to remember to add MixIn

I simplified the example to demonstrate a key concept. Must work with files and with the configuration file.

+2
source

Source: https://habr.com/ru/post/1215422/


All Articles