使用Python记录程序包,如何将其他格式插入具有自己的格式化器的多个Logger处理程序?
题
我有一个带有多个处理程序的记录仪,它们具有自己的格式化器。现在,我想添加一个缩进功能,并在运行时控制缩进级别。我希望所有处理人员的消息能够获得此缩进。我试图将其作为过滤器创建,但发现我似乎无法更改消息内容。然后,我尝试了一个格式化器,但是我只能每个处理程序。如何在不明确更改每个处理程序的格式的情况下添加这种凹痕?
我应该提到的是,我拥有的一个格式是为输出增添颜色的类。这不是一个简单的格式字符串。
另外,我正在使用配置文件。理想情况下,我希望大部分从那里开车。但是,我需要修改缩进格式的状态(例如设置缩进级别),但我不知道如何到达特定的格式化器,因为没有 logger.getFormatter("by_name")
方法。
为了澄清,我需要访问特定的格式化实例,从本质上讲,以随时调整格式。该实例是由文件中的loggging.config创建的。我找不到任何可以允许我给出特定格式的访问者的方法。
解决方案 3
这是另一个,骇人听闻但简单的。我对所有处理程序的消息总是以消息级别的字符串开始。只需在每个缩进的变化上修改那些darn弦:
# (make a LEVELS dict out of all the logging levels first)
def indent(self, step = 1):
"Change the current indent level by the step (use negative to decrease)"
self._indent_level += step
if self._indent_level < 0:
self._indent_level = 0
self._indent_str = self._indent_str_base * self._indent_level
for lvl in LEVELS:
level_name = self._indent_str + LEVELS[lvl]
logging.addLevelName(lvl, level_name)
(请参阅我的其他答案,以获取周围缩进功能的内容)
现在,缩进器可以是一个独立的类,而不会弄乱记录过程的细节。只要该消息包含级别字符串,即使有些东西要出现,缩进也将存在。总体上并不理想,但可能对我有用。
有人有更多的想法可用于任何味精格式吗?
其他提示
#!/usr/bin/env python
import logging
from random import randint
log = logging.getLogger("mylog")
log.setLevel(logging.DEBUG)
class MyFormatter(logging.Formatter):
def __init__(self, fmt):
logging.Formatter.__init__(self, fmt)
def format(self, record):
indent = " " * randint(0, 10) # To show that it works
msg = logging.Formatter.format(self, record)
return "\n".join([indent + x for x in msg.split("\n")])
# Log to file
filehandler = logging.FileHandler("indent.txt", "w")
filehandler.setLevel(logging.DEBUG)
filehandler.setFormatter(MyFormatter("%(levelname)-10s %(message)s"))
log.addHandler(filehandler)
# Log to stdout too
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.INFO)
streamhandler.setFormatter(MyFormatter("%(message)s"))
log.addHandler(streamhandler)
# Test it
log.debug("Can you show me the dog-kennels, please")
log.info("They could grip it by the husk")
log.warning("That's no ordinary rabbit!")
log.error("Nobody expects the spanish inquisition")
try:
crunchy_frog()
except:
log.exception("It's a real frog")
结果:
They could grip it by the husk That's no ordinary rabbit! Nobody expects the spanish inquisition It's a real frog Traceback (most recent call last): File "./logtest2.py", line 36, in crunchy_frog() NameError: name 'crunchy_frog' is not defined
我不确定我明白您的第二个问题。
好的,这是让我几乎需要的一种方式。子类别列表到覆盖getMessage,以将缩进和子类记录仪插入makerecord:
import logging
import logging.config
################################################################################
class IndentingLogger(logging.Logger):
"""A Logger subclass to add indent on top of any logger output
"""
############################################################################
def __init__(self, name = 'root', logging_level = logging.NOTSET):
"Constructor to keep indent persistent"
logging.Logger.__init__(self, name, logging_level)
self.indenter = IndentedRecord("", logging.NOTSET, "", 0, None, None, None, None, None)
############################################################################
def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
return self.indenter.set_record(name, level, fn, lno, msg, args, exc_info, func, extra)
################################################################################
class IndentedRecord(logging.LogRecord):
"""A LogRecord subclass to add indent on top of any logger output
"""
######## Class data #########
DEFAULT_INDENT_STR = ' '
############################################################################
def __init__(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
"Constructor"
logging.LogRecord.__init__(self, name, level, fn, lno, msg, args, exc_info, func)
self._indent_level = 0
self._indent_str_base = IndentedRecord.DEFAULT_INDENT_STR
self._indent_str = "" # cache it
############################################################################
def set_record(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
"Constructs the base record"
logging.LogRecord.__init__(self, name, level, fn, lno, msg, args, exc_info, func)
return self
################################################################################
def getMessage(self):
"Adds indent on top of the normal getMessage result"
# Call base class to get the formatted message
message = logging.LogRecord.getMessage(self)
# Now insert the indent
return self._indent_str + message
################################################################################
def indent(self, step = 1):
"Change the current indent level by the step (use negative to decrease)"
self._indent_level += step
if self._indent_level < 0:
self._indent_level = 0
self._indent_str = self._indent_str_base * self._indent_level
################################################################################
def set_indent_str(self, chars):
"Change the current indent string"
if not isinstance(chars, str):
raise ValueError("Argument must be a string. Got %s" % chars)
self._indent_str_base = chars
logging.config.fileConfig("reporter.conf")
logging.setLoggerClass(IndentingLogger)
logger = logging.getLogger('root') # will be wrong logger, if without argument
logger.debug("debug message")
logger.info("info message")
logger.indenter.indent(+1)
logger.warn("Indented? warn message")
logger.indenter.set_indent_str("***")
logger.error("Indented? error message: %s", "Oops, I did it again!")
logger.indenter.indent(+1)
logger.error("Indented? error message: %s", "Oops, I did it again!")
logger.indenter.indent(-1)
logger.critical("No indent; critical message")
结果是(实际上是彩色的):
Debug: debug message
Info: info message
Warning: Indented? warn message
Error: Indented? error message: Oops, I did it again!
Error: ******Indented? error message: Oops, I did it again!
Internal Error: ***No indent; critical message
不知何故,日志级别的字符串仍在前面偷偷摸摸,因此这不是我想要的。此外,这很尴尬 - 对于如此简单的功能来说太多了:(
更好的想法?