Python 使用新格式字符串记录变量数据

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

我使用python 2.7.3的日志记录功能:

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

我喜欢带大括号的“新”格式。所以我试着做一些类似的事情:

 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)