将日志信息格式化为树形结构

5

我希望Python日志输出以与记录器树相对应的树形式呈现。请看下面的示例。

假设我们有以下代码:

import logging

logger_a = logging.getLogger("a")
logger_a_b = logging.getLogger("a.b")
logger_a_b_c = logging.getLogger("a.b.c")
# ...


logger_a.debug("One")

logger_a_b.warning("two")
logger_a_b.warning("three")

logger_a_b_c.critical("Four")

logger_a_b.warning("Five")

输出应该类似于:
<--"a"
   |   DEBUG: One
   |
   o<--"a.b"
   |   | WARNING: Two
   |   | WARNING: Three
   |   |
   |   o<--"a.b.c"
   |       | CRITICAL: Four   
   |       |
   |   | WARNING: Five

我可以手写格式化程序来处理每个日志,但这并不能解决像o<--"a.b"这样的插入问题,而且我更喜欢通过记录结构自动计算偏移量。

有一个名为logging tree的模块。它打印出日志布局。我想要的是以类似的方式打印日志信息。

你知道有什么库或简单方法可以实现吗?


我认为你需要编写自己的日志处理程序,可能要继承内置的 logging.StreamHandler - Tom Dalton
不难将标准输出转换为这棵树形结构:获取模块名称,按句点拆分,在消息前绘制树形结构。看起来你已经拥有所有信息,只需要一个视图变换即可。 - viraptor
可能那已经足够了。 - MajesticRa
1个回答

4

根据您的示例,我创建了一个自定义的格式化程序,它将处理该树。

import logging

# custom tree formatter
class TreeFormatter(logging.Formatter):
    formatPrefix = {} # map loggername, formatPrefix

    def format(self, record):
        s = ""
        # first time this name is encountered: create the prefix and print the name
        if not record.name in self.formatPrefix: 
            f = self.getFormatPrefix(record)
            s += "%s \"%s\"\n" % (f, record.name)

        # print the actual message
        s += "%s %s: %s" % (self.formatPrefix[record.name], record.levelname, record.msg) 
        return s


    # create the format prefix for the given package name 
    # (stored in self.formatPrefix[record.name])
    # and return the first line to print
    def getFormatPrefix(self, record):
        depth = record.name.count(".")
        self.formatPrefix[record.name] = "   |" * (depth+1)

        if depth == 0:
            return "<--"

        return "%so<--" % ( ("   |" * depth)[:-1])

您可以使用它来创建第一级记录器(这里是a)。其余代码保持不变。

以下是示例:

# use this to create the first level logger
def createTreeLogger(name, level=logging.DEBUG):
    logger = logging.getLogger(name)
    logger.setLevel(level)
    ch = logging.StreamHandler()
    ch.setLevel(level)
    ch.setFormatter(TreeFormatter())
    logger.addHandler(ch)
    return logger


if __name__ == '__main__':

    logger_a = createTreeLogger("a") # first level: use createLogger
    # then create your loggers as always
    logger_a_b = logging.getLogger("a.b") 
    logger_a_b_c = logging.getLogger("a.b.c")


    logger_a.debug("One")

    logger_a_b.warning("two")
    logger_a_b.warning("three")

    logger_a_b_c.critical("Four")

    logger_a_b.warning("Five")
    logger_a.warning("Six")

值得注意的是,日志记录包的内部会自动为子包(a.b,a.b.c)使用相同的处理程序。因此,通过运行此代码,您将获得:

<-- "a"
   | DEBUG: One
   o<-- "a.b"
   |   | WARNING: two
   |   | WARNING: three
   |   o<-- "a.b.c"
   |   |   | CRITICAL: Four
   |   | WARNING: Five
   | WARNING: Six

一个缺点是,如果您有多个包层次结构,日志会变得混乱。但是TreeFormatter类很容易调整以满足您的需求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接