简体   繁体   English

Python使用参数中的函数记录开销

[英]Python Logging overhead with function in argument

I am using python logging module (python 3.4.3). 我正在使用python日志模块(python 3.4.3)。 Let's say my logging level is set to INFO and I would like to use a function as an argument of my logging message. 假设我的日志记录级别设置为INFO,我想使用函数作为我的日志消息的参数。

Now I would expect when log level is decreased (eg WARNING) that the argument functions are not executed. 现在我希望当日志级别降低(例如警告)时,不执行参数函数。

Is this true? 这是真的?

Example (console): 示例(控制台):

>>> import math
>>> import logging
>>> logging.basicConfig(level=logging.INFO)
>>> logging.debug('Factorial of 100000=',math.factorial(100000))
>>> */empty output but slightly delayed, small CPU peak/*
>>> logging.debug('Factorial of 10=',math.factorial(10))
>>> */empty output but instant, no noticeable CPU usage/*

The function is probably not executed, but still there is noticable delay. 该功能可能未执行,但仍有明显的延迟。 Could someone explain me please the possible cause? 有人可以解释我可能的原因吗?

Function arguments evaluations before call 调用前的函数参数评估

Well you see the arguments you pass to logging.debug('Factorial of 100000=', math.factorial(100000)) need to be evaluated before logging.debug() gets called , and evaluating math.factorial(100000) takes time 那么你看到传递给logging.debug('Factorial of 100000=', math.factorial(100000))的参数logging.debug('Factorial of 100000=', math.factorial(100000))需要在调用 logging.debug()之前进行评估 ,并且评估math.factorial(100000)需要时间

It doesn't matter if ultimately the function does nothing in its call, the arguments will still get evaluated 如果函数最终在调用中没有做任何事情都没关系,那么参数仍然会得到评估

Conceptually you can think of it as doing 从概念上讲,你可以把它想象成一样

value = math.factorial(100000)           # This gets evaluated
logging.debug('Factorial is %d', value)  # Even if this is a "no op" call, 
                                         # the arg evaluation still takes place

Workaround 解决方法

import math
import logging

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

if logger.getEffectiveLevel() <= logging.DEBUG:    # Or use == if you want DEBUG only
    logger.debug('Factorial is %d', math.factorial(100000))

Using Python 3's logger.getEffectiveLevel() 使用Python 3的logger.getEffectiveLevel()

Given that the levels are ordered as 鉴于水平被命令为

CRITICAL    50
ERROR       40
WARNING     30
INFO        20
DEBUG       10
NOTSET      0

You can see that our if block (with <= logging.DEBUG ) will only run on DEBUG and below (so DEBUG and NOTSET ) 你可以看到我们的if块(带<= logging.DEBUG )只能在DEBUG及以下运行(所以DEBUGNOTSET

The problem is that you evaluate the function before you call. 问题是你在打电话之前评估这个功能。 One way of dealing with this is using logger.getEffectiveLevel() as described by bakkal. 解决这个问题的一种方法是使用logger.getEffectiveLevel()描述的logger.getEffectiveLevel logger.getEffectiveLevel() This can be a bit verbose though. 但这可能有点冗长。 Another way to get around this is to create an intermediary object to pass to the logger that does the work in it's __str__ function. 解决此问题的另一种方法是创建一个中间对象,以传递给在其__str__函数中执行工作的记录器。 This delays the function call until the logging module is sure the message will be emitted. 这会延迟函数调用,直到日志记录模块确定将发出消息。

For example: 例如:

import logging
import math

class CallableMsg:
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return str(self.func(*self.args, **self.kwargs))

logging.basicConfig(level=logging.INFO)

# single arg log
logging.info(CallableMsg(math.factorial, 10)) 
# using the CallableMsg in a format string
logging.info("10! = %s", CallableMsg(math.factorial, 10))
# factorial function never gets evaluated for the debug call
logging.debug(CallableMsg(math.factorial, 1000000))

The logging module is also kind enough to preserve the line on which the log call was actually made if there is ever an error. 如果出现错误,日志记录模块也足以保留实际进行日志调用的行。

logging.info(CallableMsg(math.factorial, -1))

Outputs: 输出:

--- Logging error ---
Traceback (most recent call last):
  File "C:\Python34\lib\logging\__init__.py", line 971, in emit
    msg = self.format(record)
  File "C:\Python34\lib\logging\__init__.py", line 821, in format
    return fmt.format(record)
  File "C:\Python34\lib\logging\__init__.py", line 558, in format
    record.message = record.getMessage()
  File "C:\Python34\lib\logging\__init__.py", line 319, in getMessage
    msg = str(self.msg)
  File "C:python\run.py", line 12, in __str__
    return str(self.func(*self.args, **self.kwargs))
ValueError: factorial() not defined for negative values
Call stack:
  File "<string>", line 1, in <module>
  File "C:\Python34\lib\idlelib\run.py", line 126, in main
    ret = method(*args, **kwargs)
  File "C:\Python34\lib\idlelib\run.py", line 353, in runcode
    exec(code, self.locals)
  File "C:\python\run.py", line 18, in <module>
    logging.info(CallableMsg(math.factorial, -1))
Message: <__main__.CallableMsg object at 0x02ECF970>
Arguments: ()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM