Vantagens do logger slf4j de formatação com {} em vez de concatenação de string

100

Existe alguma vantagem de usar em {}vez da concatenação de string?

Um exemplo de slf4j

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

ao invés de

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

Acho que se trata de otimização de velocidade, porque a avaliação de parâmetros (e concatenação de string) pode ser evitada em tempo de execução, dependendo de um arquivo de configuração. Mas apenas dois parâmetros são possíveis, então às vezes não há outra escolha a não ser a concatenação de string. Precisando de opiniões sobre este assunto.

Hernán Eche
fonte

Respostas:

74

Ele é sobre o desempenho de concatenação. É potencialmente significativo se você tiver instruções de registro densas.

(Antes de SLF4J 1.7) Mas apenas dois parâmetros são possíveis

Como a grande maioria das instruções de registro têm 2 ou menos parâmetros, a API SLF4J até a versão 1.6 cobre (apenas) a maioria dos casos de uso. Os designers da API forneceram métodos sobrecarregados com parâmetros varargs desde a versão 1.7 da API.

Para aqueles casos em que você precisa de mais de 2 e está preso ao SLF4J pré-1.7, use a concatenação de string ou new Object[] { param1, param2, param3, ... }. Deve haver poucos deles para que o desempenho não seja tão importante.

skaffman
fonte
2
Deve-se evitar a concatenação de strings não utilizadas (ou seja, instruções de depuração). Use a verificação de nível de log (excessivamente prolixo, mas eficiente) ou o parâmetro de matriz de objeto (menor, mas talvez menor sobrecarga). (Eu prefiro o último, todas as coisas sendo iguais.) É difícil dizer que o concat de string não será importante / não afetará o desempenho. A criação do array de objetos poderia, em teoria, ser embutida e otimizada e "realmente" não fazer diferença (vs. pensamento positivo). (Não é uma otimização prematura, é simplesmente uma questão de fazer algo certo / melhor da primeira vez.)
Michael
por que modificações sobrecarregadas não são feitas para System.out.println () seguir de forma semelhante ao logger do slf4j, de modo que evita a concatenação de strings?
a3.14_Infinity
44

Versão resumida: Sim, é mais rápido, com menos código!

A concatenação de string faz muito trabalho sem saber se é necessária ou não (o tradicional teste "está habilitado para depuração" conhecido no log4j) e deve ser evitada se possível, pois o {} permite atrasar a chamada toString () e a construção de string para depois de ter sido decidido se o evento precisa ser capturado ou não. Ao fazer o logger formatar uma única string, o código se torna mais limpo na minha opinião.

Você pode fornecer qualquer número de argumentos. Observe que se você usar uma versão antiga de sljf4j e tiver mais de dois argumentos para {}, deverá usar a new Object[]{a,b,c,d}sintaxe para passar um array. Consulte, por exemplo , http://slf4j.org/apidocs/org/slf4j/Logger.html#debug(java.lang.String, java.lang.Object []) .

Com relação à velocidade: Ceki postou um benchmark um tempo atrás em uma das listas.

Thorbjørn Ravn Andersen
fonte
6
observação: o javadoc mais recente mostra a sintaxe var-arg mais recente debug(String format, Object... arguments),. Consulte slf4j.org/faq.html#logging_performance
michael
Aprovado devido à menção da avaliação .toString (), além do desempenho da concatenação. Isso é algo que acontece dentro do logger e o logger pode decidir se é necessário invocar esse método. Isso não acontecerá se a barra de nível de registro não for atendida.
Chetan Narsude
6

Uma vez que String é imutável em Java, a String esquerda e direita devem ser copiadas na nova String para cada par de concatenação. Então, é melhor ir para o espaço reservado.

Rabin Pantha
fonte
2
Isso está correto se houver um único par, mas geralmente incorreto, pois o compilador transforma a concatenação em chamadas de construtor de strings, resultando em um código muito mais rápido que não faz tanta alocação.
cdeszaq
3

Outra alternativa é String.format(). Estamos usando-o em jcabi-log (wrapper de utilitário estático em torno de slf4j).

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

É muito mais sustentável e extensível. Além disso, é fácil de traduzir.

Yegor256
fonte
3
Não acho que seja mais sustentável. se o tipo de valuealteração for alterado, você terá que voltar e alterar a instrução de registro também. Algo em que os IDEs não o ajudarão. Os registradores devem ajudar na depuração e não interferir nisso. :-)
Chetan Narsude
3
@ChetanNarsude IntelliJ 2016 pelo menos me informa quando a string de formato não se ajusta aos argumentos de formatação. Por exemplo: String.format("%d", "Test")produz o aviso IntelliJ Argument type 'String' does not match the type of the format specifier '%d'.. Porém, não tenho certeza de que ainda seria capaz de fornecer essa resposta inteligente ao trabalhar com a solução acima.
esmagamento
qual a velocidade disso?
Thorbjørn Ravn Andersen de
@ ThorbjørnRavnAndersen é bem primitivo por dentro, mas é claro que é mais lento do que um logger estático
yegor256
Envolvendo slf4j? isso não anula o propósito de usar slf4j? Além disso, tenho visto muitas pessoas usarem incorretamente String.format de forma que a string seja formatada antes que o nível de log seja avaliado, como: logger.info (String.format ("hello% s", username)).
Juan Bustamante
2

Acho que, do ponto de vista do autor, o principal motivo é reduzir a sobrecarga da concatenação de strings. Acabei de ler a documentação do logger, e você pode encontrar as seguintes palavras:

/**
* <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
fonte