CodeGym /Cursos /Módulo 5. Spring /Registro de errores y excepciones

Registro de errores y excepciones

Módulo 5. Spring
Nivel 8 , Lección 9
Disponible

Hoy nos vamos a sumergir un poco más en el registro. Antes de lanzarte al código, pregúntate: ¿cómo sabrías que la aplicación "se rompió"? Claro, los usuarios pueden quejarse de que "el sitio no funciona", pero eso no es suficiente para diagnosticarlo. Ahí entra en juego el registro. Spring ofrece un mecanismo potente y flexible para el registro, que se integra fácilmente con librerías populares como SLF4J, Logback y Log4j. Ahora imagina tu vida sin logs. ¿Tu única forma de depurar sería poner System.out.println() por todo el código? No solo es ineficiente, sino que parece intentar encontrar el norte con un cactus.


Configuración del registro en Spring Boot

Por defecto, Spring Boot viene con soporte para SLF4J (Simple Logging Facade for Java) y Logback. Es la combinación estándar que cubre la mayoría de los casos.

Añadir la dependencia (por si no la tienes)

Si aún no estás usando Spring Boot Starter Logging, añade esto al pom.xml:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

Spring Boot conectará automáticamente Logback, así que no harán falta manipulaciones adicionales con las librerías.


Registro en los manejadores de errores

Registrar errores en los manejadores es una parte clave del manejo de excepciones. Empecemos con un ejemplo sencillo usando @ExceptionHandler y añadiendo logging.

Ejemplo


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        LOGGER.error("Ocurrió un error: {}", ex.getMessage(), ex);
        return new ResponseEntity<>("Solicitud incorrecta: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

Aquí usamos Logger para registrar el mensaje de error, junto con el stack trace (ex al final). Esto permite obtener una imagen completa del fallo.


Entendiendo los niveles de logging

Antes de escribir un logger mega-cool, es importante entender qué son los niveles de logging:

  1. TRACE: información muy detallada sobre el funcionamiento de la aplicación (se usa rara vez).
  2. DEBUG: información para depurar la aplicación. Por ejemplo, qué valores se pasaron a un método.
  3. INFO: información general sobre el funcionamiento normal de la aplicación.
  4. WARN: advertencias que no son críticas pero requieren revisión (por ejemplo, la sesión está a punto de expirar).
  5. ERROR: errores que requieren intervención.

Ejemplo usando distintos niveles:


LOGGER.trace("Información detallada para depuración.");
LOGGER.debug("Datos para analizar: valor X = {}", x);
LOGGER.info("El usuario se ha autenticado correctamente.");
LOGGER.warn("La contraseña del usuario caducará pronto.");
LOGGER.error("Error al procesar la petición.", exception);

En la práctica, los logs por debajo de INFO suelen estar desactivados en la mayoría de sistemas en producción.


Configuración de Logback mediante archivo de configuración

¿Quieres más control sobre los logs? Spring Boot permite configurar Logback en el archivo logback-spring.xml.

Ejemplo de configuración


<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>logs/app.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.example" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </logger>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

Este archivo crea dos salidas: una imprime los logs en la consola y la otra en el archivo app.log. Fíjate en el pattern: define el formato de salida de los logs (incluyendo timestamp, nivel, nombre del logger y el mensaje).


Registro con SLF4J en controladores y servicios

Ejemplo

Añadamos logs a un controlador:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private static final Logger LOGGER = LoggerFactory.getLogger(DemoController.class);

    @GetMapping("/example")
    public ResponseEntity<String> exampleMethod(@RequestParam String input) {
        LOGGER.info("Solicitud recibida con parámetro: {}", input);

        if (input.isEmpty()) {
            LOGGER.warn("¡Parámetro de la petición vacío!");
            return new ResponseEntity<>("El parámetro no debe estar vacío", HttpStatus.BAD_REQUEST);
        }

        LOGGER.debug("Procesando la petición...");
        return new ResponseEntity<>("Respuesta exitosa", HttpStatus.OK);
    }
}

Ejercicio práctico: registrando excepciones personalizadas

Creamos una excepción personalizada y un manejador global con logging.

Paso 1. Crear la excepción personalizada


public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}

Paso 2. Añadir el manejo


@RestControllerAdvice
public class GlobalErrorHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalErrorHandler.class);

    @ExceptionHandler(CustomException.class)
    public ResponseEntity<String> handleCustomException(CustomException ex) {
        LOGGER.error("Excepción personalizada: {}", ex.getMessage(), ex);
        return new ResponseEntity<>("Ocurrió un error: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Paso 3. Usar la excepción en el código


@RestController
public class CustomExceptionController {

    @GetMapping("/trigger-error")
    public ResponseEntity<String> triggerError() {
        throw new CustomException("¡Algo salió mal!");
    }
}

Resultado: si el usuario abre /trigger-error, verá un mensaje de error claro, y en los logs habrá el stack trace con los detalles.


Registro y monitorización

Integrar los logs con sistemas de monitorización (por ejemplo, ELK: Elasticsearch, Logstash, Kibana) te permite seguir los logs en tiempo real. Spring Actuator también puede registrar eventos del sistema. En resumen, tus logs dejarán de ser solo texto y se convertirán en una herramienta para anticiparte a problemas.


Errores típicos al registrar

  • Intentar loguear absolutamente todo (un millón de logs DEBUG = fin del rendimiento).
  • Usar System.out.println() en vez de un logger adecuado.
  • Olvidarse de los niveles de logging: todo es WARN o INFO.
  • Registrar datos sensibles (por ejemplo, contraseñas).

Usa los niveles adecuados y evita saturar los logs con información innecesaria.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION