如何使用style='{'

2024-10-03 06:31:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用Python 3.8.10。我正在尝试使用格式化程序下documentation的日志记录样式“{”

它声明如下

If the style is ‘%’, the message format string uses %()s styled string substitution; the possible keys are documented in LogRecord attributes. If the style is ‘{‘, the message format string is assumed to be compatible with str.format() (using keyword arguments), while if the style is ‘$’ then the message format string should conform to what is expected by string.Template.substitute().

我在让style='{'正常工作时遇到问题。特别是在每个文档中传递它所需的关键字参数

import logging

logger = logging.getLogger(__name__) 

formatter = logging.Formatter('{asctime} - {name} - {levelname} - {message}',style='{')

scrnhandler = logging.StreamHandler()
scrnhandler.setLevel(logging.INFO)
scrnhandler.setFormatter(formatter)

logger.addHandler(scrnhandler)
logger.level = logging.INFO

logger.info("plain message as base case")    #<-- Works, no args passed

mystr = "Input args: {0:f}".format(10)       #<-- This is supposed to be str.format this is to be followed

logger.info("Input args: {0:f}", 10)         #<-- Does not work, I assume it wants a 'named argument'
logger.info("Input args: {args[0]:f}", 10)   #<-- Does not work, 
                                         # I know the data goes into the LogRecord args attribute so tried that
                                         
logger.info("Input args: {a:f}", a=10)       #<-- Does not work, is looking for more args to parse

我还没有找到一个例子来说明如何使用这种风格的日志记录,有人能帮我吗 找出我在使用这种日志风格时遗漏的细微差别


Tags: thetoinfoformatmessageinputstringis
1条回答
网友
1楼 · 发布于 2024-10-03 06:31:59

我刚才去过那里,也很困惑。至少还有几个开发人员也有同样的感受,因为有bpo14031

摘要

摘要在Use of alternative formatting styles的Python文档中提供(来自BPO)

When logging was added to the Python standard library, the only way of formatting messages with variable content was to use the %-formatting method.

[...]

Note that the formatting of logging messages for final output to logs is completely independent of how an individual logging message is constructed.

[...]

[...] you cannot directly make logging calls using str.format() or string.Template syntax, because internally the logging package uses %-formatting to merge the format string and the variable arguments. There would be no changing this while preserving backward compatibility, since all logging calls which are out there in existing code will be using %-format strings.

将其翻译成Python就是这样

In [1]: import logging

In [2]: # this is roughly how result of "logging.info('foo %s', 'bar')" looks
   ...: dummy = logging.makeLogRecord({'msg': 'foo %s', 'args': ('bar',)})

In [3]: # logger.Formatter roughly works as follows
   ...: dummy.message = dummy.getMessage()         # ①
   ...: '%(message)s' % dummy.__dict__             # ②
Out[3]: 'foo bar'

In [4]: '{message}'.format(**dummy.__dict__)       # ③
Out[4]: 'foo bar'

② 和③ 是可配置的(还有string.Template选项),① 不是,而all it is是:

def getMessage(self):
    """
    Return the message for this LogRecord.
    Return the message for this LogRecord after merging any user-supplied
    arguments with the message.
    """
    msg = str(self.msg)
    if self.args:
        msg = msg % self.args
    return msg

当然,因为Python 3.2日志记录工厂可以通过^{}进行配置,如Customizing LogRecord中所述。但是对它进行子分类,覆盖getMessage并用msg = msg.format(self.args)*替换msg = msg % self.args将使用logging和传递参数(可能以微妙的方式取决于日志记录级别)破坏所有现有代码

解决方案

我在考虑如何重写getMessage,使其向后兼容%样式,支持{样式,并且不会变得太慢。这就是我的目的

import collections.abc
import logging


class StrFormatLogRecord(logging.LogRecord):
    """
    Drop-in replacement for ``LogRecord`` that supports ``str.format``.
    """

    def getMessage(self):
        msg = str(self.msg)
        if self.args:
            try:
                msg = msg % ()
            except TypeError:
                # Either or the two is expected indicating there's
                # a placeholder to interpolate:
                #
                # - not all arguments converted during string formatting
                # - format requires a mapping" expected
                #
                # If would've been easier if Python printf-style behaved
                # consistently for "'' % (1,)" and "'' % {'foo': 1}". But
                # it raises TypeError only for the former case.
                msg = msg % self.args
            else:
                # There's special case of first mapping argument. See
                # duner init of logging.LogRecord.
                if isinstance(self.args, collections.abc.Mapping):
                    msg = msg.format(**self.args)
                else:
                    msg = msg.format(*self.args)
                
        return msg

它可以像这样使用:

def main():
    args = {'arg1': 10, 'arg2': 20}
    logging.info('Input args: %.2f %.2f', *args.values())
    logging.info('Input args: %d %d', *args.values())
    logging.info('Input args: %s %s', *args.values())
    logging.info('Input args: %(arg1).2f %(arg2).2f', args)
    logging.info('Input args: %(arg1)d %(arg2)d', args)
    logging.info('Input args: %(arg1)s %(arg2)s', args)
    logging.info('Input args: {0:.2f} {1:.2f}', *args.values())
    logging.info('Input args: {0:d} {1:d}', *args.values())
    logging.info('Input args: {0} {1}', *args.values())
    logging.info('Input args: {arg1:.2f} {arg2:.2f}', args)
    logging.info('Input args: {arg1:d} {arg2:d}', args)
    logging.info('Input args: {arg1} {arg2}', args)


if __name__ == '__main__':
    logging.basicConfig(
        level=logging.INFO,
        format='{asctime} {message}',
        style='{',
    )
    logging.setLogRecordFactory(StrFormatLogRecord)
    
    main()

在所有4种情况下,它都会生成格式正确的输出。但是,仍然存在一些边缘中断或格式不正确的情况(除了str.format模板包含printf样式占位符的情况之外,但是它是否相关取决于您)

... Input args: 10.00 20.00
... Input args: 10 20
... Input args: 10 20

相关问题 更多 >