是否可以根据消息日志级别修改Python的日志记录格式?

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

例如:

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

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

# error
Aug 27, 2009 - ERROR: Running cmd failed
# info
Running cmd passed

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

bedwyr asked 2019-10-09T18:35:30Z
7个解决方案
57 votes

我只是遇到了这个问题,在填写上面示例中遗留的“漏洞”时遇到了麻烦。 这是我使用的更完整的工作版本。 希望这可以帮助某人:

# 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方法,请参见此处。

JS. answered 2019-10-09T18:36:29Z
30 votes

是的,您可以通过自定义MyFormatter类来实现:

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实例附加到您的处理程序。

Vinay Sajip answered 2019-10-09T18:35:48Z
16 votes

再像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)
estani answered 2019-10-09T18:36:53Z
8 votes

这是estani对现在依赖格式设置样式的logging.Formatter新实现的回答的改编。 Mine依赖'{'样式格式,但可以进行调整。 可以改进为更通用,并允许选择格式设置样式和自定义消息作为__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)
Evpok answered 2019-10-09T18:37:18Z
8 votes

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

简单的方法如下所示:

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 ...>
Joris answered 2019-10-09T18:38:03Z
5 votes

以上解决方案适用于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)
user1837990 answered 2019-10-09T18:39:00Z
3 votes

如果您只是想跳过某些级别的格式,则可以执行比其他答案更简单的操作,如下所示:

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版本之前和之后工作的优点。

JDiMatteo answered 2019-10-09T18:39:31Z
translate from https://stackoverflow.com:/questions/1343227/can-pythons-logging-format-be-modified-depending-on-the-message-log-level