我想从一个基本的日志类开始,它继承自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!
所有内容都打印了两次,可能是因为我未能正确设置logger类。
--更新(01):澄清我的目标--
(1)我想将主要日志功能封装在一个位置,这样我就可以做到:
from mylogger import MyLogger
my_logger = MyLogger("myApp")
my_logger.info("Hello from %s!" % ("__CMD"))
(2)我计划使用CustomFormatter
和CustomAdapter
类。此位不需要自定义日志类,可以直接插入。
(3)我可能不需要深入了解底层日志类(记录等)的定制,截取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
--更新(02):正在进行的工作可能的解决方案--
我用一个简单的python应用程序创建了一个repo,演示了一个可能的解决方案。请随时登峰造极,帮助我改进。
这个例子有效地演示了通过继承重写logging.Logger
类和logging.LogRecord
类的技术。
两个外部项混合到日志流中:funcname
和username
,而不使用任何Formatters
或Adapters
。
这条线
总是检索对同一个记录器的引用,因此每次实例化
MyLogger
时都要向其添加一个附加处理程序。下面将修复当前实例,因为您两次都用不同的参数调用MyLogger
。但是请注意,如果多次传递同一个
name
参数,您仍然会遇到同样的问题。您的类需要做的是跟踪它已经配置了哪些记录器。
这根本不允许您重新配置记录器,但我将其留作练习,以了解如何正确地进行配置。
但是,要注意的关键是,不能有两个单独配置的具有相同名称的记录器。
当然,
logging.getLogger
总是为给定名称返回对同一对象的引用,这意味着您的类与logging
模块的工作不一致。只需在程序启动时配置一次日志记录器,然后根据需要使用getLogger
获取引用。在这个阶段,我相信我迄今所做的研究和提供的旨在总结解决方案的例子足以作为我问题的答案。一般来说,有很多方法可以用来包装日志解决方案。这个特别的问题旨在关注一个使用
logging.Logger
类继承的解决方案,以便可以更改内部机制,而其余的功能保持原样,因为它将由原始logging.Logger
类提供。尽管如此,类继承技术应该非常小心地使用。日志模块提供的许多功能已经足以维护和运行稳定的日志工作流程。当目标是对日志数据的处理和导出方式进行某种根本性的改变时,从
logging.Logger
类继承可能是很好的。总结一下,我发现有两种包装日志功能的方法:
1)传统的日志记录:
这只是使用提供的日志记录方法和函数,但将它们包装在一个模块中,以便将一些通用的重复任务组织在一个地方。这样,诸如日志文件、日志级别、管理自定义的
Filters
、Adapters
等就很容易了。我不确定在这个场景中是否可以使用
class
方法(我不是在讨论第二个项目的主题是一个超级类方法),因为当日志调用包装在一个类中时,事情似乎变得越来越复杂。我想听听这个问题,我一定会准备一个问题,探讨这个方面。2)日志继承:
这种方法基于从原始的
logging.Logger
类继承并添加到现有方法中,或者通过修改内部行为来完全劫持它们。这些机制基于以下代码位:从现在起,我们依靠我们自己的记录器,但我们仍然能够从所有其他日志设施中受益:
这个例子很重要,因为它演示了在不使用自定义
Adapters
或Formatters
的情况下注入两个数据位username
和funcname
。有关此解决方案的详细信息,请参见xlog.py repo。这是我基于other questions和来自其他sources的代码位准备的示例。
相关问题 更多 >
编程相关推荐