Оценка строки ленивого журнала

Я использую стандартный модуль регистрации python в моем приложении python:

 регистрация импорта
 logging.basicConfig (уровень = logging.INFO)
 logger = logging.getLogger ("log")
 в то время как True:
   logger.debug ('Stupid log message "+' '.join ([str (i) для i в диапазоне (20)]))
   # Сделай что-нибудь

Проблема в том, что хотя уровень отладки не включен, это глупое сообщение журнала оценивается на каждой итерации цикла, что плохо влияет на производительность.

Есть ли решение для этого?

В C ++ у нас есть пакет log4cxx , который предоставляет макросы следующим образом:
LOG4CXX_DEBUG(logger, messasage)
Это эффективно оценивает

 if (log4cxx :: debugEnabled (logger)) {
     log4cxx.log (logger, log4cxx :: LOG4CXX_DEBUG, сообщение)
 }

Но так как макросов в Python (AFAIK) нет, если есть эффективный способ ведения журнала?

4 Solutions collect form web for “Оценка строки ленивого журнала”

Модуль регистрации уже частично поддерживает то, что вы хотите сделать. Сделай это:

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

… вместо этого:

 log.debug("Some message: a=%sb=%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 () не дошел так далеко.

Конечно, следующее не так эффективно, как макрос:

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

но простой, оценивается ленивым образом и в 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 для моей настройки.

 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 секунд. Это показывает, что аргумент не оценивается, когда уровень ведения журнала ниже установленного уровня.

Как указывает Шейн, используя

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

… вместо этого:

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

экономит некоторое время, только выполнив форматирование строки, если сообщение действительно зарегистрировано.

Однако это не полностью решает проблему, поскольку вам, возможно, придется предварительно обработать значения для форматирования в строке, такие как:

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

В этом случае obj.get_a() и obj.get_b() будут вычисляться, даже если никаких протоколов не происходит.

Решением этого будет использование лямбда-функций, но для этого требуется несколько дополнительных механизмов:

 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=%sb=%s" % (foo.get_a(), foo.get_b())) 

В этом случае функция лямбда log.debug только в том случае, если log.debug решает выполнить форматирование, поэтому вызов метода __str__ .

Имейте в виду: накладные расходы этого решения могут очень сильно превысить выгоду 🙂 Но, по крайней мере теоретически, это позволяет делать совершенно ленивые каротажи.

Python - лучший язык программирования в мире.