Logger slf4j: преимущества форматирования с помощью {} вместо конкатенации строк

100

Есть ли преимущество использования {}вместо конкатенации строк?

Пример из slf4j

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

вместо того

logger.debug("Temperature set to"+ t + ". Old temperature was " + oldT);

Я думаю, это об оптимизации скорости, потому что оценки параметров (и конкатенации строк) можно избежать во время выполнения в зависимости от файла конфигурации. Но возможны только два параметра, тогда иногда нет другого выбора, кроме конкатенации строк. Нужны мнения по этому поводу.

Эрнан Эче
источник

Ответы:

74

Речь идет о производительности конкатенации строк. Это потенциально важно, если у вас есть плотные записи в журнале.

(До SLF4J 1.7) Но возможны только два параметра

Поскольку подавляющее большинство операторов регистрации имеют 2 или меньше параметров, SLF4J API до версии 1.6 охватывает (только) большинство случаев использования. Разработчики API предоставили перегруженные методы с параметрами varargs, начиная с версии API 1.7.

В тех случаях, когда вам нужно больше двух и вы застряли на SLF4J до версии 1.7, просто используйте либо конкатенацию строк, либо new Object[] { param1, param2, param3, ... }. Их должно быть достаточно мало, чтобы производительность не так важна.

Скаффман
источник
2
Следует избегать объединения неиспользуемых строк (т.е. операторов отладки). Используйте либо (чрезмерно подробную, но эффективную) проверку на уровне журнала, либо параметр массива объектов (более тонкий, но, возможно, незначительные накладные расходы). (Я бы предпочел последнее при прочих равных.) Трудно сказать, что конкатенация строк не будет важна / не повлияет на производительность. Теоретически создание массива объектов может быть встроено и оптимизировано и «на самом деле» не имеет значения (по сравнению с принятием желаемого за действительное). (Это не преждевременная оптимизация, это просто вопрос сделать что-то правильно / лучше с первого раза.)
Майкл
почему перегруженные модификации не выполняются для System.out.println (), чтобы следовать аналогично регистратору slf4j, чтобы избежать конкатенации строк?
a3.14_Infinity
45

Краткая версия: да, это быстрее, с меньшим количеством кода!

Конкатенация строк выполняет много работы, не зная, нужна она или нет (традиционный тест «включена отладка», известный из log4j), и его следует по возможности избегать, поскольку {} позволяет отложить вызов toString () и построение строки. до после того, как было решено, нужно событие или нет. На мой взгляд, благодаря формату регистратора в виде одной строки код становится чище.

Вы можете указать любое количество аргументов. Обратите внимание, что если вы используете старую версию sljf4j и у вас более двух аргументов {}, вы должны new Object[]{a,b,c,d}вместо этого использовать синтаксис для передачи массива. См., Например, http://slf4j.org/apidocs/org/slf4j/Logger.html#debug(java.lang.String, java.lang.Object []) .

Что касается скорости: некоторое время назад Ceki разместил тест в одном из списков.

Торбьёрн Равн Андерсен
источник
6
Примечание: последние Javadoc показывает новый синтаксис вар-Arg, debug(String format, Object... arguments). См. Slf4j.org/faq.html#logging_performance
Майкл
Проголосовали за упоминание об оценке .toString () в дополнение к производительности конкатенации. Это то, что происходит внутри регистратора, и регистратор может решить, нужно ли вызывать этот метод. Это не так, если не соблюдена планка уровня ведения журнала.
Chetan Narsude
6

Поскольку String неизменяема в Java, левую и правую String необходимо скопировать в новую строку для каждой пары конкатенации. Итак, лучше выбрать заполнитель.

Рабин Панта
источник
2
Это правильно, если есть одна пара, но в целом неверно, поскольку компилятор превращает конкатенацию в вызовы построителя строк, что приводит к гораздо более быстрому коду, который не выполняет столько распределения.
cdeszaq
3

Другая альтернатива есть String.format(). Мы используем его в jcabi-log (статическая служебная оболочка вокруг slf4j).

Logger.debug(this, "some variable = %s", value);

Это намного удобнее в обслуживании и расширении. Кроме того, это легко перевести.

Егор256
источник
3
Не думаю, что ремонтопригоднее. если тип valueизменится, вам нужно будет вернуться и также изменить оператор журнала. То, с чем вам не помогут IDE. Регистраторы должны помогать с отладкой, а не мешать ей. :-)
Chetan Narsude
3
@ChetanNarsude IntelliJ 2016 по крайней мере сообщает мне, когда строка формата не соответствует аргументам форматирования. Например: String.format("%d", "Test")выдает предупреждение IntelliJ Argument type 'String' does not match the type of the format specifier '%d'.. Хотя я не уверен, что он по-прежнему сможет обеспечить такой разумный ответ при работе с вышеуказанным решением.
давка
какова скорость этого?
Торбьорн Равн Андерсен
@ ThorbjørnRavnAndersen он довольно примитивен внутри, но, конечно, медленнее, чем статический регистратор
yegor256
Переносить slf4j? разве это не противоречит цели использования slf4j? Кроме того, я видел, как многие люди неправильно используют String.format, так что строка форматируется до оценки уровня журнала, например: logger.info (String.format ("hello% s", username)).
Хуан Бустаманте
2

Я думаю, с точки зрения автора, основная причина - уменьшить накладные расходы на конкатенацию строк. Я только что прочитал документацию к регистратору, вы могли найти следующие слова:

/**
* <p>This form avoids superfluous string concatenation when the logger
* is disabled for the DEBUG level. However, this variant incurs the hidden
* (and relatively small) cost of creating an <code>Object[]</code> before 
  invoking the method,
* even if this logger is disabled for DEBUG. The variants taking
* {@link #debug(String, Object) one} and {@link #debug(String, Object, Object) two}
* arguments exist solely in order to avoid this hidden cost.</p>
*/
*
 * @param format    the format string
 * @param arguments a list of 3 or more arguments
 */
public void debug(String format, Object... arguments);
Тяньхао Ван
источник