Logger slf4j zalety formatowania z {} zamiast konkatenacji ciągów

100

Czy jest jakaś zaleta używania {}zamiast konkatenacji ciągów?

Przykład z slf4j

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

zamiast

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

Myślę, że chodzi o optymalizację prędkości, ponieważ oceny parametrów (i konkatenacji ciągów) można uniknąć w czasie wykonywania, w zależności od pliku konfiguracyjnego. Ale możliwe są tylko dwa parametry, a czasami nie ma innego wyboru niż konkatenacja ciągów. Potrzeba opinii na ten temat.

Hernán Eche
źródło

Odpowiedzi:

74

To jest o wydajności konkatenacji. Jest to potencjalnie istotne, jeśli masz gęste instrukcje logowania.

(Przed SLF4J 1.7) Ale możliwe są tylko dwa parametry

Ponieważ zdecydowana większość instrukcji logowania ma 2 lub mniej parametrów, interfejs API SLF4J do wersji 1.6 obejmuje (tylko) większość przypadków użycia. Projektanci API udostępnili przeciążone metody z parametrami varargs od wersji API 1.7.

W przypadkach, w których potrzebujesz więcej niż 2 i utknąłeś z SLF4J starszym niż 1.7, po prostu użyj połączenia ciągów lub new Object[] { param1, param2, param3, ... }. Powinno ich być wystarczająco mało, aby wydajność nie była tak ważna.

skaffman
źródło
2
Należy unikać nieużywanych konkatenacji ciągów (tj. Instrukcji debugowania). Używaj albo (nadmiernie rozwlekłego, ale wydajnego) sprawdzania poziomu dziennika lub (cieńszego, ale być może mniejszego narzutu) parametru tablicy obiektów. (Wolałbym to drugie, przy czym wszystkie rzeczy są równe). Trudno powiedzieć, że konkatacja ciągów nie będzie ważna / nie wpłynie na wydajność. Teoretycznie tworzenie tablicy obiektów mogłoby być wstawione i zoptymalizowane i „naprawdę” nie robi różnicy (w porównaniu z myśleniem życzeniowym). (To nie jest przedwczesna optymalizacja, to po prostu kwestia zrobienia czegoś dobrze / lepszego za pierwszym razem.)
michael
dlaczego przeciążone modyfikacje nie są wykonywane, aby System.out.println () postępował podobnie jak rejestrator slf4j, aby uniknąć łączenia ciągów?
a3.14_Infinity
45

Krótka wersja: Tak, jest szybsza, z mniejszą ilością kodu!

Łączenie ciągów wykonuje dużo pracy bez wiedzy, czy jest potrzebne, czy nie (tradycyjny test „obsługuje debugowanie” znany z log4j) i należy go unikać, jeśli to możliwe, ponieważ {} umożliwia opóźnienie wywołania toString () i konstrukcji ciągu po ustaleniu, czy wydarzenie wymaga nagrania, czy nie. Dzięki formatowaniu pliku rejestrującego pojedynczy plik ciąg, kod staje się moim zdaniem czystszy.

Możesz podać dowolną liczbę argumentów. Zauważ, że jeśli używasz starej wersji sljf4j i masz więcej niż dwa argumenty {}, musisz użyć new Object[]{a,b,c,d}składni, aby zamiast tego przekazać tablicę. Zobacz np. Http://slf4j.org/apidocs/org/slf4j/Logger.html#debug(java.lang.String, java.lang.Object []) .

Odnośnie prędkości: Ceki jakiś czas temu opublikował benchmark na jednej z list.

Thorbjørn Ravn Andersen
źródło
6
Uwaga: najnowsze javadoc pokazuje nowsze składnia var-Arg debug(String format, Object... arguments). Zobacz slf4j.org/faq.html#logging_performance
michael
Głos za pozytywnymi z powodu wzmianki o ocenie .toString () oprócz wydajności konkatenacji. Jest to coś, co dzieje się wewnątrz programu rejestrującego, a program rejestrujący może zdecydować, czy konieczne jest wywołanie tej metody. Nie dzieje się, jeśli pasek poziomu logowania nie jest spełniony.
Chetan Narsude
6

Ponieważ String jest niezmienny w Javie, więc lewy i prawy ciąg muszą zostać skopiowane do nowego ciągu dla każdej pary konkatenacji. Więc lepiej wybierz symbol zastępczy.

Rabin Pantha
źródło
2
Jest to poprawne, jeśli istnieje pojedyncza para, ale generalnie niepoprawne, ponieważ kompilator zamienia konkatenację w wywołania konstruktora ciągów, co powoduje znacznie szybszy kod, który nie wykonuje tak dużej alokacji.
cdeszaq
3

Inną alternatywą jest String.format(). Używamy go w jcabi-log (statyczne opakowanie narzędziowe wokół slf4j).

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

Jest znacznie łatwiejszy w utrzymaniu i rozbudowie. Poza tym łatwo to przetłumaczyć.

yegor256
źródło
3
Nie sądzę, że jest to łatwiejsze do utrzymania. jeśli rodzaj valuezmian, musisz cofnąć się i zmienić również instrukcję logowania. Coś, w czym IDE nie pomogą. Rejestratory powinny pomagać w debugowaniu i nie przeszkadzać. :-)
Chetan Narsude
3
@ChetanNarsude IntelliJ 2016 przynajmniej mówi mi, kiedy ciąg formatu nie pasuje do argumentów formatujących. Na przykład: String.format("%d", "Test")generuje ostrzeżenie IntelliJ Argument type 'String' does not match the type of the format specifier '%d'.. Chociaż nie jestem pewien, czy nadal byłby w stanie zapewnić tę inteligentną odpowiedź podczas pracy z powyższym rozwiązaniem.
zmiażdżyć
jaka jest prędkość tego?
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen jest dość prymitywny w środku, ale oczywiście jest wolniejszy niż statyczny rejestrator
yegor256
Zawijasz slf4j? czy to nie jest sprzeczne z celem używania slf4j? Ponadto widziałem wiele osób nadużywających String.format w taki sposób, że ciąg jest formatowany przed oceną poziomu dziennika, na przykład: logger.info (String.format ("hello% s", nazwa użytkownika)).
Juan Bustamante
2

Myślę, że z punktu widzenia autora głównym powodem jest zmniejszenie narzutu związanego z konkatenacją ciągów, po prostu przeczytałem dokumentację rejestratora, możesz znaleźć następujące słowa:

/**
* <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);
Tianhao Wang
źródło