В 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.
Вы также можете очень легко "свернуть свою" версию.
Надеюсь, что это поможет.
Ответ 7
В Java8 вам не нужно использовать isDebugEnabled()
для повышения производительности.
https://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#finer-java.util.function.Supplier-
import java.util.logging.Logger;
...
Logger.getLogger("hello").info(()->"Hello " + name);
Ответ 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()", их не нужно оценивать, где сохраняется производительность.