对于以下代码:

logger.debug('message: {}'.format('test'))

Pylint产生以下警告:

logging-format-interpolation (W1202): 在日志函数中使用%格式,并将%参数传递为 当日志语句的调用形式为 “日志记录。(format_string.format (format_args…)”。这样的 调用应该使用%格式,而将插值留给 日志函数,通过将参数作为参数传递。

我知道我可以关闭这个警告,但我想了解它。我假设使用format()是Python 3中输出语句的首选方式。为什么记录器语句不是这样?


记录器语句不是这样的,因为它依赖于以前的“%”格式,例如字符串,使用提供给记录器调用的额外参数来提供该字符串的惰性插值。例如,而不是做:

logger.error('oops caused by %s' % exc)

你应该这样做

logger.error('oops caused by %s', exc)

因此,只有在消息实际发出时,字符串才会被插入。

在使用.format()时,您无法受益于此功能。


根据日志文档的优化部分:

消息参数的格式化将被推迟,直到无法避免为止。但是,计算传递给日志记录方法的参数的开销也很大,如果日志记录器将丢弃您的事件,您可能希望避免这样做。

也许这个时差能帮到你。

下面的描述并不能回答你的问题,但它可以帮助人们。

如果你想使用fstrings(字面字符串插值)来记录日志,那么你可以在.pylintrc文件中使用disable=logging-fstring-插值来禁用它,请参阅:相关问题和注释。

此外,您还可以禁用logging-format-interpolation。


周期诡异?

pylintrc文件中有3个日志样式选项:old, new, fstr

FSTR选项在2.4中添加,在2.5中删除

来自.pylintrc文件(v2.4)的描述:

[LOGGING]

# Format style used to check logging format string. `old` means using %
# formatting, `new` is for `{}` formatting,and `fstr` is for f-strings.
logging-format-style=old

对于old (logging-format-style=old):

foo = "bar"
self.logger.info("foo: %s", foo)

对于new (logging-format-style=new):

foo = "bar"
self.logger.info("foo: {}", foo)
# OR
self.logger.info("foo: {foo}", foo=foo)

注意:即使选择了new选项,也不能使用.format()。

Pylint仍然对这段代码给出相同的警告:

self.logger.info("foo: {}".format(foo))  # W1202
# OR
self.logger.info("foo: {foo}".format(foo=foo))  # W1202

对于FSTR (logging-format-style= FSTR):

foo = "bar"
self.logger.info(f"foo: {foo}")

就个人而言,我更喜欢fstr选项,因为PEP-0498。

根据我的经验,相比优化(对于大多数用例),延迟插值的一个更有说服力的理由是,它可以很好地与Sentry等日志聚合器一起使用。

考虑一条“用户登录”日志消息。如果您将用户插入到格式字符串中,那么您将拥有与用户数量相同的不同日志消息。如果您使用像这样的惰性插值,日志聚合器可以更合理地将其解释为具有一堆不同实例的相同日志消息。

可能是几年之后,但有一天我不得不处理这个问题,我简单地说;只是在记录器之前格式化了字符串。

message = 'message: {}'.format('test')
logger.debug(message)

这样就不需要从日志中更改任何设置,如果以后想要更改为正常打印,也不需要更改格式或代码。

“logging-format-interpolation (W1202)”是pylint的另一个错误建议(就像pep8中的许多建议一样)。 f字符串被描述为慢vs %,但你检查过吗? 使用f-string对% ->进行500_000旋转记录f-string:23.01秒,%:25.43秒。

所以使用f-string记录日志比%快。 当你看日志源代码:log.error() -> self.logger._log() -> self.makeRecord() -> self._logRecordFactory() ->类LogRecord() -> home made等效于format()

代码:

import logging
import random
import time

loops = 500_000
r_fstr = 0.0
r_format = 0.0

def test_fstr():
    global loops, r_fstr
    for i in range(0, loops):
        r1 = time.time()
        logging.error(f'test {random.randint(0, 1000)}')
        r2 = time.time()
        r_fstr += r2 - r1

def test_format():
    global loops, r_format
    for i in range(0 ,loops):
        r1 = time.time()
        logging.error('test %d', random.randint(0, 1000))
        r2 = time.time()
        r_format += r2 - r1

test_fstr()
test_format()
print(f'Logging f-string:{round(r_fstr,2)} sec. , %:{round(r_format,2)} sec.')

下面是一个例子,说明为什么在日志记录中使用%s而不是f-strings更好。

>>> import logging
>>> logging.basicConfig(level=logging.INFO)
>>> logger = logging.getLogger('MyLogger')
>>>
>>> class MyClass:
...     def __init__(self, name: str) -> None:
...         self._name = name
...     def __str__(self) -> str:
...         print('GENERATING STRING')
...         return self._name
...
>>> c = MyClass('foo')
>>> logger.debug('Created: %s', c)
>>> logger.debug(f'Created: {c}')
GENERATING STRING

受Python 3.7日志的启发:f-strings vs %。