Maj*_*cRa 76 python string-formatting python-2.7
我使用python 2.7.3的日志工具.这个Python版本的文档说:
日志包提前更新格式化选项,如str.format()和string.Template.支持这些较新的格式化选项...
我喜欢花括号的"新"格式.所以我想尝试做类似的事情:
log = logging.getLogger("some.logger")
log.debug("format this message {0}", 1)
Run Code Online (Sandbox Code Playgroud)
并得到错误:
TypeError:并非在字符串格式化期间转换所有参数
我想念的是什么?
PS我不想用
log.debug("format this message {0}".format(1))
Run Code Online (Sandbox Code Playgroud)
因为在这种情况下,无论记录器级别如何,都始终格式化消息.
jfs*_*jfs 38
编辑:看看StyleAdapter
@Dunes答案中的方法,不像这个答案; 它允许在调用logger的方法(debug(),info(),error()等)时使用没有样板的替代格式样式.
从文档 - 使用替代格式样式:
记录调用(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)
Run Code Online (Sandbox Code Playgroud)
然后:
from wherever import BraceMessage as __
log.debug(__('Message with {0} {name}', 2, name='placeholders'))
Run Code Online (Sandbox Code Playgroud)
注意:实际格式化会延迟到必要时为止,例如,如果未记录DEBUG消息,则根本不执行格式化.
Fel*_*ipe 26
这是另一个没有Dunes回答中提到的关键字问题的选项.它只能处理positional({0}
)参数而不能处理keyword({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
Run Code Online (Sandbox Code Playgroud)
你这样使用它:
logger = StyleAdapter(logging.getLogger(__name__))
logger.info("knights:{0}", "ni", style="{")
logger.info("knights:{}", "shrubbery", style="{")
Run Code Online (Sandbox Code Playgroud)
当然,您可以删除标记的检查# optional
以强制通过适配器的所有消息使用新式格式.
多年后阅读此答案的人都要注意:从Python 3.2开始,您可以将style参数与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
Run Code Online (Sandbox Code Playgroud)
Dun*_*nes 22
当我发现日志记录仅使用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}
Run Code Online (Sandbox Code Playgroud)
用法是:
log = StyleAdapter(logging.getLogger(__name__))
log.info("a log message using {type} substiution", type="brace")
Run Code Online (Sandbox Code Playgroud)
值得一提的是这个实现有问题,如果使用大括号替换的关键词包括level
,msg
,args
,exc_info
,extra
或stack_info
.这些是log
方法使用的参数名称Logger
.如果您需要将这些名称之一然后修改process
排除这些名字或只是删除log_kwargs
从_log
通话.另外值得注意的是,此实现还默默地忽略了针对Logger的拼写错误的关键字(例如ectra
).
Tho*_*zco 20
更简单的解决方案是使用优秀的logbook
模块
import logbook
import sys
logbook.StreamHandler(sys.stdout).push_application()
logbook.debug('Format this message {k}', k=1)
Run Code Online (Sandbox Code Playgroud)
或者更完整:
>>> 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
Run Code Online (Sandbox Code Playgroud)
pR0*_*0Ps 10
正如其他答案所提到的,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
its 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
Run Code Online (Sandbox Code Playgroud)
用法:
>>> log = get_logger()
>>> log.warning("{!r}", log)
<logging.RootLogger object at 0x4985a4d3987b>
Run Code Online (Sandbox Code Playgroud)
笔记:
logging.getLogger
函数创建的特定记录器.get_logger
调用再次访问记录器,则新样式格式仍将适用get_logger
对象上(在某些情况下很有用)logging.getLogger()
模块源代码,它似乎应该exc_info
在引入时一直回到Python 2.6 (但仅在Python 3.5上进行了测试). 归档时间: |
|
查看次数: |
38270 次 |
最近记录: |