惰性记录器消息字符串评估
我在 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(记录器,消息)
这实际上等于
if (log4cxx::debugEnabled(logger)) { log4cxx.log(logger,log4cxx::LOG4CXX_DEBUG, message) }
但是由于Python中没有宏(据我所知),是否有一种有效的方法来进行日志记录?
I'm using standard python logging module in my python application:
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
The issue is that although debug level is not enable, that stupid log message is evaluated on each loop iteration, which harms performance badly.
Is there any solution for this?
In C++ we have log4cxx
package that provides macros like this:LOG4CXX_DEBUG(logger, messasage)
That effectively evaluates to
if (log4cxx::debugEnabled(logger)) { log4cxx.log(logger,log4cxx::LOG4CXX_DEBUG, message) }
But since there are no macros in Python (AFAIK), if there a efficient way to do logging?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
日志记录模块已经部分支持您想要做的事情。这样做:
...而不是这样:
日志记录模块足够聪明,不会生成完整的日志消息,除非该消息实际上被记录在某处。
要将此功能应用于您的特定请求,您可以创建一个惰性连接类。
像这样使用它(注意生成器表达式的使用,增加了懒惰):
这是一个演示,展示了它的工作原理。
在演示中,logger.info() 调用遇到了断言错误,而 logger.debug() 则没有达到这一点。
The logging module already has partial support for what you want to do. Do this:
... instead of this:
The logging module is smart enough to not produce the complete log message unless the message actually gets logged somewhere.
To apply this feature to your specific request, you could create a lazyjoin class.
Use it like this (note the use of a generator expression, adding to the laziness):
Here is a demo that shows this works.
In the demo, The logger.info() call hit the assertion error, while logger.debug() did not get that far.
当然,以下内容不如宏高效:
但很简单,以惰性方式求值并且是4倍比接受的答案更快:
请参阅 benchmark-src 了解我的设置。
Of course the following is not as efficient as a Macro:
but simple, evaluates in lazy fashion and is 4 times faster than the accepted answer:
See benchmark-src for my setup.
如果运行该脚本,您会注意到第一个
logger.debug
命令不需要 20 秒即可执行。这表明当日志记录级别低于设置级别时,不会评估参数。If you run the script, you'll notice the first
logger.debug
command does not take 20 seconds to execute. This shows the argument is not evaluated when the logging level is below the set level.正如 Shane 指出的,使用
... 而不是这样:
仅在实际记录消息时才执行字符串格式化,从而节省一些时间。
但这并不能完全解决问题,因为您可能必须预处理这些值以格式化为字符串,例如:
在这种情况下,
obj.get_a()
和obj即使没有发生日志记录,.get_b()
也会被计算。解决方案是使用 lambda 函数,但这需要一些额外的机制:
...然后您可以使用以下内容进行日志记录:
在这种情况下,lambda 函数将仅在以下情况下被调用:
log.debug
决定执行格式化,因此调用__str__
方法。请注意:该解决方案的开销很可能超过收益:-) 但至少在理论上,它使得完全惰性日志记录成为可能。
As Shane points out, using
... instead of this:
saves some time by only performing the string formatting if the message is actually logged.
This does not completely solve the problem, though, as you may have to pre-process the values to format into the string, such as:
In that case,
obj.get_a()
andobj.get_b()
will be computed even if no logging happens.A solution to that would be to use lambda functions, but this requires some extra machinery:
... then you can log with the following:
In that case, the lambda function will only be called if
log.debug
decides to perform the formatting, hence calling the__str__
method.Mind you: the overhead of that solution may very well exceed the benefit :-) But at least in theory, it makes it possible to do perfectly lazy logging.
我提出,
Lazyfy
:用法:
原始示例:
如您所见,这也涵盖了使用 lambda 函数的其他答案,但使用
value
属性和扩展使用更多内存。但是,它可以通过以下方式节省更多内存: Usage of __slots__?最后,到目前为止,最有效的解决方案仍然是正如另一个答案所建议的那样:
I present,
Lazyfy
:Usage:
The original example:
As you see, this also covers the other answer which uses lambda function, but uses more memory with the
value
atribute and expansion. However, it saves more memory with: Usage of __slots__?Finally, by far, the most efficient solution still being the following as suggested another answer:
如果您仅依赖于访问全局状态属性,则可以实例化一个 python 类并使用
__str__
方法来惰性化它:相关:
If you depend only on accessing global state attributes, you can instantiate a python class and lazify it by using the
__str__
method:Related: