JavaRush /Курси /Модуль 5. Spring /Логування помилок та виключень

Логування помилок та виключень

Модуль 5. Spring
Рівень 8 , Лекція 9
Відкрита

Сьогодні ми трішки глибше зануримося в логування. Перш ніж кидатися в код, задумайся: як би ти зрозумів, що додаток "зламався"? Звісно, користувачі можуть поскаржитися, що "сайт не працює", але цього замало для діагностики. Тут у гру вступає логування. Spring дає потужний і гнучкий механізм для логування, який легко інтегрується з популярними бібліотеками, такими як SLF4J, Logback і Log4j. А тепер уяви життя без логів. Єдиний спосіб відладки — розставляти System.out.println() по коду? Це не тільки неефективно, а й нагадує спробу знайти північ за допомогою кактуса.


Налаштування логування в Spring Boot

За замовчуванням Spring Boot постачається з підтримкою SLF4J (Simple Logging Facade for Java) і Logback. Це стандартне поєднання, яке покриває більшість задач.

Додавання залежності (якщо її ще немає)

Якщо ти ще не використовуєш Spring Boot Starter Logging, додай це в pom.xml:


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

Spring Boot автоматично підключить Logback, тож додаткових маніпуляцій з бібліотеками не знадобиться.


Логування в обробниках помилок

Логування помилок в обробниках — це ключова частина обробки виключень. Давай почнемо з простого прикладу використання @ExceptionHandler з додаванням логування.

Приклад


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("Виникла помилка: {}", ex.getMessage(), ex);
        return new ResponseEntity<>("Некоректний запит: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

Тут ми використовуємо Logger, щоб записати повідомлення про помилку разом зі стек-трейсом (ex в кінці). Це дає повне уявлення про помилку.


Розбираємося в рівнях логування

Перед тим як написати мегакрутий логгер, важливо зрозуміти, що таке рівні логування:

  1. TRACE: детальна інформація про роботу додатку (рідко використовується).
  2. DEBUG: інформація для налагодження. Наприклад, які значення були передані в метод.
  3. INFO: загальна інформація про нормальну роботу додатку.
  4. WARN: попередження, які не критичні, але потребують перевірки (наприклад, термін сесії закінчується).
  5. ERROR: помилки, які вимагають втручання.

Приклад використання різних рівнів:


LOGGER.trace("Детальна інформація для налагодження.");
LOGGER.debug("Дані для аналізу роботи: значення X = {}", x);
LOGGER.info("Користувач успішно авторизувався.");
LOGGER.warn("Термін дії пароля користувача скоро закінчиться.");
LOGGER.error("Помилка при обробці запиту.", exception);

На практиці логи з рівнів нижче INFO вимкнені в більшості production-систем.


Налаштування Logback через конфігураційний файл

Хочеш більше контролю над логами? Spring Boot дозволяє налаштувати Logback у файлі logback-spring.xml.

Приклад конфігурації


<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>

Цей файл створює два апендери: один виводить логи в консоль, інший — у файл app.log. Зверни увагу на pattern: він визначає формат виведення логів (включаючи timestamp, рівень, ім'я логера і повідомлення).


Логування за допомогою SLF4J у контролерах і сервісах

Приклад

Давай додамо логи в контролер:


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("Отримано запит з параметром: {}", input);

        if (input.isEmpty()) {
            LOGGER.warn("Пустий параметр запиту!");
            return new ResponseEntity<>("Параметр не повинен бути пустим", HttpStatus.BAD_REQUEST);
        }

        LOGGER.debug("Виконується обробка запиту...");
        return new ResponseEntity<>("Успішна відповідь", HttpStatus.OK);
    }
}

Практичне завдання: логування кастомних виключень

Створимо кастомне виключення й глобальний обробник з логуванням.

Крок 1. Створення кастомного виключення


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

Крок 2. Додавання обробки


@RestControllerAdvice
public class GlobalErrorHandler {

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

    @ExceptionHandler(CustomException.class)
    public ResponseEntity<String> handleCustomException(CustomException ex) {
        LOGGER.error("Кастомне виключення: {}", ex.getMessage(), ex);
        return new ResponseEntity<>("Сталася помилка: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Крок 3. Використання виключення в коді


@RestController
public class CustomExceptionController {

    @GetMapping("/trigger-error")
    public ResponseEntity<String> triggerError() {
        throw new CustomException("Щось пішло не так!");
    }
}

Результат: якщо користувач відкриє /trigger-error, він побачить зрозуміле повідомлення про помилку, а в логах буде стек-трейс з деталями.


Логування і моніторинг

Інтеграція логів з системами моніторингу (наприклад, ELK: Elasticsearch, Logstash, Kibana) дозволяє стежити за логами в реальному часі. Spring Actuator також може логувати системні події. Іншими словами, твої логи будуть не просто текстом, а інструментом для прогнозування проблем.


Типові помилки при логуванні

  • Намагаються логувати абсолютно все (мільйон DEBUG-логів = кінець продуктивності).
  • Використання System.out.println() замість нормального логера.
  • Забуття про рівні логування: все або WARN, або INFO.
  • Логування чутливих даних (наприклад, паролів).

Використовуй правильні рівні і уникай перевантаження логів непотрібною інформацією.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ