记录与新格式字符串变量数据(Logging variable data with new forma

2019-06-18 16:58发布

我使用日志记录工具为Python 2.7.3。 这个Python版本的文档说 :

记录包预日期较新的格式选项如str.format()和string.Template。 这些新的格式选项,支持...

我喜欢用花括号“新”的格式。 所以我试图做这样的事情:

 log = logging.getLogger("some.logger")
 log.debug("format this message {0}", 1)

并得到错误:

类型错误:不是所有的参数字符串格式化过程中转换

我怀念的是什么?

PS我不希望使用

log.debug("format this message {0}".format(1))

因为在这种情况下,消息总是被记录不分级别格式化。

Answer 1:

编辑:看看该StyleAdapter在@Dunes的回答方法与此不同的答案; 它允许使用替代格式样式没有样板,同时调用记录仪的方法(调试(),信息(),误差(),等)。


从文档- 替代格式样式用途 :

日志记录的呼叫(logger.debug(),logger.info()等等)仅采取实际的日志消息本身位置参数,只有用于确定如何处理该实际记录的电话选项中使用的关键字的参数(例如该exc_info关键字参数以表明追溯信息应该被记录下来,或额外的关键字参数来表示要添加到日志额外的上下文信息)。 所以你不能使用str.format()或string.Template语法直接进行通话记录,因为内部日志包使用%-formatting合并格式字符串和可变参数。 也就没有改变这一点的同时保持向后兼容,因为所有的日志记录调用它们赫然出现在现有的代码将使用%-format字符串。

和:

然而,有,你可以使用{}的方式 - 和$ - 格式化,构建您的个人日志消息。 回想一下,一个消息就可以使用任意对象作为消息格式字符串,并且记录包调用STR()的对象上,以获得实际格式字符串。

这个复制-粘贴到wherever模块:

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)

然后:

from wherever import BraceMessage as __

log.debug(__('Message with {0} {name}', 2, name='placeholders'))

注:实际的格式被延迟,直到有必要例如,如果DEBUG消息不被记录则不执行格式化的。



Answer 2:

这里是一个没有在沙丘的回答中提到的关键字问题的另一种选择。 它只能处理位置( {0}的参数和不关键字( {foo}参数。 它也并不需要两个电话(使用下划线)格式化。 它有子类的ICK因素str

class BraceString(str):
    def __mod__(self, other):
        return self.format(*other)
    def __str__(self):
        return self


class StyleAdapter(logging.LoggerAdapter):

    def __init__(self, logger, extra=None):
        super(StyleAdapter, self).__init__(logger, extra)

    def process(self, msg, kwargs):
        if kwargs.pop('style', "%") == "{":  # optional
            msg = BraceString(msg)
        return msg, kwargs

您可以使用这样的:

logger = StyleAdapter(logging.getLogger(__name__))
logger.info("knights:{0}", "ni", style="{")
logger.info("knights:{}", "shrubbery", style="{")

当然,你可以删除复地注意到# optional迫使所有的消息通过适配器使用新型的格式。


注意后面的人阅读这个答案岁 :与Python 3.2开始,你可以使用样式参数与Formatter对象:

日志记录(如3.2)规定了这两个附加的格式样式改进的支持。 Formatter类得到增强,可以采取命名的附加,可选关键字参数style 。 默认为'%' ,但其他可能的值是'{''$' ,这对应于其他两种格式样式。 向后兼容默认情况下(如你所期望的)保持不变,但通过明确指定样式参数,你可以指定与合作格式化字符串的能力str.format()string.Template

该文档提供的示例logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')

请注意,在这种情况下,你仍然不能调用logger使用新的格式。 即下仍然无法工作:

logger.info("knights:{say}", say="ni")  # Doesn't work!
logger.info("knights:{0}", "ni")  # Doesn't work either


Answer 3:

较容易的解决办法是使用优秀的logbook模块

import logbook
import sys

logbook.StreamHandler(sys.stdout).push_application()
logbook.debug('Format this message {k}', k=1)

或者更完整:

>>> import logbook
>>> import sys
>>> logbook.StreamHandler(sys.stdout).push_application()
>>> log = logbook.Logger('MyLog')
>>> log.debug('Format this message {k}', k=1)
[2017-05-06 21:46:52.578329] DEBUG: MyLog: Format this message 1


Answer 4:

这是我解决问题时,我发现记录仅使用printf类型的格式。 它可以记录通话保持不变-无特殊语法如log.info(__("val is {}", "x")) 需要的代码的变化来包装记录器中一个StyleAdapter

from inspect import getargspec

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

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

class StyleAdapter(logging.LoggerAdapter):
    def __init__(self, logger):
        self.logger = logger

    def log(self, level, msg, *args, **kwargs):
        if self.isEnabledFor(level):
            msg, log_kwargs = self.process(msg, kwargs)
            self.logger._log(level, BraceMessage(msg, args, kwargs), (), 
                    **log_kwargs)

    def process(self, msg, kwargs):
        return msg, {key: kwargs[key] 
                for key in getargspec(self.logger._log).args[1:] if key in kwargs}

使用方法是:

log = StyleAdapter(logging.getLogger(__name__))
log.info("a log message using {type} substiution", type="brace")

值得一提的是这个实现有问题,如果使用大括号替代关键词包括levelmsgargsexc_infoextrastack_info 。 这是由所使用的参数名称log的方法Logger 。 如果你需要这些名称之一,那么修改process排除这些名字或只是删除log_kwargs_log电话。 在进一步的说明,这也实现默默地忽略拼错的关键字意味着记录器(如ectra )。



Answer 5:

至于其他的答案中提到,支架式的格式在Python 3.2中引入只用于格式字符串,而不是实际的日志消息。

对于Python 3.5的,还有就是用括号形式的格式来记录消息没有很好的方式。

然而,与在Python中大多数事情一样,有是一个不很好的方式。

下面的猴子补丁的logging模块来创建一个get_logger函数会返回一个使用新样式格式为它处理每个日志记录的记录器。

import functools
import logging
import types

def _get_message(record):
    """Replacement for logging.LogRecord.getMessage
    that uses the new-style string formatting for
    it's messages"""
    msg = str(record.msg)
    args = record.args
    if args:
        if not isinstance(args, tuple):
            args = (args,)
        msg = msg.format(*args)
    return msg

def _handle_wrap(fcn):
    """Wrap the handle function to replace the passed in
    record's getMessage function before calling handle"""
    @functools.wraps(fcn)
    def handle(record):
        record.getMessage = types.MethodType(_get_message, record)
        return fcn(record)
    return handle

def get_logger(name=None):
    """Get a logger instance that uses new-style string formatting"""
    log = logging.getLogger(name)
    if not hasattr(log, "_newstyle"):
        log.handle = _handle_wrap(log.handle)
    log._newstyle = True
    return log

用法:

>>> log = get_logger()
>>> log.warning("{!r}", log)
<logging.RootLogger object at 0x4985a4d3987b>

笔记:

  • 只会影响通过创建特定的记录器get_logger功能。
  • 如果记录是从正常的再次访问logging.getLogger()调用中,新样式格式仍然适用
  • kwargs不支持
  • 性能损失应该是最小的(重写单一的功能的指针为每个日志消息)
  • 该消息的格式被延迟,直到它被输出
  • 不会被存储在停止ARGS logging.LogRecord对象(在某些情况下非常有用)
  • 从看logging模块的源代码 ,好像它应该工作在Python 2.6时一路str.format介绍(但只在Python的3.5测试)。


Answer 6:

尝试logging.setLogRecordFactory在Python 3.2+:

import collections
import logging


class _LogRecord(logging.LogRecord):

    def getMessage(self):
        msg = str(self.msg)
        if self.args:
            if isinstance(self.args, collections.Mapping):
                msg = msg.format(**self.args)
            else:
                msg = msg.format(*self.args)
        return msg


logging.setLogRecordFactory(_LogRecord)


Answer 7:

下面是一些工作真正简单:

debug_logger: logging.Logger = logging.getLogger("app.debug")

def mydebuglog(msg: str, *args, **kwargs):
    if debug_logger.isEnabledFor(logging.DEBUG):
        debug_logger.debug(msg.format(*args, **kwargs))

然后:

mydebuglog("hello {} {val}", "Python", val="World")


Answer 8:

我创建了一个自定义格式,称为ColorFormatter处理该问题是这样的:

class ColorFormatter(logging.Formatter):

    def format(self, record):
        # previous stuff, copy from logging.py…

        try:  # Allow {} style
            message = record.getMessage()  # printf
        except TypeError:
            message = record.msg.format(*record.args)

        # later stuff…

这使得它与各种库兼容。 其缺点是,它可能不是高性能由于潜在的尝试两次字符串的格式。



文章来源: Logging variable data with new format string