Python 使用新格式字符串记录变量数据
我使用python 2.7.3的日志记录功能: 日志记录包预先更新更新的格式选项,如str.format()和string.Template。支持这些较新的格式选项 我喜欢带大括号的“新”格式。所以我试着做一些类似的事情:Python 使用新格式字符串记录变量数据,python,python-2.7,string-formatting,Python,Python 2.7,String Formatting,我使用python 2.7.3的日志记录功能: 日志记录包预先更新更新的格式选项,如str.format()和string.Template。支持这些较新的格式选项 我喜欢带大括号的“新”格式。所以我试着做一些类似的事情: log = logging.getLogger("some.logger") log.debug("format this message {0}", 1) 和获取错误: TypeError:在字符串格式化过程中并非所有参数都已转换 我错过了什么 还有,我不想用 log
log = logging.getLogger("some.logger")
log.debug("format this message {0}", 1)
和获取错误:
TypeError:在字符串格式化过程中并非所有参数都已转换
我错过了什么
还有,我不想用
log.debug("format this message {0}".format(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
编辑:看看答案;它允许在调用记录器的方法(debug()、info()、error()等)时,在不使用样板文件的情况下使用其他格式样式
从文档-: 日志调用(logger.debug()、logger.info()等)只需要 实际日志记录消息本身的位置参数,带有 关键字参数仅用于确定如何处理的选项 实际的日志调用(例如exc_info关键字参数) 指示应记录回溯信息,或附加 关键字参数,指示要添加的其他上下文信息 添加到日志中)。因此,您不能使用直接进行日志记录调用 str.format()或string.Template语法,因为日志记录 包使用%-格式来合并格式字符串和变量 论据。在向后保留的同时不会改变这一点 兼容性,因为所有日志调用都存在于现有 代码将使用%格式字符串 以及: 但是,有一种方法可以使用{}和$-格式 构建您的个人日志消息。回想一下,这是给你的留言 可以使用任意对象作为消息格式字符串,并且 日志记录包将对该对象调用str(),以获取实际的 格式字符串 将此复制粘贴到
模块的任何位置:
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)
然后:
注意:实际格式设置会延迟到必要时,例如,如果未记录调试消息,则根本不会执行格式设置。当我发现日志记录仅使用printf样式的格式设置时,这是我解决问题的方法。它允许日志调用保持不变——没有特殊的语法,例如log.info(uu(“val是{}”,“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} substitution", type="brace")
值得注意的是,如果用于大括号替换的关键字包括level
,msg
,args
,exc\u info
,extra
或stack\u info
,则此实现存在问题。这些是Logger
的log
方法使用的参数名称。如果需要使用这些名称中的一个,请修改过程
以排除这些名称,或从日志
调用中删除日志
。进一步说明,此实现也会自动忽略记录器的拼写错误的关键字(例如,ectra
)。这里是另一个没有Dunes答案中提到的关键字问题的选项。它只能处理位置({0}
)参数,不能处理关键字({foo}
)参数。它也不需要两次调用来格式化(使用下划线)。它确实具有子类化str
的ick因素:
类括号字符串(str):
def _; mod ___;(自身、其他):
返回self.format(*其他)
定义(自我):
回归自我
类样式适配器(logging.LoggerAdapter):
def uuu init uuuu(self,logger,extra=None):
super(样式适配器,self)。\uuuuu init\uuuuuu(记录器,额外)
def流程(自我、味精、kwargs):
如果kwargs.pop('style',“%”)==“{”:#可选
msg=括号字符串(msg)
返回味精,kwargs
您可以这样使用它:
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…
logger=StyleAdapter(logging.getLogger(_名称__))
info(“骑士:{0}”,“ni”,style=“{”)
logger.info(“骑士:{}”,“灌木丛”,style=“{”)
当然,您可以删除用#optional
标记的复选框,以强制通过适配器的所有消息使用新样式的格式
几年后阅读此答案的读者请注意:从Python 3.2开始,您可以使用Formatter
对象:
日志记录(从3.2开始)改进了对这两种附加格式样式的支持
Formatter类已得到增强,可采用一个名为style
的附加可选关键字参数
默认值为'%
,但其他可能的值为'{'
和'$'
,它们对应于其他两个值
格式化样式。默认情况下(正如您所期望的那样)会保持向后兼容性,但是
通过显式指定样式参数,可以指定有效的格式字符串
与或
这些文件提供了一个例子
logging.Formatter({asctime}{name}{levelname:8s}{message}),style={')
请注意,在这种情况下,您仍然无法使用新格式调用记录器
logger.info(“骑士:{say}”,say=“ni”)#不起作用!
info(“骑士:{0}”,“ni”)#也不起作用
正如其他答案所提到的,大括号样式的格式仅用于格式字符串,而不是实际的日志消息
要在实际日志消息上启用大括号样式的格式,我们可以对日志程序代码进行一些修补
下面对logging
模块进行了修补,以创建一个get\u logger
函数,该函数将返回一个记录器,该记录器将为其处理的每个日志记录使用新型格式
导入工具
导入日志记录
导入类型
def_获取_消息(记录):
“”“logging.LogRecord.getMessage的替换
使用新型字符串格式的
log = StyleAdapter(logging.getLogger(__name__))
log.info("a log message using {type} substitution", type="brace")
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)
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")
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…
from collections import namedtuple
from collections.abc import Mapping
from functools import partial
from pprint import pformat
from string import Formatter
import logging
Logger = logging.getLoggerClass()
LogRecord = logging.getLogRecordFactory()
class CustomFormatter(Formatter):
def format_field(self, value, format_spec):
if format_spec.endswith('p'):
value = pformat(value)
format_spec = format_spec[:-1]
return super().format_field(value, format_spec)
custom_formatter = CustomFormatter()
class LogWithFormat:
def __init__(self, obj):
self.obj = obj
def __getattr__(self, name):
return partial(getattr(self.obj, name), use_format=True)
ArgsSmuggler = namedtuple('ArgsSmuggler', ('args', 'smuggled'))
class CustomLogger(Logger):
def __init__(self, *ar, **kw):
super().__init__(*ar, **kw)
self.f = LogWithFormat(self)
def _log(self, level, msg, args, *ar, use_format=False, **kw):
super()._log(level, msg, ArgsSmuggler(args, use_format), *ar, **kw)
class CustomLogRecord(LogRecord):
def __init__(self, *ar, **kw):
args = ar[5]
# RootLogger use CustomLogRecord but not CustomLogger
# then just unpack only ArgsSmuggler instance
args, use_format = args if isinstance(args, ArgsSmuggler) else (args, False)
super().__init__(*ar[:5], args, *ar[6:], **kw)
self.use_format = use_format
def getMessage(self):
return self.getMessageWithFormat() if self.use_format else super().getMessage()
def getMessageWithFormat(self):
msg = str(self.msg)
args = self.args
if args:
fmt = custom_formatter.format
msg = fmt(msg, **args) if isinstance(args, Mapping) else fmt(msg, *args)
return msg
logging.setLogRecordFactory(CustomLogRecord)
logging.setLoggerClass(CustomLogger)
log = logging.getLogger(__name__)
log.info('%s %s', dict(a=1, b=2), 5)
log.f.info('{:p} {:d}', dict(a=1, b=2), 5)