简体   繁体   English

带有通用记录器类 mixin 和类继承的 Python 日志记录

[英]Python Logging with a common logger class mixin and class inheritance

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.我想创建一个 Python 日志类,该类可以作为日志配置的常用方法进行继承,但可以从父类中单独控制基类的日志记录级别。 This is similar to How to use python logging in multiple modules .这类似于如何在多个模块中使用 python 日志记录 The answer by Vinay Sajip to use a LogMixin is very close. Vinay Sajip 使用 LogMixin 的答案非常接近。 Below is my slightly modified version.下面是我稍微修改的版本。

Most of my classes inherit smaller classes.我的大多数类都继承了较小的类。 For example:例如:

filename: LogMixin.py文件名: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文件名: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文件名: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文件名: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.但是,我希望对 Base 和 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). (注意没有与 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? (注意名称是 Parent.Base 所以我可以看到继承。)这可以用一个简单的 LogMixin 类实现吗?

A metaclass would be more appropriate. 元类会更合适。 When a class is defined it will get its own logger.当一个类被定义时,它将获得自己的记录器。 Name mangling ensures each class uses its own logger.名称修改确保每个类都使用自己的记录器。

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 in:结果是:

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

This approach has a few additional benifites over mix in.这种方法比混入有一些额外的好处。

  • The logger for each class is created at class definition and accessed via a direct attribute reference.每个类的记录器在类定义时创建,并通过直接属性引用访问。 Avoids property and getLogger call避免属性和getLogger调用
  • Subclasses only need to inherit base, no need to remember to add MixIn子类只需要继承base,不用记得加MixIn

I've simplified the example to demonstrate the key concept.我已经简化了示例以演示关键概念。 Should work across files and with a config file.应该跨文件和配置文件工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM