В log4j ли проверка isDebugEnabled перед протоколированием повышает производительность?

Я использую 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():

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

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

Ответ 1

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

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

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

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

См. мой ответ на соответствующий вопрос для получения дополнительной информации и пример того, как делать это с помощью log4j.

Ответ 2

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

Ответ 3

Использование 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);

что еще проще читать.

Ответ 4

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

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

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

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

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

и т.п.

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

Вот почему Вариант 2 безопасен. Даже конкатенации строк не выполняются.

Ответ 5

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

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

if(repository.isDisabled(Level.DEBUG_INT))
return;

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

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

Это метод

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

Это javadoc для этого

/**
*  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 );
*  </pre>
*
*  <p>You incur the cost constructing the message, concatenatiion in
*  this case, regardless of whether the message is logged or not.
*
*  <p>If you are worried about speed, then you should write
*  <pre>
*    if(cat.isDebugEnabled()) {
*      cat.debug("This is entry number: " + i );
*    }
*  </pre>
*
*  <p>This way you will not incur the cost of parameter
*  construction if debugging is disabled for <code>cat</code>. On
*  the other hand, if the <code>cat</code> is debug enabled, you
*  will incur the cost of evaluating whether the category is debug
*  enabled twice. Once in <code>isDebugEnabled</code> and once in
*  the <code>debug</code>.  This is an insignificant overhead
*  since evaluating a category takes about 1%% of the time it
*  takes to actually log.
*
*  @return boolean - <code>true</code> if this category is debug
*  enabled, <code>false</code> otherwise.
*   */

Ответ 6

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

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

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

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

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

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

Ответ 8

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

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

Ответ 9

Как и у @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());
}

Ответ 10

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

EJ:

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

Я делаю:

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

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

EJ.

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

Я делаю:

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

Ответ 11

Если вы используете опцию 2, вы выполняете булевскую проверку, которая быстрая. В первом варианте вы выполняете вызов метода (толкаете материал в стеке), а затем выполняете булевскую проверку, которая все еще выполняется быстро. Проблема, которую я вижу, - это последовательность. Если некоторые из ваших отладочных и инфо-операторов завернуты, а некоторые - это не согласованный стиль кода. Кроме того, кто-то позже мог изменить оператор отладки, чтобы включить строки конкатенации, что по-прежнему довольно быстро. Я обнаружил, что когда мы завернули отладку и информацию в большом приложении и профилировали ее, мы сохранили пару процентных пунктов в производительности. Не много, но достаточно, чтобы сделать его достойным работы. У меня теперь есть пара настроек макросов в IntelliJ, чтобы автоматически генерировать завершенные отладочные и информационные инструкции для меня.

Ответ 12

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

Случай 1: log.debug( "одна строка" )

Вариант 2: log.debug( "одна строка" + "две строки" + object.toString + object2.toString)

В то время, когда они вызываются, строка параметров в log.debug(будь то CASE 1 или Case2) должна быть оценена. Это то, что все означает "дорого". Если у вас есть условие перед этим, "isDebugEnabled()", их не нужно оценивать, где сохраняется производительность.