用新的格式stringloggingvariables数据

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

日志包会预先更新格式化选项,比如str.format()和string.Template。 这些新的格式选项是支持…

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

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

并得到错误:

TypeError:不是在string格式化过程中转换的所有参数

我在这里想念什么?

PS我不想用

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

因为在这种情况下,不pipelogging器级别如何,消息总是被格式化。

编辑:看看@Dunes的答案StyleAdapter方法不像这个答案; 它允许在调用logging器的方法(debug(),info(),error()等)时使用替代格式化样式而不使用样板文件。


从文档 – 使用其他格式样式 :

logging调用(logger.debug(),logger.info()等)只为实际日志消息本身提供位置参数,关键字参数仅用于确定如何处理实际日志logging调用的选项(例如,exc_info关键字参数指示追溯信息应该被logging,或者extra关键字参数指示附加的上下文信息被添加到日志)。 所以你不能直接使用str.format()或string.Template语法进行日志logging调用,因为在内部日志logging包使用%-formatting来合并格式string和variables参数。 在保持向后兼容性的同时不会改变这一点,因为现有代码中的所有日志调用将使用%格式的string。

和:

但是,有一种方法可以使用{ – 和$ – 格式来构build您的个人日志消息。 回想一下,对于消息,可以使用任意对象作为消息格式string,并且日志logging包将调用该对象的str()以获取实际的格式string。

复制粘贴到模块的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消息没有被logging,那么根本不执行格式化。

这是另一个选项,没有在Dunes的答案中提到的关键字问题。 它只能处理位置( {0} )参数而不处理关键字( {foo} )参数。 它也不需要两个调用格式(使用下划线)。 它具有子类str的ick因子:

 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对象中使用style参数 :

日志logging(从3.2开始)为这两种附加的格式化样式提供了改进的支持。 Formatter类已经增强,可以使用一个名为style可选关键字参数。 默认为'%' ,但其他可能的值是'{''$' ,这与其他两种格式样式相对应。 向后兼容性默认情况下保持不变(如您所期望的),但是通过明确指定样式参数,您可以指定格式string,它们可以与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 

更简单的解决scheme将是使用优秀的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 

这是我的解决scheme,当我发现日志logging只使用printf样式格式。 它允许日志logging调用保持不变 – 没有特殊的语法,如log.info(__("val is {}", "x")) 。 编码所需的更改是将logging器包装在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") 

值得注意的是,如果用于大括号replace的关键字包括levelmsgargsexc_infoextrastack_info ,则此实现有问题。 这些是Loggerlog方法使用的参数名称。 如果您需要这些名称之一,则修改process以排除这些名称,或从_log调用中删除log_kwargs 。 另外还要注意的是,这个实现也默默地忽略了logging器的拼写错误的关键字(如ectra )。

正如其他答案所提到的, Python 3.2中引入的大括号格式化仅用于格式string,而不是实际的日志消息。

从Python 3.5开始,使用大括号格式来logging消息是没有好方法的。

但是,与Python中的大多数事情一样,这是一个不好的方法。

下面的monkey-patches logging模块创build一个get_logger函数,该函数将返回一个logging器,该logging器使用它处理的每个日志logging的新样式格式。

 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函数创build的特定logging器。
  • 如果通过正常的logging.getLogger()调用再次访问logging器,则新样式的格式仍然适用
  • kwargs不支持
  • 性能命中应该是最小的(重写每个日志消息的单个函数指针)
  • 消息的格式化被延迟直到输出
  • 不会停止将参数存储在logging.LogRecord对象(在某些情况下有用)
  • 从查看logging模块的源代码看来,它应该可以一直运行到Python 2.6,当str.format被引入时(但是只在Python 3.5上testing过)。

这是一个真正简单的工作:

 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") 

在Python 3.2+中尝试logging.setLogRecordFactory

 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)