Logger slf4j ventajas de formatear con {} en lugar de concatenación de cadenas

100

¿Hay alguna ventaja de usar en {}lugar de la concatenación de cadenas?

Un ejemplo de slf4j

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

en vez de

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

Creo que se trata de la optimización de la velocidad porque la evaluación de parámetros (y la concatenación de cadenas) podrían evitarse en tiempo de ejecución dependiendo de un archivo de configuración. Pero solo son posibles dos parámetros, entonces a veces no hay otra opción que la concatenación de cadenas. Necesita opiniones sobre este tema.

Hernán Eche
fuente

Respuestas:

74

Se trata del rendimiento de la concatenación de cadenas. Es potencialmente significativo si tiene declaraciones de registro densas.

(Antes de SLF4J 1.7) Pero solo dos parámetros son posibles

Debido a que la gran mayoría de las declaraciones de registro tienen 2 o menos parámetros, la API de SLF4J hasta la versión 1.6 cubre (solo) la mayoría de los casos de uso. Los diseñadores de API han proporcionado métodos sobrecargados con parámetros varargs desde la versión 1.7 de API.

Para aquellos casos en los que necesita más de 2 y está atascado con SLF4J anterior a 1.7, use la concatenación de cadenas o new Object[] { param1, param2, param3, ... }. Debería haber pocos de ellos para que el rendimiento no sea tan importante.

skaffman
fuente
2
Se debe evitar la concatenación de cadenas no utilizadas (es decir, declaraciones de depuración). Utilice la comprobación de nivel de registro (demasiado detallada pero eficiente) o el parámetro de matriz de objetos (más delgado, pero quizás menor). (Preferiría lo último, en igualdad de condiciones). Es difícil decir que la cadena concat no será importante / no afectará el rendimiento. La creación de la matriz de objetos podría, en teoría, estar en línea y optimizada y "realmente" no marcar una diferencia (frente a las ilusiones). (No es una optimización prematura, es simplemente una cuestión de hacer algo bien / mejor la primera vez)
michael
¿Por qué no se realizan modificaciones sobrecargadas para que System.out.println () siga de manera similar al registrador de slf4j, de modo que evite la concatenación de cadenas?
a3.14_Infinity
44

Versión corta: ¡Sí, es más rápido, con menos código!

La concatenación de cadenas hace mucho trabajo sin saber si es necesaria o no (la prueba tradicional de "depuración habilitada" conocida por log4j), y debe evitarse si es posible, ya que {} permite retrasar la llamada toString () y la construcción de cadenas hasta después de que se haya decidido si el evento necesita capturarse o no. Al hacer que el registrador formatee una sola cadena, el código se vuelve más limpio en mi opinión.

Puede proporcionar cualquier número de argumentos. Tenga en cuenta que si usa una versión anterior de sljf4j y tiene más de dos argumentos {}, debe usar la new Object[]{a,b,c,d}sintaxis para pasar una matriz en su lugar. Consulte, por ejemplo , http://slf4j.org/apidocs/org/slf4j/Logger.html#debug(java.lang.String, java.lang.Object []) .

En cuanto a la velocidad: Ceki publicó un punto de referencia hace un tiempo en una de las listas.

Thorbjørn Ravn Andersen
fuente
6
nota: las últimas javadoc muestra la sintaxis var-arg más reciente, debug(String format, Object... arguments). Ver slf4j.org/faq.html#logging_performance
michael
Votado a favor debido a la mención de la evaluación .toString () además del rendimiento de concatenación. Esto es algo que sucede dentro del registrador y el registrador puede decidir si es necesario invocar ese método. No es así si no se cumple la barra de nivel de registro.
Chetan Narsude
6

Dado que String es inmutable en Java, la String izquierda y derecha deben copiarse en la nueva String para cada par de concatenación. Entonces, mejor opta por el marcador de posición.

Rabin Pantha
fuente
2
Esto es correcto si hay un solo par, pero generalmente es incorrecto, ya que el compilador convierte la concatenación en llamadas al constructor de cadenas, lo que resulta en un código mucho más rápido que no realiza tanta asignación.
cdeszaq
3

Otra alternativa es String.format(). Lo estamos usando en jcabi-log (envoltorio de utilidad estática alrededor de slf4j).

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

Es mucho más fácil de mantener y ampliar. Además, es fácil de traducir.

yegor256
fuente
3
No creo que sea más fácil de mantener. si el tipo de valuecambios, debe volver atrás y cambiar la declaración de registro también. Algo con lo que los IDE no te ayudarán. Los registradores deben ayudar con la depuración y no interponerse en ella. :-)
Chetan Narsude
3
@ChetanNarsude IntelliJ 2016 al menos me dice cuándo la cadena de formato no se ajusta a los argumentos de formato. Por ejemplo: String.format("%d", "Test")produce la advertencia de IntelliJ Argument type 'String' does not match the type of the format specifier '%d'.. Sin embargo, no estoy seguro de que pueda proporcionar esta respuesta inteligente al trabajar con la solución anterior.
aplastar
cual es la velocidad de esto?
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen es bastante primitivo por dentro, pero por supuesto es más lento que un registrador estático
yegor256
Envolviendo slf4j? ¿No anula eso el propósito de usar slf4j? Además, he visto a muchas personas hacer un mal uso de String.format de modo que la cadena se formatea antes de que se evalúe el nivel de registro, como: logger.info (String.format ("hello% s", username)).
Juan Bustamante
2

Creo que desde el punto de vista del autor, la razón principal es reducir la sobrecarga de la concatenación de cadenas. Acabo de leer la documentación del registrador, puede encontrar las siguientes palabras:

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