懒惰记录器消息字符串评估

时间:2010-11-10 20:33:31

标签: python logging

我在python应用程序中使用标准的python日志记录模块:

import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("log")
while True:
  logger.debug('Stupid log message " + ' '.join([str(i) for i in range(20)]) )
  # Do something

问题在于虽然调试级别未启用,但是在每次循环迭代时都会评估该愚蠢的日志消息,这会严重损害性能。

有什么解决方法吗?

在C ++中,我们有log4cxx包提供如下宏:
LOG4CXX_DEBUG(logger, messasage)
这有效地评估为

if (log4cxx::debugEnabled(logger)) {
    log4cxx.log(logger,log4cxx::LOG4CXX_DEBUG, message)
}

但是由于Python(AFAIK)中没有宏,是否有一种有效的记录方法?

6 个答案:

答案 0 :(得分:70)

日志记录模块已经部分支持您要执行的操作。这样做:

log.debug("Some message: a=%s b=%s", a, b)

......而不是:

log.debug("Some message: a=%s b=%s" % (a, b))

日志记录模块非常智能,不会产生完整的日志消息,除非消息实际记录在某处。

要将此功能应用于您的特定请求,您可以创建一个lazyjoin类。

class lazyjoin:
    def __init__(self, s, items):
        self.s = s
        self.items = items
    def __str__(self):
        return self.s.join(self.items)

像这样使用它(注意使用生成器表达式,增加了懒惰):

logger.info('Stupid log message %s', lazyjoin(' ', (str(i) for i in range(20))))

这是一个显示此功能的演示。

>>> import logging
>>> logging.basicConfig(level=logging.INFO)
>>> logger = logging.getLogger("log")
>>> class DoNotStr:
...     def __str__(self):
...         raise AssertionError("the code should not have called this")
... 
>>> logger.info('Message %s', DoNotStr())
Traceback (most recent call last):
...
AssertionError: the code should not have called this
>>> logger.debug('Message %s', DoNotStr())
>>>

在演示中,logger.info()调用命中了断言错误,而logger.debug()没有达到那么远。

答案 1 :(得分:34)

当然,以下内容不如宏有效:

if logger.isEnabledFor(logging.DEBUG):
    logger.debug(
        'Stupid log message ' + ' '.join([str(i) for i in range(20)])
    )

但简单,evaluates in lazy fashion并且比接受的答案快4倍

class lazyjoin:
    def __init__(self, s, items):
        self.s = s
        self.items = items

    def __str__(self):
        return self.s.join(self.items)

logger.debug(
    'Stupid log message %s', lazyjoin(' ', (str(i) for i in range(20)))
)

请参阅benchmark-src了解我的设置。

答案 2 :(得分:24)

import logging
import time

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("log")

class Lazy(object):
    def __init__(self,func):
        self.func=func
    def __str__(self):
        return self.func()

logger.debug(Lazy(lambda: time.sleep(20)))

logger.info(Lazy(lambda: "Stupid log message " + ' '.join([str(i) for i in range(20)])))
# INFO:log:Stupid log message 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

如果运行脚本,您会注意到第一个logger.debug命令执行时间不超过20秒。这表明当日志记录级别低于设置级别时,不会评估参数。

答案 3 :(得分:13)

Shane指出,使用

log.debug("Some message: a=%s b=%s", a, b)

......而不是:

log.debug("Some message: a=%s b=%s" % (a, b))
如果实际记录了消息,

仅通过执行字符串格式化来节省一些时间。

但这并不能完全解决问题,因为您可能需要预先处理值以格式化为字符串,例如:

log.debug("Some message: a=%s b=%s", foo.get_a(), foo.get_b())

在这种情况下,如果没有记录,则obj.get_a()obj.get_b()将计算甚至

解决方法是使用lambda函数,但这需要一些额外的机制:

class lazy_log_debug(object):
    def __init__(self, func):
        self.func = func
        logging.debug("%s", self)
    def __str__(self):
        return self.func()

...然后您可以使用以下内容进行记录:

lazy_log_debug(lambda: "Some message: a=%s b=%s" % (foo.get_a(), foo.get_b()))

在这种情况下,如果log.debug决定执行格式化,则lambda函数将仅 ,因此调用__str__方法。

请注意:该解决方案的开销可能会超出收益:-)但至少在理论上,它可以完成懒惰的日志记录。

答案 4 :(得分:0)

我出席,Lazyfy

class Lazyfy(object):
    __slots__ = 'action', 'value'

    def __init__(self, action, *value):
        self.action = action
        self.value = value

    def __str__(self):
        return self.action(*self.value)

用法:

from pprint import pformat
log.debug("big_result: %s", Lazyfy(pformat, big_result))
log.debug( "x y z: %s", Lazyfy( lambda x, y, z: ' ,'.join( [x, y, z] ), '1', '2', '3' ) )

原始示例:

logger.info('Stupid log message %s', Lazyfy(lambda: ' '.join((str(i) for i in range(20)))))

如您所见,这还涵盖了另一个使用lambda函数的答案,但是使用value属性和扩展会占用更多内存。但是,它通过以下方式节省了更多内存:Usage of __slots__?

最后,到目前为止,最有效的解决方案仍然是以下建议的另一个答案:

if logger.isEnabledFor(logging.DEBUG): 
    logger.debug('Stupid log message ' + ' '.join([str(i) for i in range(20)]))

答案 5 :(得分:0)

如果仅依赖于访问全局状态属性,则可以实例化python类,并使用var a = await FunctionA(param1); var output = await FunctionB(a, param2); 方法将其分类化:

__str__

相关:

  1. Conditionally evaluated debug statements in Python
  2. What are metaclasses in Python?