用新的格式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的关键字包括level
, msg
, args
, exc_info
, extra
或stack_info
,则此实现有问题。 这些是Logger
的log
方法使用的参数名称。 如果您需要这些名称之一,则修改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)