#java #logging #conditional-compilation #overhead
#java #ведение журнала #условная компиляция #накладные расходы
Вопрос:
Я немного прочитал о различных способах регистрации отладочного сообщения с помощью Java, и, исходя из опыта работы с C, меня беспокоит следующее :
Эти библиотеки требуют минимальных накладных расходов в случае, если ведение журнала отключено (например, в производственной среде), но поскольку аргументы их log()
функции все еще вычисляются, меня беспокоит то, что накладные расходы в реальном сценарии, на самом деле, совсем не будут незначительными.
Например, у a log(myobject.toString(), "info message")
все еще есть накладные расходы на оценку myobject.toString()
, которые могут быть довольно большими, даже если сама функция log ничего не делает.
У кого — нибудь есть решение этой проблемы?
PS: для тех, кому интересно, почему я упомянул фон C: C позволяет использовать макрос препроцессора и инструкции времени компиляции, которые полностью удаляют весь код, связанный с отладкой во время компиляции, включая параметры макросов (которые просто не будут отображаться вообще).
РЕДАКТИРОВАТЬ : После прочтения первой партии ответов кажется, что в java явно нет ничего, что помогло бы (подумайте о регистрации косинуса числа в большом цикле в мобильной среде, где важен каждый бит процессора).). Поэтому я добавлю, что я бы даже выбрал решение на основе IDE. Моим последним средством является создание чего-то вроде макроса «найти все / заменить». Сначала я подумал, что, возможно, что-то, взятое из аспектно-ориентированного фреймворка, поможет… Кто-нибудь?
Ответ №1:
Я думаю, что часто задаваемые вопросы log4j хорошо справляются с решением этой проблемы:
Для некоторого регистратора l, пишущего,
l.debug("Entry number: " i " is " String.valueOf(entry[i]));
влечет за собой затраты на создание параметра сообщения, то есть преобразование как целого числа i, так и записи [i] в строку и объединение промежуточных строк. Это независимо от того, будет ли сообщение зарегистрировано или нет.
Если вы беспокоитесь о скорости, тогда напишите
if(l.isDebugEnabled()) { l.debug("Entry number: " i " is " String.valueOf(entry[i])); }
Таким образом, вы не понесете затрат на построение параметров, если отладка отключена для logger l. С другой стороны, если у регистратора включена функция отладки, вы понесете расходы на оценку того, включен регистратор или нет, дважды: один раз в debugEnabled и один раз в debug. Это незначительные накладные расходы, поскольку оценка регистратора занимает менее 1% времени, необходимого для фактического протоколирования инструкции.
Использование защитного предложения — это общий подход, позволяющий избежать здесь построения строк.
Другие популярные фреймворки, такие как slf4j, используют подход использования форматированных строк / параметризованных сообщений, чтобы сообщение не удалялось без необходимости.
Комментарии:
1. 1 за последнюю строку. Параметризованные аргументы — это всегда вариант.
2. @Тим Бендер, при условии, что у вас нет никаких примитивов.
3. Спасибо за ответ. Параметризованные сообщения мне не подходят, потому что иногда вам нужно создавать сложные выражения (например, вычисления), и я действительно не хочу выполнять их в производственной среде. Я немного подожду, чтобы посмотреть, не предложит ли кто-нибудь волшебное решение, потому что я все еще нахожу невероятным, что у java нет способа сделать это вообще без накладных расходов…
4. Как это можно было бы сделать без каких-либо накладных расходов в любой среде программирования? Выражение / переменная должны быть разрешены для передачи функции. Вы могли бы передать указатель на функцию, который кажется слишком сложным. В Java вы могли бы выполнить это, передав анонимную функцию, которая расширяет Object, где
toString()
возвращает выражение, которое вы хотели бы (условно) зарегистрировать, но это сделало бы ваш код еще более уродливым.5. взгляните на препроцессор C и макросы, вы будете поражены. Кроме того, посмотрите на мой комментарий к редактированию.
Ответ №2:
Современные фреймворки ведения журнала имеют переменную замену. Затем ваше ведение журнала выглядит примерно так:
log.debug("The value of my first object is %s and of my second object is %s", firstObject, secondObject).
toString() данных объектов будет выполняться только тогда, когда для logging установлено значение debug. В противном случае он просто проигнорирует параметры и вернет.
Ответ №3:
Ответ довольно прост: не вызывайте дорогостоящие методы в самом вызове журнала. Кроме того, используйте охрану вокруг входа в систему вызова, если вы не можете этого избежать.
if(logger.isDebugEnabled()) {
logger.debug("this is an " expensive() " log call.");
}
И, как указывали другие, если у вас есть форматирование, доступное в вашей платформе ведения журнала (т. Е. Если вы используете достаточно современное для его поддержки, что должно быть во всех них, но не является), вы должны полагаться на это, чтобы помочь покрыть расходы на этапе ведения журнала. Если выбранный вами фреймворк уже не поддерживает форматирование, то либо переключитесь, либо напишите свою собственную оболочку.
Комментарии:
1. Я считаю, что это обходной путь, который портит мой код: почему я должен добавлять 3 строки кода для тривиального оператора log?
2. Тогда не использовать его? Я имею в виду, эй, вы сами выбираете, какую платформу ведения журнала использовать и как; я сам не использую инструкции guard, потому что я использую оболочку для ведения журнала, если базовая структура не позволяет выполнять отложенную оценку инструкций форматирования.
3. Извините, если вы посчитали, что это была атака, я не хотел быть таковым. Я просто хотел добавить этот комментарий, чтобы было известно, что это не всеми любимый подход 😉
4. Конечно, NP. Я не чувствовал себя атакованным; я просто повторял линию участника (как написано Ceki Gulcu) о защитных предложениях. Я не очень уважаю большинство фреймворков ведения журнала, поскольку они действительно довольно негибкие, и забавно, что Ceki — хороший парень, кстати — сейчас предпринимает свою четвертую попытку написать всеобъемлющий фреймворк ведения журнала.
5. Опустите фигурные скобки, и 3 строки станут одной. Привяжите ярлык / шаблон в вашей IDE для автоматической печати защитного предложения и запустите сообщение журнала с именем текущего метода, а затем это займет всего несколько нажатий клавиш.
Ответ №4:
Вы правы, оценка аргументов для log()
вызова может увеличить накладные расходы, которые не нужны и могут быть дорогостоящими.
Вот почему большинство разумных фреймворков ведения журнала также предоставляют некоторые функции форматирования строк, чтобы вы могли писать подобные вещи:
log.debug("Frobnicating {0}", objectWithExpensiveToString);
Таким образом, ваши единственные накладные расходы — это вызов debug()
. Если этот уровень деактивирован, то больше ничего не делается, а если он активирован, то интерпретируется строка формата, вызывается toString()
on objectWithExpensiveToString()
и результат вставляется в строку формата до того, как он будет зарегистрирован.
Некоторые инструкции журнала используют заполнители MessageFormat
стиля ( {0}
), другие используют заполнители format()
стиля ( %s
), а третьи могут использовать третий подход.
Ответ №5:
Вы можете использовать забавный способ — правда, немного подробный — с утверждениями. При включенных утверждениях будут выходные данные и накладные расходы, а при отключенных утверждениях выходных данных не будет и абсолютно никаких накладных расходов.
public static void main(String[] args) {
assert returnsTrue(new Runnable() {
@Override
public void run() {
// your logging code
}
});
}
public static boolean returnsTrue(Runnable r) {
r.run();
return true;
}
Здесь необходима функция returnsTrue(), потому что я не знаю лучшего способа заставить выражение возвращать значение true, а для утверждения требуется логическое значение.
Комментарии:
1.
assert
не нуждается в круглых скобках, и я бы сказал, что без этого чище. Потому что таким образом это выглядит как вызов метода, которым это не является.