Python:Logging TypeError:并非在字符串格式化过程中转换所有参数

day*_*mer 32 python logging string-formatting

这就是我在做的事情

>>> import logging
>>> logging.getLogger().setLevel(logging.INFO)
>>> from datetime import date
>>> date = date.today()
>>> logging.info('date={}', date)
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 846, in emit
    msg = self.format(record)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 723, in format
    return fmt.format(record)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 464, in format
    record.message = record.getMessage()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 328, in getMessage
    msg = msg % self.args
TypeError: not all arguments converted during string formatting
Logged from file <stdin>, line 1
>>> 
Run Code Online (Sandbox Code Playgroud)

我的python版本是

$ python --version
Python 2.7.3
Run Code Online (Sandbox Code Playgroud)

我如何使其工作?

Mar*_*ers 47

使用日志记录模块时,不能使用新式格式; 用%s而不是{}.

logging.info('date=%s', date)
Run Code Online (Sandbox Code Playgroud)

日志记录模块使用旧式%运算符来格式化日志字符串.有关详细信息,请参阅debug方法.

如果您真的想使用str.format()字符串格式,请考虑使用在实际转换为字符串时应用格式"late"的自定义对象:

class BraceMessage(object):
    def __init__(self, fmt, *args, **kwargs):
        self.fmt = fmt
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return self.fmt.format(*self.args, **self.kwargs)

__ = BraceMessage

logging.info(__('date={}', date))
Run Code Online (Sandbox Code Playgroud)

这是Python 3 logging模块文档提出的一种方法,它也适用于Python 2.


Mat*_*att 22

你可以自己做格式化:

logging.info('date={}'.format(date))
Run Code Online (Sandbox Code Playgroud)

正如Martijn Pieters所指出的,这将始终运行字符串格式,而使用日志记录模块将导致仅在实际记录消息时才执行格式化.

  • 要知道,通过自己进行格式化,您将失去速度优势,让日志记录模块为您执行此操作*仅在实际记录消息时*.换句话说,如果使用`debug()`处理程序但日志级别不包括DEBUG级别,则不会产生字符串格式化操作的惩罚.如果您有大量调试消息,速度差异可能很大. (19认同)

Jef*_*ows 6

Martijn的答案是正确的,但是如果你更喜欢在日志记录中使用新的格式化格式,可以通过子类化Logger来完成.

import logging

class LogRecord(logging.LogRecord):
    def getMessage(self):
        msg = self.msg
        if self.args:
            if isinstance(self.args, dict):
                msg = msg.format(**self.args)
            else:
                msg = msg.format(*self.args)
        return msg

class Logger(logging.Logger):
    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)
        if extra is not None:
            for key in extra:
                rv.__dict__[key] = extra[key]
        return rv
Run Code Online (Sandbox Code Playgroud)

然后只需设置日志记录类:

logging.setLoggerClass(Logger)
Run Code Online (Sandbox Code Playgroud)

  • 或者甚至(至少在 Python 3.5 上)只使用 LogRecord 和 `logging.setLogRecordFactory(LogRecord)` (3认同)

yus*_*suf 6

你也可以这样做(Python 3);

logging.info(f'date={date}')
Run Code Online (Sandbox Code Playgroud)

  • 不过它仅适用于 Python 3.6 及以上版本。它也急切地评估,而日志记录模块则懒惰地评估消息 (3认同)