使用 python 日志记录包,如何将附加格式插入到具有自己的格式化程序的多个记录器处理程序中?

发布于 2024-10-13 02:27:08 字数 462 浏览 7 评论 0原文

我有一个带有多个处理程序的记录器,它们有自己的格式化程序。现在我想添加一个缩进功能,并在运行时控制缩进级别。我希望来自所有处理程序的消息都能获得此缩进。我尝试将其创建为过滤器,但发现我似乎无法更改消息内容。然后我尝试将它作为格式化程序,但每个处理程序只能有一个。如何在不显式更改每个处理程序的格式化程序的情况下添加此类缩进?
我应该提到,我拥有的格式化程序之一是一个为输出添加颜色的类。它不是一个简单的格式字符串。


此外,我正在使用配置文件。理想情况下,我希望能够主要从那里驱动它。但是,我需要修改缩进格式化程序的状态(例如设置缩进级别),但我不知道如何访问该特定格式化程序,因为没有 logger.getFormatter("by_name")方法。
为了澄清这一点,我需要访问特定的格式化程序实例,本质上是动态调整格式。该实例已由文件中的logging.config 创建。我没有找到任何访问器方法可以让我获得特定格式化程序的名称。

I have a single logger with multiple handlers, which have their own formatters. Now I want to add an indenting feature, with the indent level controlled at runtime. I want messages from all the handlers to get this indent. I've tried to create it as a filter, but found that I seem to be unable to alter the message content. Then I've tried it as a formatter, but I can only have one per handler. How can I add such indentation without explicitly changing the formatters of every handler?
I should mention that one of the formatters I have is a class that adds color to the output. It is not a simple format string.


In addition, I am using a config file. Ideally, I'd like to be able to drive this mostly from there. However, I need to modify the state of the indent formatter (e.g. set indent level), but I don't know how to get to that particular formatter as there's no logger.getFormatter("by_name") method.
To clarify, I need to access the specific formatter instance, essentially to adjust the format on the fly. The instance has been created by logging.config from the file. I don't find any accessor methods that would allow me to get the particular formatter given its name.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酒与心事 2024-10-20 02:27:08
#!/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

我不确定我是否理解你的第二个问题。

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

result:

    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

I'm not sure I understand your second question.

还如梦归 2024-10-20 02:27:08

这是另一种,虽然很老套,但很简单。我的所有处理程序的消息始终以消息级别字符串开头。只需在每次缩进更改时修改这些该死的字符串即可:

# (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)

(有关缩进功能的内容,请参阅我的其他答案)
现在压头可以是一个独立的类,而不会干扰日志记录过程的细节。只要消息包含级别字符串,就会出现缩进,即使在它之前有一些内容。一般来说并不理想,但可能对我有用。
有人有更多适用于任何消息格式的想法吗?

Here's another, hacky, but simple one. My messages for all handlers always start with a message level string. Just modify those darn strings on every indent change:

# (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)

(see my other answer for the stuff that surrounds indent function)
Now the indenter can be an independent class without messing with the details of logging process. As long as the message includes the level string, the indent will be there, even if some stuff is coming before it. Not ideal in general, but may work for me.
Anybody has more ideas that work for any msg format?

不乱于心 2024-10-20 02:27:08

好吧,这是一种几乎可以满足我需要的方法。子类化 LogRecord 以覆盖 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

不知怎的,日志级别字符串仍然潜到前面,所以它不完全是我想要的。此外,这很尴尬 - 对于这样一个简单的功能来说太多了:(
更好的想法?

Ok, here's one way that gets me ALMOST what I need. Subclass the LogRecord to overwrite getMessage for inserting indent and subclass logger to makeRecord with it:

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

The result is (colored in reality):

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

Somehow the log level string still sneaks to the front, so it is not quite what I want. Besides, this is awkward - too much for such a simple feature :(
Better ideas?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文