【问题标题】:Python: how to do lazy debug loggingPython:如何进行延迟调试日志记录
【发布时间】:2014-01-27 09:21:05
【问题描述】:

我有一些像这样的python:

def foo():
    logger = logging.getLogger()
    # do something here
    logger.debug('blah blah {}'.format(expensive_func()))

foo()

其中expensive_func() 是一个返回字符串的函数,执行起来很昂贵。

开发时,日志级别设置为DEBUG,执行expensive_func(),记录消息,一切正常。

问题是当我将日志级别设置为严格大于 DEBUG 时,比如说 WARNING,在生产环境中,显然expensive_func() 的返回值不会被记录,但是昂贵的函数本身仍然会被执行。

我的问题是:如何防止python在日志级别为WARNING时执行昂贵的功能?

我不想删除该调试行或在昂贵的函数中添加类似if level > DEBUG: return 的内容。

谢谢。

编辑

我刚才访问了Lazy logger message string evaluation,但对它并不满意,主要是因为:

  1. 有点难看;
  2. 即使我用一些 Lazy 类包装了昂贵的函数,当我有两个昂贵的函数时我应该怎么办? (如下所示)。
class Lazy:
    def __init__(self, func, *a, **ka):
        self.func= func
        self.a = a
        self.ka= ka
    def __str__(self):
        return str(self.func(*self.a, **self.ka))

# Though this is ugly, it works
logger.debug('Message: %s', Lazy(expensive_func))

# What if I wanted to do this?
# logger.debug('Message: {}'.format(expf_1(expf_2(some_arg))))
# Maybe I can modify class Lazy to make something like this to work
# but it really doesn't feel right
# logger.debug('Message: {}', Lazy(expf_1, Lazy(expf_2, some_arg)))

【问题讨论】:

标签: python logging lazy-evaluation


【解决方案1】:

查看this part的文档。

更新:日志记录已经支持惰性评估,但与您评论中描述的方式略有不同。例如,请参阅以下脚本:

import logging

def expensive_func(*args):
    print('Expensive func called: %s' % (args,))
    return sum(args)

class DeferredMessage(object):
    def __init__(self, func, *args):
        self.func = func
        self.args = args

    def __str__(self):
        return 'Message {0}'.format(self.func(*self.args))

if __name__ == '__main__':
    logging.basicConfig()
    logging.info(DeferredMessage(expensive_func, 1, 2))
    logging.warning(DeferredMessage(expensive_func, 3, 4))
    logging.error(DeferredMessage(expensive_func, 5, 6))

当上面的脚本运行时,它应该打印出来

Expensive func called: (3, 4)
WARNING:root:Message 7
Expensive func called: (5, 6)
ERROR:root:Message 11

这表明仅在必要时才调用可能昂贵的函数。上面的例子当然可以泛化为允许将格式字符串传递给DeferredMessage,并使用kwargs,等等。

【讨论】:

  • 谢谢,我采用了这个方法。我可以看到你是日志包的维护者,这个模块有没有机会支持这样的一些特性(懒惰评估)?也许是像logger.debug('Message {}', F) 这样的风格,其中F 是像(expensive_func, [arg1, arg2]) 这样的元组。如果当前日志级别合适,expensive_func 将被日志模块调用,然后生成一些用于填充空白的字符串。
【解决方案2】:

正如 Vinay Sajip 建议的那样,您可以执行以下操作:

def foo():
    logger = logging.getLogger()
    if logger.isEnabledFor(logging.DEBUG):
        logger.debug('blah blah {}'.format(expensive_func()))
        logger.debug('Message: {}'.format(expf_1(expf_2(some_arg))))
        logger.debug('Message: {}', Lazy(expf_1, Lazy(expf_2, some_arg)))
foo()

已经很懒了!

那是因为 then 表达式

        logger.debug('blah blah {}'.format(expensive_func()))
        logger.debug('Message: {}'.format(expf_1(expf_2(some_arg))))
        logger.debug('Message: {}', Lazy(expf_1, Lazy(expf_2, some_arg)))

仅当且仅当logger.isEnabledFor(logging.DEBUG) 返回True 时才进行评估,即当且仅当需要评估


更多

logging.info(DeferredMessage(expensive_func, 1, 2))

并不像人们想象的那么懒惰DeferredMessage(expensive_func, 1, 2) 必须以一种渴望的方式进行评估。这比评估还慢:

    if logger.isEnabledFor(logging.DEBUG):

【讨论】:

【解决方案3】:

您可以使用stringlike 库为您的消息添加懒惰

例如:

logger.debug(
    'blah blah {value}'
    .format(
        value=LazyString(expensive_func)
    )
)

库链接:https://github.com/CovenantEyes/py_stringlike

【讨论】:

    猜你喜欢
    • 2014-09-07
    • 2014-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 2015-05-02
    • 1970-01-01
    相关资源
    最近更新 更多