具有通用记录器类mixin和类继承的Python日志记录 [英] Python Logging with a common logger class mixin and class inheritance
问题描述
我想创建一个Python日志记录类,该类可以作为一种常用的日志记录配置方式继承,但又要从父类单独控制基类的日志记录级别.这类似于如何在多个模块中使用python登录 . Vinay Sajip的 answer 非常接近使用LogMixin.下面是我稍加修改的版本.
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:
文件名:LogMixin.py
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)
文件名:Base.py
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()
文件名:Parent.py
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()
文件名:log_config.yaml
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
...
我从通用日志记录配置中受益.但是,我希望对Base和Parent的日志级别进行独立控制.使用上面的配置文件,我得到:
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
(注意,没有与Base.py相关的DEBUG).
甚至更好:
(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
(请注意,名称为Parent.Base,所以我可以看到继承.) 一个简单的LogMixin类有可能吗?
(Notice the name is Parent.Base so I can see the inheritance.) Is this possible with a single simple LogMixin class?
推荐答案
A 元类会更合适.定义类后,它将获得自己的记录器.名称修饰可确保每个类使用其自己的记录器.
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()
结果:
ERROR:Base:init base
ERROR:Base:func base
ERROR:Base.Parent:func parent
这种方法在混入过程中还有其他好处.
This approach has a few additional benifites over mix in.
- 每个类的记录器都是在类定义时创建的,并可以通过直接属性引用进行访问.避免属性和
getLogger
调用 - 子类只需要继承基类,无需记住添加MixIn
我已经简化了示例以演示关键概念.应该可以跨文件使用,也可以与配置文件一起使用.
I've simplified the example to demonstrate the key concept. Should work across files and with a config file.
这篇关于具有通用记录器类mixin和类继承的Python日志记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!