В log4j проверка производительности isDebugEnabled перед регистрацией улучшает производительность? - PullRequest
178 голосов
/ 08 июня 2009

Я использую Log4J в моем приложении для ведения журнала. Ранее я использовал вызов отладки, как:

Вариант 1:

logger.debug("some debug text");

но некоторые ссылки предполагают, что лучше сначала проверить isDebugEnabled(), например:

Вариант 2:

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text");
}

Итак, мой вопрос: " Улучшает ли вариант 2 производительность? ".

Потому что в любом случае каркас Log4J имеет ту же проверку для debugEnabled. Для варианта 2 может быть полезно, если мы используем несколько операторов отладки в одном методе или классе, где каркасу не нужно вызывать метод isDebugEnabled() несколько раз (при каждом вызове); в этом случае он вызывает метод isDebugEnabled() только один раз, а если Log4J настроен на уровень отладки, то фактически он вызывает метод isDebugEnabled() дважды:

  1. В случае присвоения значения переменной debugEnabled и
  2. На самом деле вызывается методом logger.debug ().

Я не думаю, что если мы напишем несколько операторов logger.debug() в методе или классе и вызовем метод debug() в соответствии с вариантом 1, тогда это издержки для инфраструктуры Log4J по сравнению с вариантом 2. Поскольку isDebugEnabled() является очень маленький метод (с точки зрения кода), он может быть хорошим кандидатом для встраивания.

Ответы [ 15 ]

218 голосов
/ 08 июня 2009

В данном конкретном случае вариант 1 лучше.

Защитный оператор (проверка isDebugEnabled()) предназначен для предотвращения потенциально дорогостоящих вычислений сообщения журнала, когда он включает в себя вызов методов toString() различных объектов и объединение результатов.

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

Еще лучше - использовать более современную структуру ведения журнала, где операторы журнала принимают спецификацию формата и список аргументов, которые должны быть заменены средством ведения журнала - но «лениво», только если средство ведения журнала включено. Это подход, принятый slf4j .

См. мой ответ на связанный вопрос для получения дополнительной информации и пример выполнения чего-то подобного с log4j.

30 голосов
/ 08 июня 2009

Так как в варианте 1 строка сообщения является константой, нет абсолютно никакой выгоды в переносе оператора регистрации с условием, наоборот, если оператор журнала разрешен, отладка будет выполняться дважды, один раз в isDebugEnabled() метод и один раз в debug() метод. Стоимость вызова isDebugEnabled() составляет порядка 5–30 наносекунд, что для большинства практических целей должно быть незначительным. Таким образом, вариант 2 нежелателен, потому что он загрязняет ваш код и не дает никакой другой выгоды.

17 голосов
/ 08 июня 2009

Использование isDebugEnabled() зарезервировано для случаев, когда вы создаете сообщения журнала путем объединения строк:

Var myVar = new MyVar();
log.debug("My var is " + myVar + ", value:" + myVar.someCall());

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

Лично я использую вызовы формата Java 1.5 в классе String следующим образом:

Var myVar = new MyVar();
log.debug(String.format("My var is '%s', value: '%s'", myVar, myVar.someCall()));

Я сомневаюсь, что есть много оптимизаций, но легче читать.

Обратите внимание, что большинство API-интерфейсов ведения журналов предлагают форматирование «из коробки»: например, slf4j предоставляет следующее:

logger.debug("My var is {}", myVar);

, который даже легче читать.

8 голосов
/ 25 мая 2011

Короткая версия: Вы также можете выполнить логическую проверку isDebugEnabled ().

Причины:
1- Если сложная логика / строка конкат. добавлен в ваше заявление отладки, вы уже будете иметь проверку на месте.
2. Вам не нужно выборочно включать оператор в «сложные» операторы отладки. Все заявления включены таким образом.
3- Вызов log.debug выполняет перед регистрацией следующее:

if(repository.isDisabled(Level.DEBUG_INT))<br/> return;

Это в основном то же самое, что журнал вызовов. или кот. isDebugEnabled ().

ОДНАКО! Это то, что думают разработчики log4j (как и в их javadoc, и вы, вероятно, должны пойти по этому пути.)

Это метод

public
  boolean isDebugEnabled() {
     if(repository.isDisabled( Level.DEBUG_INT))
      return false;
    return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel());
  }

Это Javadoc для него

<code>/**
*  Check whether this category is enabled for the <code>DEBUG</code>
*  Level.
*
*  <p> This function is intended to lessen the computational cost of
*  disabled log debug statements.
*
*  <p> For some <code>cat</code> Category object, when you write,
*  <pre>
*      cat.debug("This is entry number: " + i );
*  
* *

Вы несете расходы на построение сообщения, объединение в * этот случай, независимо от того, зарегистрировано сообщение или нет. * *

Если вас беспокоит скорость, то вам следует написать *

*    if(cat.isDebugEnabled()) {
*      cat.debug("This is entry number: " + i );
*    }
*  
* *

Таким образом, вы не будете нести стоимость параметра * конструкция, если отладка отключена для cat. На * с другой стороны, если cat включена отладка, вы * понесет затраты на оценку отладки категории * включен дважды. Один раз в isDebugEnabled и один раз в debug. Это незначительные накладные расходы * так как оценка категории занимает около 1 %% времени * требуется, чтобы на самом деле войти. * * @return boolean - true если эта категория отлажена * включено, false в противном случае. * * /

7 голосов
/ 11 сентября 2015

В Java 8 вам не нужно использовать isDebugEnabled() для повышения производительности.

https://logging.apache.org/log4j/2.0/manual/api.html#Java_8_lambda_support_for_lazy_logging

import java.util.logging.Logger;
...
Logger.getLogger("hello").info(() -> "Hello " + name);
6 голосов
/ 09 июня 2009

Как уже упоминалось, использование оператора guard полезно только в том случае, если создание строки требует много времени. Конкретные примеры этого - при создании строки будет запускаться ленивая загрузка.

Стоит отметить, что этой проблемы можно избежать, используя Simple Logging Facade для Java или (SLF4J) - http://www.slf4j.org/manual.html. Это позволяет вызывать методы, такие как:

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

Это будет преобразовывать только переданные параметры в строки, если включена отладка. SLF4J, как следует из его названия, является лишь фасадом, и вызовы регистрации могут передаваться в log4j.

Вы также можете очень легко "свернуть свою" версию этого.

Надеюсь, это поможет.

6 голосов
/ 08 июня 2009

Вариант 2 лучше.

По сути, это не улучшает производительность. Но это гарантирует, что производительность не ухудшается. Вот как.

Обычно мы ожидаем logger.debug (SomeString);

Но, как правило, по мере того, как приложение растет, многие руки переходят, особенно начинающие разработчики,

logger.debug (str1 + str2 + str3 + str4);

и т. П.

Даже если для уровня журнала задано значение ERROR или FATAL, конкатенация строк действительно происходит! Если приложение содержит много сообщений уровня DEBUG с конкатенацией строк, то это, безусловно, требует снижения производительности, особенно с jdk 1.4 или ниже. (Я не уверен, что в более поздних версиях jdk internall есть какой-либо stringbuffer.append ()).

Вот почему Вариант 2 безопасен. Даже конкатенации строк не происходит.

3 голосов
/ 08 июня 2009

Как @erickson, это зависит. Насколько я помню, isDebugEnabled уже встроен в метод debug() Log4j.
Пока вы не выполняете дорогостоящих вычислений в своих операторах отладки, таких как циклы над объектами, выполняете вычисления и объединяете строки, на мой взгляд, все в порядке.

StringBuilder buffer = new StringBuilder();
for(Object o : myHugeCollection){
  buffer.append(o.getName()).append(":");
  buffer.append(o.getResultFromExpensiveComputation()).append(",");
}
log.debug(buffer.toString());

будет лучше как

if (log.isDebugEnabled(){
  StringBuilder buffer = new StringBuilder();
  for(Object o : myHugeCollection){
    buffer.append(o.getName()).append(":");
    buffer.append(o.getResultFromExpensiveComputation()).append(",");
  }
  log.debug(buffer.toString());
}
2 голосов
/ 23 марта 2018

Поскольку многие люди, вероятно, просматривают этот ответ при поиске log4j2, и почти все текущие ответы не учитывают log4j2 или недавние изменения в нем, следует надеяться, что это ответ на вопрос.

log4j2 поддерживает Supplier s (в настоящее время их собственная реализация, но согласно документации планируется использовать интерфейс поставщика Java в версии 3.0). Вы можете прочитать немного больше об этом в руководстве . Это позволяет помещать дорогостоящее создание сообщения журнала в поставщика, который создает сообщение, только если оно будет зарегистрировано:

LogManager.getLogger().debug(() -> createExpensiveLogMessage());
2 голосов
/ 14 октября 2014

Для однострочной я использую троицу внутри сообщения регистрации, таким образом я не делаю конкатенацию:

EJ:

logger.debug(str1 + str2 + str3 + str4);

Я делаю:

logger.debug(logger.isDebugEnable()?str1 + str2 + str3 + str4:null);

Но для несколько строк кода

е ^.

for(Message mess:list) {
    logger.debug("mess:" + mess.getText());
}

Я делаю:

if(logger.isDebugEnable()) {
    for(Message mess:list) {
         logger.debug("mess:" + mess.getText());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...