ChatGPT解决这个技术问题 Extra ChatGPT

Python的日志格式可以根据消息日志级别进行修改吗?

我正在使用 Python 的 logging 机制将输出打印到屏幕上。我可以使用打印语句来做到这一点,但我想允许用户更精细地调整粒度以禁用某些类型的输出。我喜欢打印错误的格式,但当输出级别为“信息”时,我更喜欢更简单的格式。

例如:

  logger.error("Running cmd failed")
  logger.info("Running cmd passed")

在此示例中,我希望以不同的方式打印错误的格式:

# 错误 2009 年 8 月 27 日 - 错误:运行 cmd 失败 # info 运行 cmd 已通过

是否可以在没有多个日志记录对象的情况下为不同的日志级别提供不同的格式?我更愿意在创建记录器后不修改它,因为有大量的 if/else 语句来确定应该如何记录输出。


C
Community

我刚刚遇到了这个问题,无法填补上面示例中留下的“漏洞”。这是我使用的更完整的工作版本。希望这可以帮助某人:

# Custom formatter
class MyFormatter(logging.Formatter):

    err_fmt  = "ERROR: %(msg)s"
    dbg_fmt  = "DBG: %(module)s: %(lineno)d: %(msg)s"
    info_fmt = "%(msg)s"


    def __init__(self, fmt="%(levelno)s: %(msg)s"):
        logging.Formatter.__init__(self, fmt)


    def format(self, record):

        # Save the original format configured by the user
        # when the logger formatter was instantiated
        format_orig = self._fmt

        # Replace the original format with one customized by logging level
        if record.levelno == logging.DEBUG:
            self._fmt = MyFormatter.dbg_fmt

        elif record.levelno == logging.INFO:
            self._fmt = MyFormatter.info_fmt

        elif record.levelno == logging.ERROR:
            self._fmt = MyFormatter.err_fmt

        # Call the original formatter class to do the grunt work
        result = logging.Formatter.format(self, record)

        # Restore the original format configured by the user
        self._fmt = format_orig

        return result

编辑:

Halloleo 的赞美,这里有一个如何在脚本中使用上述内容的示例:

fmt = MyFormatter()
hdlr = logging.StreamHandler(sys.stdout)

hdlr.setFormatter(fmt)
logging.root.addHandler(hdlr)
logging.root.setLevel(DEBUG)

编辑2:

Python3 日志记录发生了一些变化。有关 Python3 方法,请参阅 here


在这里,我可能会添加您可以在程序中使用 MyFormatter 类的方式(将每个 <CR> 替换为回车符):fmt = MyFormatter()<CR> hdlr = logging.StreamHandler(sys.stdout)<CR> <CR> hdlr.setFormatter(fmt)<CR> logging.root.addHandler(hdlr)<CR> logging.root.setLevel(DEBUG)`<CR>
由于内部日志记录机制的变化,此答案在 3.2 之后将不起作用。 logging.Formatter.format 现在取决于 __init__style 参数。
Evpok 是对的。分配 self._fmt: self._style = logging.PercentStyle(self._fmt) 后添加
这可以通过使用 super() 而不是调用 logging.Formatter 来改进吗?
@phoenix:是的。良好的观察力。
V
Vinay Sajip

是的,您可以通过自定义 Formatter 类来做到这一点:

class MyFormatter(logging.Formatter):
    def format(self, record):
        #compute s according to record.levelno
        #for example, by setting self._fmt
        #according to the levelno, then calling
        #the superclass to do the actual formatting
        return s

然后将 MyFormatter 实例附加到您的处理程序。


出色 - 效果很好。我修改了 format() 方法以检查 levelno 并在需要时更改消息。否则,它会将其重置为我传入的原始字符串。谢谢!
请从此答案中删除复选标记。就在这下面的一个是完整的。此答案中缺少大量代码,其中仅包含描述您应该做什么的注释。
e
estani

再次像 JS 回答一样,但更紧凑。

class SpecialFormatter(logging.Formatter):
    FORMATS = {logging.DEBUG :"DBG: %(module)s: %(lineno)d: %(message)s",
               logging.ERROR : "ERROR: %(message)s",
               logging.INFO : "%(message)s",
               'DEFAULT' : "%(levelname)s: %(message)s"}

    def format(self, record):
        self._fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
        return logging.Formatter.format(self, record)

hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)

由于内部日志记录机制的变化,此答案在 3.2 之后将不起作用。 logging.Formatter.format 现在取决于 __init__style 参数。
S
Sergey Pleshakov

这样做的一种方法

定义一个类

import logging

class CustomFormatter(logging.Formatter):
    """Logging Formatter to add colors and count warning / errors"""

    grey = "\x1b[38;21m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"
    format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"

    FORMATS = {
        logging.DEBUG: grey + format + reset,
        logging.INFO: grey + format + reset,
        logging.WARNING: yellow + format + reset,
        logging.ERROR: red + format + reset,
        logging.CRITICAL: bold_red + format + reset
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)

实例化记录器

# create logger with 'spam_application'
logger = logging.getLogger("My_app")
logger.setLevel(logging.DEBUG)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

ch.setFormatter(CustomFormatter())

logger.addHandler(ch)

并使用!

logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")

https://i.stack.imgur.com/klCcc.png


这似乎是适用于 python 3.6 的唯一答案
我尝试在此处stackoverflow.com/a/56944256/9150146保持相同的答案是最新的,如果有帮助,请点赞。谢谢@user3821178
这是一个很好的答案。颜色编码记录也非常有用。
请注意,您可以在 init 中实例化格式化程序一次,而不是在每次调用 format
J
Joris

除了依赖样式或内部字段,您还可以创建一个 Formatter,根据 record.levelno(或其他标准)委托给其他格式化程序。以我的拙见,这是一个稍微干净的解决方案。下面的代码应该适用于 >= 2.7 的任何 python 版本:

简单的方法看起来像这样:

class MyFormatter(logging.Formatter):

    default_fmt = logging.Formatter('%(levelname)s in %(name)s: %(message)s')
    info_fmt = logging.Formatter('%(message)s')

    def format(self, record):
        if record.levelno == logging.INFO:
            return self.info_fmt.format(record)
        else:
            return self.default_fmt.format(record)

但你可以让它更通用:

class VarFormatter(logging.Formatter):

    default_formatter = logging.Formatter('%(levelname)s in %(name)s: %(message)s')

    def __init__(self, formats):
        """ formats is a dict { loglevel : logformat } """
        self.formatters = {}
        for loglevel in formats:
            self.formatters[loglevel] = logging.Formatter(formats[loglevel])

    def format(self, record):
        formatter = self.formatters.get(record.levelno, self.default_formatter)
        return formatter.format(record)

我在这里使用 dict 作为输入,但显然你也可以使用元组,**kwargs,任何漂浮你的船。这将被用作:

formatter = VarFormatter({logging.INFO: '[%(message)s]', 
                          logging.WARNING: 'warning: %(message)s'})
<... attach formatter to logger ...>

C
Community

这是对 estani's answer 的改编,以适应现在依赖于格式样式的 logging.Formatter 的新实现。我的依赖于 '{' 样式格式,但可以进行调整。可以改进为更通用,并允许选择格式样式和自定义消息作为 __init__ 的参数。

class SpecialFormatter(logging.Formatter):
    FORMATS = {logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
           logging.ERROR : logging._STYLES['{']("{module} ERROR: {message}"),
           logging.INFO : logging._STYLES['{']("{module}: {message}"),
           'DEFAULT' : logging._STYLES['{']("{module}: {message}")}

    def format(self, record):
        # Ugly. Should be better
        self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
        return logging.Formatter.format(self, record)

hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)

感谢您更新它以使用 Python3。我在 Python3 中遇到了同样的问题,并提出了类似的解决方案。请您也将这个答案发布在那里好吗? stackoverflow.com/questions/14844970/…
在您的评论中使用新的“{”样式? :-)
u
user1837990

上述解决方案适用于 3.3.3 版本。但是,对于 3.3.4,您会收到以下错误。

FORMATS = { logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),

TypeError:“元组”对象不可调用

在日志类 Lib\logging__init__.py 中进行了一些搜索后,我发现数据结构已从 3.3.3 更改为 3.3.4,这导致了问题

3.3.3

_STYLES = {
    '%': PercentStyle,
    '{': StrFormatStyle,
    '$': StringTemplateStyle
}

3.3.4

_STYLES = {
   '%': (PercentStyle, BASIC_FORMAT),
   '{': (StrFormatStyle, '{levelname}:{name}:{message} AA'),
    '$': (StringTemplateStyle, '${levelname}:${name}:${message} BB'),
}

因此,更新的解决方案是

class SpecialFormatter(logging.Formatter):
     FORMATS = {logging.DEBUG : logging._STYLES['{'][0]("{module} DEBUG: {lineno}: {message}"),
       logging.ERROR : logging._STYLES['{'][0]("{module} ERROR: {message}"),
       logging.INFO : logging._STYLES['{'][0]("{module}: {message}"),
       'DEFAULT' : logging._STYLES['{'][0]("{module}: {message}")}

 def format(self, record):
    # Ugly. Should be better
    self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
    return logging.Formatter.format(self, record)

直接使用 from logging import StrFormatStyle 导入样式类型可能比使用 logging._STYLES['{'][0] 更容易
J
JDiMatteo

如果您只是想跳过某些级别的格式,您可以做一些比其他答案更简单的事情,如下所示:

class FormatterNotFormattingInfo(logging.Formatter):
    def __init__(self, fmt = '%(levelname)s:%(message)s'):
        logging.Formatter.__init__(self, fmt)

    def format(self, record):
        if record.levelno == logging.INFO:
            return record.getMessage()
        return logging.Formatter.format(self, record)

通过不使用 self._fmt 或 self._style 等内部变量,这还具有在 3.2 版本之前和之后工作的优势。


我认为这是最干净的解决方案

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅