繁体   English   中英

如何扩展 logging.Logger 类?

[英]How to extend the logging.Logger Class?

我想从一个继承自 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)我打算使用CustomFormatterCustomAdapter类。 该位不需要自定义日志记录类,可以立即插入。

(3)底层logger类(记录等)的定制我大概不需要logger.info loggin.debug ,拦截logger.infologgin.debug等应该就够了。

所以回顾一下在这些论坛上多次流传的这个python收据

我试图找到一个Logger Class之间的甜蜜点,但仍然能够使用内置函数,如分配FormattersAdapters等。所以一切都与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,演示了一个可能的解决方案。 但是,我很想改进这一点。

xlog_example

此示例有效地演示了通过继承覆盖logging.Logger类和logging.LogRecord类的技术。

两个外部项目混合到日志流中: funcnameusername不使用任何FormattersAdapters

在这个阶段,我相信我迄今为止所做的研究以及旨在总结解决方案的示例足以作为我问题的答案。 通常,有许多方法可用于包装日志记录解决方案。 这个特定问题旨在关注利用logging.Logger类继承的解决方案,以便可以更改内部机制,但其余功能保持原样,因为它将由原始logging.Logger类提供。

话虽如此,类继承技术应该非常小心地使用。 日志模块提供的许多工具已经足以维持和运行稳定的日志工作流。 当目标是对日志数据的处理和导出方式进行某种根本性更改时,从logging.Logger类继承可能很好。

总结一下,我看到有两种包装日志功能的方法:

1) 传统日志:

这只是使用提供的日志记录方法和函数,但将它们包装在一个模块中,以便将一些通用的重复性任务组织在一个地方。 通过这种方式,日志文件、日志级别、管理自定义FiltersAdapters等将变得容易。

我不确定在这种情况下是否可以使用class方法(我不是在谈论作为第二项主题的超类方法),因为当日志记录调用包含在一个班级。 我想听听这个问题,我肯定会准备一个探索这方面的问题。

2)Logger继承:

这种方法基于从原始logging.Logger类继承并添加到现有方法或通过修改内部行为完全劫持它们。 机制基于以下代码:

# Register our logger.
logging.setLoggerClass(OurLogger)
my_logger = logging.getLogger("main")

从这里开始,我们将依赖于我们自己的 Logger,但我们仍然能够从所有其他日志记录工具中受益:

# We still need a loggin handler.
ch = logging.StreamHandler()
my_logger.addHandler(ch)

# Confgure a formatter.
formatter = logging.Formatter('LOGGER:%(name)12s - %(levelname)7s - <%(filename)s:%(username)s:%(funcname)s> %(message)s')
ch.setFormatter(formatter)

# Example main message.
my_logger.setLevel(DEBUG)
my_logger.warn("Hi mom!")

此示例至关重要,因为它演示了在不使用自定义AdaptersFormatters情况下注入两个数据位usernamefuncname

有关此解决方案的更多信息,请参阅xlog.py 存储库 这是我根据其他问题和其他来源的一些代码准备的示例。

这条线

self.logger = logging.getLogger("myApp")

总是检索对同一个记录器的引用,因此每次实例化MyLogger时都会向它添加一个额外的处理程序。 以下将修复您当前的实例,因为您MyLogger使用不同的参数调用MyLogger

self.logger = logging.getLogger(name)

但请注意,如果多次传递相同的name参数,您仍然会遇到同样的问题。

你的类需要做的是跟踪它已经配置了哪些记录器。

class MyLogger(object):
    loggers = set()
    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(name)
        if name not in self.loggers:
            self.loggers.add(name)
            self.logger.setLevel(self.level)
            self.logger.addHandler(self.console_logger)

这根本不允许您重新配置记录器,但我将其留作练习以找出如何正确执行此操作。

但是,需要注意的关键是不能有两个单独配置的同名记录器。


当然, logging.getLogger总是返回对给定名称的同一对象的引用这一事实意味着您的类正在与logging模块不一致。 只需在程序启动时配置一次记录器,然后根据需要使用getLogger获取引用。

暂无
暂无

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

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