为什么python日志包不支持打印变长args?

时间:2022-06-23 21:59:49

When I first learned Python, I got used to doing this:

当我第一次学习Python时,我习惯这样做:

  print "text", lineNumber, "some dictionary", my_dict

When I wrote my own logging facility, I naturally wanted to be able to hand it an arbitrarily-sized list of items, so I did this:

当我编写自己的日志记录工具时,我自然希望能够提供一个任意大小的项目列表,所以我这样做:

def error(*args):
   print ERR_PREFIX,
   for _x in args:
      print _x,
   print "\r\n",

error("text", lineNumber, "some dictionary", my_dict)

Now I want to start using the logging package because it has a lot more goodies and I don't feel like replicating their effort. Overall it looks like a clean design that can do a lot. But I'm stymied by the fact that you can no longer present it with the same list of items for it to print. Instead, I must change all my calls to something more like this:

现在我想开始使用日志包,因为它有更多的好东西,我不想复制他们的努力。总的来说,它看起来像一个可以做很多事情的干净设计。但是我被你不能再提供相同的项目列表以便打印它而受到阻碍。相反,我必须将所有调用更改为更像这样的内容:

error("text %d some dictionary %s" % (lineNumber, my_dict))

Or, I could do something really silly like this:

或者,我可以做一些非常愚蠢的事情:

error(' '.join(map, str(("text", lineNumber, "some dictionary", my_dict))))

The question is, why omit such an obvious usage case? If you want to go from the typical 'print' statement straight to the new-fangled logging facility, shouldn't this be easier?

问题是,为什么省略这样一个明显的用例?如果您想从典型的“打印”声明直接进入新的日志记录设施,这不应该更容易吗?

As a follow-up question, can you think of a way to override the Logger class to perform this?

作为后续问题,您能想到一种覆盖Logger类来执行此操作的方法吗?

5 个解决方案

#1


I would suggest that it would be better to update the existing logging messages to the style that the logging module expects as it will be easier for other people looking at your code as the logging module will not longer function as they expect.

我建议最好将现有的日志消息更新为日志记录模块所期望的样式,因为对于查看代码的其他人来说,更容易,因为日志记录模块将不再按预期运行。

That out of the way, the following code will make the logging module behave as you desire.

除此之外,以下代码将使日志记录模块按您的需要运行。

import logging
import types

class ExtendedLogRecord(logging.LogRecord):

    def getMessage(self):
        """
        Return the message for this LogRecord.

        Return the message for this LogRecord after merging any user-supplied
        arguments with the message.
        """
        if not hasattr(types, "UnicodeType"): #if no unicode support...
            msg = str(self.msg)
        else:
            try:
                msg = str(self.msg)
            except UnicodeError:
                msg = self.msg      #Defer encoding till later
        if self.args:
            msg +=' '+' '.join(map(str,self.args))
        return msg

#Patch the logging default logging class
logging.RootLogger.makeRecord=lambda self,*args: ExtendedLogRecord(*args)

some_dict={'foo':14,'bar':15}
logging.error('text',15,'some dictionary',some_dict)

Output:

ERROR:root:text 15 some dictionary {'foo': 14, 'bar': 15}

#2


Patching the logging package (as one answer recommended) is actually a bad idea, because it means that other code (that you didn't write, e.g. stuff in the standard library) that calls logging.error() would no longer work correctly.

修补日志包(作为一个建议的答案)实际上是一个坏主意,因为这意味着调用logging.error()的其他代码(您没有编写,例如标准库中的内容)将无法正常工作。

Instead, you can change your existing error() function to call logging.error() instead or print:

相反,您可以更改现有的error()函数来调用logging.error()或print:

def error(*args):
  logging.error('%s', ' '.join(map(str, args)))

(If there might be unicode objects you'd have to be a bit more careful, but you get the idea.)

(如果可能有unicode对象,你必须要小心一些,但是你明白了。)

#3


Your claim about logging is not completely true.

您对日志记录的主张并不完全正确。

log= logging.getLogger( "some.logger" )
log.info( "%s %d", "test", value )
log.error("text %d some dictionary %s", lineNumber, my_dict) 

You don't need to explicitly use the string formatting operator, %

您不需要显式使用字符串格式化运算符,%


Edit

You can leverage your original "error" function.

您可以利用原始的“错误”功能。

def error( *args ):
    log.error( " ".join( map( str, args ) ) )

Which will probably make the transition less complex.

这可能会使转变变得不那么复杂。

You can also do this.

你也可以这样做。

class MyErrorMessageHandler( object ):
    def __init__( self, logger ):
        self.log= logger
    def __call__( self, *args ):
        self.log.error( " ".join( map( str, args ) ) )
error= MyErrorMessageHandler( logging.getLogger("some.name") )

Which might be palatable, also.

这也许是可口的。

#4


Well, printing and logging are two very different things. It stands to reason that as such their usages could potentially be quite different as well.

嗯,打印和记录是两个非常不同的东西。按理说,这样他们的用法可能也会有很大不同。

#5


It's relatively easy to add a method to a class dynamically. Why not just add your method to Logging.

动态地向类添加方法相对容易。为什么不将您的方法添加到Logging。

#1


I would suggest that it would be better to update the existing logging messages to the style that the logging module expects as it will be easier for other people looking at your code as the logging module will not longer function as they expect.

我建议最好将现有的日志消息更新为日志记录模块所期望的样式,因为对于查看代码的其他人来说,更容易,因为日志记录模块将不再按预期运行。

That out of the way, the following code will make the logging module behave as you desire.

除此之外,以下代码将使日志记录模块按您的需要运行。

import logging
import types

class ExtendedLogRecord(logging.LogRecord):

    def getMessage(self):
        """
        Return the message for this LogRecord.

        Return the message for this LogRecord after merging any user-supplied
        arguments with the message.
        """
        if not hasattr(types, "UnicodeType"): #if no unicode support...
            msg = str(self.msg)
        else:
            try:
                msg = str(self.msg)
            except UnicodeError:
                msg = self.msg      #Defer encoding till later
        if self.args:
            msg +=' '+' '.join(map(str,self.args))
        return msg

#Patch the logging default logging class
logging.RootLogger.makeRecord=lambda self,*args: ExtendedLogRecord(*args)

some_dict={'foo':14,'bar':15}
logging.error('text',15,'some dictionary',some_dict)

Output:

ERROR:root:text 15 some dictionary {'foo': 14, 'bar': 15}

#2


Patching the logging package (as one answer recommended) is actually a bad idea, because it means that other code (that you didn't write, e.g. stuff in the standard library) that calls logging.error() would no longer work correctly.

修补日志包(作为一个建议的答案)实际上是一个坏主意,因为这意味着调用logging.error()的其他代码(您没有编写,例如标准库中的内容)将无法正常工作。

Instead, you can change your existing error() function to call logging.error() instead or print:

相反,您可以更改现有的error()函数来调用logging.error()或print:

def error(*args):
  logging.error('%s', ' '.join(map(str, args)))

(If there might be unicode objects you'd have to be a bit more careful, but you get the idea.)

(如果可能有unicode对象,你必须要小心一些,但是你明白了。)

#3


Your claim about logging is not completely true.

您对日志记录的主张并不完全正确。

log= logging.getLogger( "some.logger" )
log.info( "%s %d", "test", value )
log.error("text %d some dictionary %s", lineNumber, my_dict) 

You don't need to explicitly use the string formatting operator, %

您不需要显式使用字符串格式化运算符,%


Edit

You can leverage your original "error" function.

您可以利用原始的“错误”功能。

def error( *args ):
    log.error( " ".join( map( str, args ) ) )

Which will probably make the transition less complex.

这可能会使转变变得不那么复杂。

You can also do this.

你也可以这样做。

class MyErrorMessageHandler( object ):
    def __init__( self, logger ):
        self.log= logger
    def __call__( self, *args ):
        self.log.error( " ".join( map( str, args ) ) )
error= MyErrorMessageHandler( logging.getLogger("some.name") )

Which might be palatable, also.

这也许是可口的。

#4


Well, printing and logging are two very different things. It stands to reason that as such their usages could potentially be quite different as well.

嗯,打印和记录是两个非常不同的东西。按理说,这样他们的用法可能也会有很大不同。

#5


It's relatively easy to add a method to a class dynamically. Why not just add your method to Logging.

动态地向类添加方法相对容易。为什么不将您的方法添加到Logging。