【发布时间】:2016-09-14 14:02:58
【问题描述】:
我想从一个继承自 Python 的 logging.Logger 类的基本日志记录类开始。但是,我不确定应该如何构建我的类,以便建立自定义继承记录器所需的基础知识。
这是我目前在 logger.py 文件中的内容:
import sys
import logging
from logging import DEBUG, INFO, ERROR
class MyLogger(object):
def __init__(self, name, format="%(asctime)s | %(levelname)s | %(message)s", level=INFO):
# Initial construct.
self.format = format
self.level = level
self.name = name
# Logger configuration.
self.console_formatter = logging.Formatter(self.format)
self.console_logger = logging.StreamHandler(sys.stdout)
self.console_logger.setFormatter(self.console_formatter)
# Complete logging config.
self.logger = logging.getLogger("myApp")
self.logger.setLevel(self.level)
self.logger.addHandler(self.console_logger)
def info(self, msg, extra=None):
self.logger.info(msg, extra=extra)
def error(self, msg, extra=None):
self.logger.error(msg, extra=extra)
def debug(self, msg, extra=None):
self.logger.debug(msg, extra=extra)
def warn(self, msg, extra=None):
self.logger.warn(msg, extra=extra)
这是主myApp.py:
import entity
from core import MyLogger
my_logger = MyLogger("myApp")
def cmd():
my_logger.info("Hello from %s!" % ("__CMD"))
entity.third_party()
entity.another_function()
cmd()
这是entity.py 模块:
# Local modules
from core import MyLogger
# Global modules
import logging
from logging import DEBUG, INFO, ERROR, CRITICAL
my_logger = MyLogger("myApp.entity", level=DEBUG)
def third_party():
my_logger.info("Initial message from: %s!" % ("__THIRD_PARTY"))
def another_function():
my_logger.warn("Message from: %s" % ("__ANOTHER_FUNCTION"))
当我运行主应用程序时,我得到了这个:
2016-09-14 12:40:50,445 | INFO | Initial message from: __THIRD_PARTY!
2016-09-14 12:40:50,445 | INFO | Initial message from: __THIRD_PARTY!
2016-09-14 12:40:50,445 | WARNING | Message from: __ANOTHER_FUNCTION
2016-09-14 12:40:50,445 | WARNING | Message from: __ANOTHER_FUNCTION
2016-09-14 12:40:50,445 | INFO | Hello from __CMD!
2016-09-14 12:40:50,445 | INFO | Hello from __CMD!
所有内容都打印了两次,可能是我未能正确设置记录器类。
更新 1
让我澄清一下我的目标。
(1) 我想将主要的日志记录功能封装在一个位置,这样我就可以做到这一点:
from mylogger import MyLogger
my_logger = MyLogger("myApp")
my_logger.info("Hello from %s!" % ("__CMD"))
(2) 我打算使用CustomFormatter 和CustomAdapter 类。此位不需要自定义日志记录类,可以立即插入。
(3) 在底层logger类(记录等)的定制方面我大概不需要太深入,拦截logger.info、loggin.debug等就足够了.
所以回顾一下在这些论坛上多次流传的this python receipt:
我试图找到拥有Logger Class 之间的最佳点,但仍然能够使用内置函数,例如分配Formatters 和Adapters 等。所以一切都与logging 模块兼容.
class OurLogger(logging.getLoggerClass()):
def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
# Don't pass all makeRecord args to OurLogRecord bc it doesn't expect "extra"
rv = OurLogRecord(name, level, fn, lno, msg, args, exc_info, func)
# Handle the new extra parameter.
# This if block was copied from Logger.makeRecord
if extra:
for key in extra:
if (key in ["message", "asctime"]) or (key in rv.__dict__):
raise KeyError("Attempt to overwrite %r in LogRecord" % key)
rv.__dict__[key] = extra[key]
return rv
更新 2
我用一个简单的 python 应用程序创建了一个 repo,演示了一个可能的解决方案。不过,我很想改进这一点。
此示例有效地演示了通过继承覆盖logging.Logger 类和logging.LogRecord 类的技术。
两个外部项混合到日志流中:funcname 和 username,没有使用任何 Formatters 或 Adapters。
【问题讨论】:
-
@JonasWielicki 我想封装基于现有设施的日志记录机制,我想使用自定义
Formatters、Handlers、Adapters等。这个想法是(作为练习)以覆盖所需的日志记录位。话虽如此,我知道这一直在社区中引起一定的争议。我仍然相信有一种方法可以使用自定义类等。我知道还有其他官方方法可以做到这一点。 -
我创建了一个带有工作基本版本的仓库。请阅读
UPDATE (02)了解更多信息。 -
归根结底,
logging模块在定制方面提供了许多选项。但是,如果我们想为logging.debug()或logging.info()等函数添加更复杂的自定义逻辑,该怎么办?比方说,在现有的 level 逻辑之上。例如do not display any messages if the day is Monday。如果不劫持loggin.info()方法并将我们的逻辑注入其中,这怎么可能? -
@JonasWielicki 请不要说日志模块的创建者本人鼓励
Logger继承:“...... findCaller 应该在 Logger 子类中被覆盖以用于这些用例 - 不像 gonvaled ,我觉得这不是一个常见的用例。OOP 的一点是您可以通过子类化和覆盖来扩展现有功能以适应特殊情况。请注意,可能有其他方式(例如使用过滤器)来提供额外的处理,而不是使用包装器。” -
以上引用来自thisVinay Sajip 的评论