1. ホーム
  2. python

ロガーメッセージ文字列の遅延評価

2023-10-19 01:46:37

質問

Pythonアプリケーションで標準のPythonロギングモジュールを使用しています。

インポートロギング
logging.basicConfig(レベル=logging.INFO)
logger = logging.getLogger("log")
while True:
  logger.debug('Stupid log message " + ' '.join([str(i) for i in range(20)]) )
  # 何かをする

問題は、デバッグレベルを有効にしていないにもかかわらず、ループの繰り返しごとにその愚かなログメッセージが評価され、パフォーマンスがひどく損なわれていることです。

これに対する解決策はあるのでしょうか?

C++では log4cxx パッケージがあり、このようなマクロが提供されています。

LOG4CXX_DEBUG(logger, messasage)

これは事実上、次のように評価されます。

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


しかし、Pythonにはマクロがないので(AFAIK)、ロギングを行うための効率的な方法はないでしょうか?

どのように解決するのですか?

loggingモジュールは、あなたがやりたいことをすでに部分的にサポートしています。 これを実行します。

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()はそこまで至りませんでした。