Logging básico
Un System.out.println("paso 3") puede ayudar durante un minuto, pero no indica severidad, contexto ni clase de origen. Cuando el proyecto crece, esos mensajes se mezclan con la salida funcional y cuesta decidir cuáles conservar.
Usaremos SLF4J 2.0.17 como fachada y slf4j-simple 2.0.17 como implementación local. La fachada ofrece la API; la implementación decide cómo se muestran los eventos. Ambas versiones son reales, compatibles entre sí y comprobadas con Java 17.
Dependencias
<properties>
<slf4j.version>2.0.17</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
Producción compila contra la API. La implementación se necesita al ejecutar, por eso usa scope runtime. No añadimos varias implementaciones simultáneas.
Un logger por clase
package es.skilly.tareas.servicio;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GestorTareas {
private static final Logger logger =
LoggerFactory.getLogger(GestorTareas.class);
public void crear(long id, String descripcion) {
// Guardar la tarea...
logger.info("Tarea {} creada", id);
}
}
El nombre de la clase ayuda a localizar el origen. Los placeholders {} evitan construir mediante concatenación un mensaje que quizá el nivel configurado no muestre:
logger.debug("Procesando tarea {} con estado {}", id, estado);
Elegir nivel
TRACE: detalle muy fino del recorrido; normalmente desactivado.DEBUG: información útil al investigar, como una decisión interna.INFO: evento normal relevante, por ejemplo una carga completada.WARN: situación inesperada recuperable, como una línea inválida ignorada.ERROR: operación fallida que requiere atención.
No todo método necesita un log. Registra eventos que ayuden a reconstruir qué operación ocurrió y con qué resultado.
logger.info("Carga completada: {} tareas", cantidad);
logger.warn("Línea {} ignorada: formato inválido", numeroLinea);
Excepciones con su stack trace
Este mensaje pierde la traza:
logger.error("No se pudo guardar: {}", error.getMessage());
SLF4J interpreta el último argumento Throwable como excepción y conserva la traza:
try {
repositorio.guardar(tareas);
} catch (IOException error) {
logger.error("No se pudieron guardar {} tareas", tareas.size(), error);
throw new IllegalStateException("Error al guardar las tareas", error);
}
El log aporta contexto y la excepción mantiene el contrato hacia el llamador. No registres el error y lo ocultes como si la operación hubiera terminado bien.
Contexto útil y datos sensibles
Incluye identificadores, operación y resultado cuando ayudan:
logger.info("Tarea {} actualizada: resultado={}", id, resultado);
Nunca registres contraseñas, tokens, claves o contenido personal sensible:
// Incorrecto
logger.debug("Login usuario={} password={}", usuario, password);
Si un identificador basta para investigar, no vuelques el objeto completo.
Configuración reproducible
slf4j-simple acepta simplelogger.properties en src/main/resources:
org.slf4j.simpleLogger.defaultLogLevel=info
org.slf4j.simpleLogger.showDateTime=true
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss
org.slf4j.simpleLogger.showThreadName=false
Versionar este recurso evita que cada persona dependa de opciones ocultas del IDE. Para una ejecución local sencilla podemos usar Exec Maven Plugin 3.5.0:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<mainClass>es.skilly.tareas.app.Main</mainClass>
</configuration>
</plugin>
Ejecución:
mvn compile exec:java
Una salida verificable incluye el evento y el identificador:
INFO es.skilly.tareas.servicio.GestorTareas - Tarea 42 creada
La fecha exacta puede variar; el hecho verificable es el nivel, origen y mensaje.
Práctica
Sustituye estos prints:
System.out.println("Inicio carga");
System.out.println("Tarea " + id + " creada");
System.out.println("ERROR: " + error.getMessage());
Requisitos:
- inicio de carga en
INFO; - alta con placeholder e identificador;
- excepción en
ERRORcon stack trace; - ninguna descripción completa ni secreto en los logs.
Solución
logger.info("Carga iniciada");
logger.info("Tarea {} creada", id);
logger.error("No se pudo completar la carga", error);
La solución comunica eventos estables, conserva la excepción y evita concatenaciones. Si una línea inválida puede ignorarse con seguridad, sería un WARN con su número, no un ERROR silenciosamente capturado.
Ejecuta con Maven y comprueba la salida. En la siguiente unidad documentaremos el contrato y el uso del proyecto; los logs explican qué ocurrió durante una ejecución, no cómo debe usarse la API.