簡體   English   中英

帶有通用記錄器類 mixin 和類繼承的 Python 日志記錄

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

我想創建一個 Python 日志類,該類可以作為日志配置的常用方法進行繼承,但可以從父類中單獨控制基類的日志記錄級別。 這類似於如何在多個模塊中使用 python 日志記錄 Vinay Sajip 使用 LogMixin 的答案非常接近。 下面是我稍微修改的版本。

我的大多數類都繼承了較小的類。 例如:

文件名: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)

文件名: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()         

文件名: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()

文件名: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
...

我得到了通用日志配置的好處。 但是,我希望對 Base 和 Parent 的日志級別進行獨立控制。 使用上面的配置文件,我得到:

$ 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

我明白為什么我會得到這個,我只有一個記錄器“父母”。 但是,總的來說,我寧願得到以下內容:

$ 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

(注意沒有與 Base.py 相關的調試)。
或者甚至更好:

$ 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

(注意名稱是 Parent.Base 所以我可以看到繼承。)這可以用一個簡單的 LogMixin 類實現嗎?

元類會更合適。 當一個類被定義時,它將獲得自己的記錄器。 名稱修改確保每個類都使用自己的記錄器。

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()

結果是:

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

這種方法比混入有一些額外的好處。

  • 每個類的記錄器在類定義時創建,並通過直接屬性引用訪問。 避免屬性和getLogger調用
  • 子類只需要繼承base,不用記得加MixIn

我已經簡化了示例以演示關鍵概念。 應該跨文件和配置文件工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM