如何扩展logging.Logger类? [英] How to extend the logging.Logger Class?

查看:76
本文介绍了如何扩展logging.Logger类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从一个继承自Python的 logging.Logger 类的基本日志记录类开始.但是,我不确定应该如何构造我的类,以便可以建立自定义继承的记录器所需的基础知识.

I would like to start with a basic logging class that inherits from Python's logging.Logger class. However, I am not sure about how I should be constructing my class so that I can establish the basics needed for customising the inherited logger.

这是我到目前为止在 logger.py 文件中拥有的内容:

This is what I have so far in my logger.py file:

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

当我运行主应用程序时,我得到了:

When I run the main app, I get this:

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!

所有内容都会打印两次,因为我可能无法正确设置logger类.

Everything is printed twice, as probably I have failed to set the logger class properly.

让我阐明我的目标.

(1)我想将主要的日志记录功能封装在一个位置,所以我可以这样做:

(1) I would like to encapsulate the main logging functionality in one single location so I can do this:

 from mylogger import MyLogger
 my_logger = MyLogger("myApp")
 my_logger.info("Hello from %s!" % ("__CMD"))

(2)我计划使用 CustomFormatter CustomAdapter 类.该位不需要自定义日志记录类,可以立即将其插入.

(2) I am planning to use CustomFormatter and CustomAdapter classes. This bit does not require a custom logging class, those can be plugged in straight away.

(3)我可能不需要深入了解基础记录器类(记录等),拦截 logger.info loggin.debug 等就足够了.

(3) I probably do not need to go very deep in terms of customisation of the underlying logger class (records etc.), intercepting logger.info, loggin.debug etc. should be enough.

因此回头参考此python收据,已在这些论坛上多次散布:

So referring back to this python receipt that has been circulated many times on these forums:

我试图找到具有 Logger类之间的最佳结合点,但仍然能够使用诸如分配 Formatters Adapters之类的内置功能等.因此,所有内容都与 logging 模块保持兼容.

I am trying to the find the sweet point between having a Logger Class, yet still be able to use the built in functions like assigning Formatters and Adapters etc. So everything stays compatible with the logging module.

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应用程序创建了一个仓库,展示了可能的解决方案.但是,我渴望对此进行改进.

Update 2

I have created a repo with a simple python app demonstrating a possible solution. However, I am keen to improve this.

xlog_example

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

This example effectively demonstrates the technique of overriding the logging.Logger class and the logging.LogRecord class through inheritance.

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

Two external items are mixed into the log stream: funcname and username without using any Formatters or Adapters.

推荐答案

在这个阶段,我相信我到目前为止所做的研究,并且提供了旨在总结解决方案的示例足以作为答案.我的问题.通常,可以使用许多方法来包装日志记录解决方案.这个特定问题的重点是利用 logging.Logger 类继承的解决方案,以便可以更改内部机制,但其余功能将保持原样,因为它将由以下人员提供.原始的 logging.Logger 类.

At this stage, I believe that the research I have made so far and the example provided with the intention to wrap up the solution is sufficient to serve as an answer to my question. In general, there are many approaches that may be utilised to wrap a logging solution. This particular question aimed to focus on a solution that utilises logging.Logger class inheritance so that the internal mechanics can be altered, yet the rest of the functionality kept as it is since it is going to be provided by the original logging.Logger class.

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

Having said that, class inheritance techniques should be used with great care. Many of the facilities provided by the logging module are already sufficient to maintain and run a stable logging workflow. Inheriting from the logging.Logger class is probably good when the goal is some kind of a fundamental change to the way the log data is processed and exported.

总结一下,我发现有两种包装日志记录功能的方法:

To summarise this, I see that there are two approaches for wrapping logging functionality:

1)传统日志记录:

这只是与提供的日志记录方法和函数一起使用,但是将它们包装在一个模块中,以便将一些通用的重复性任务组织在一个地方.这样,诸如日志文件,日志级别,管理自定义过滤器适配器等之类的事情将变得很容易.

This is simply working with the provided logging methods and functions, but wrap them in a module so that some of the generic repetitive tasks are organised in one place. In this way, things like log files, log levels, managing custom Filters, Adapters etc. will be easy.

我不确定在这种情况下是否可以使用 class 方法(而且我不是在谈论第二类主题的超类方法)当日志调用包装在一个类中时,事情变得越来越复杂.我希望听到有关此问题的信息,我一定会准备一个探讨此方面的问题.

I am not sure if a class approach can be utilised in this scenario (and I am not talking about a super class approach which is the topic of the second item) as it seems that things are getting complicated when the logging calls are wrapped inside a class. I would like to hear about this issue and I will definitely prepare a question that explores this aspect.

2)记录器继承:

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

This approach is based on inheriting from the original logging.Logger class and adding to the existing methods or entirely hijacking them by modifying the internal behaviour. The mechanics are based on the following bit of code:

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

从现在开始,我们依赖于自己的Logger,但是我们仍然能够从所有其他日志记录功能中受益:

From here on, we are relying on our own Logger, yet we are still able to benefit from all of the other logging facilities:

# 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!")

此示例至关重要,因为它演示了如何在不使用自定义 Adapters Formatters <的情况下注入两个数据位 username funcname /code>.

This example is crucial as it demonstrates the injection of two data bits username and funcname without using custom Adapters or Formatters.

请参阅 xlog.py回购,以获取有关此信息的更多信息解决方案.这是我基于来源.

Please see the xlog.py repo for more information regarding this solution. This is an example that I have prepared based on other questions and bits of code from other sources.

这篇关于如何扩展logging.Logger类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆