JavaRush /Курси /JAVA 25 SELF /Основи логування: java.util.logging, Log4j, SLF4J

Основи логування: java.util.logging, Log4j, SLF4J

JAVA 25 SELF
Рівень 63 , Лекція 0
Відкрита

1. Навіщо потрібне логування

Логування ≠ виведення у консоль

Коли ви лише починаєте програмувати, здається, що для пошуку проблем достатньо вставити всюди System.out.println("Ось тут я був!"). Це працює, поки програма маленька й запускається у вас на комп’ютері. Але уявіть собі великий проєкт, сервер, який працює 24/7, або застосунок, яким користуються тисячі людей — ви ж не сидітимете в кожного користувача за плечем і не стежитимете за його консоллю?

Логування — це не просто виведення повідомлень. Це систематизований запис інформації про роботу застосунку: помилок, попереджень, бізнес-подій, технічних деталей. Логи зберігаються у файли, бази даних, можуть надсилатися мережею — і дозволяють аналізувати роботу програми після її виконання або прямо під час роботи.

Для чого потрібні логи

  • Діагностика проблем: якщо щось пішло не так, логи — ваш головний помічник. За ними можна зрозуміти, де й чому виникла помилка.
  • Аудит і безпека: логи фіксують важливі дії користувачів, що допомагає розслідувати інциденти.
  • Налагодження: іноді баги проявляються лише за певних умов, і без логів їх не впіймати.
  • Моніторинг: за логами можна відстежувати стан застосунку, його продуктивність і «здоров’я».

Приклад із життя

Якби у літаків не було «чорних ящиків» (логи польоту), розслідувати причини аварій було б майже неможливо. У програмуванні логи — це ваші чорні ящики.

2. Базові рівні логування

У логуванні прийнято використовувати рівні (levels) повідомлень. Це як світлофор: червоний — небезпечно, жовтий — обережно, зелений — усе гаразд.

Основні рівні (від най«гучнішого» до най«тихішого»):

Рівень Опис
ERROR
Критична помилка, застосунок не може продовжувати роботу
WARN
Попередження: щось пішло не так, але програма працює
INFO
Інформаційне повідомлення про штатні події
DEBUG
Докладна інформація для налагодження (видно лише розробникам)
TRACE
Найдетальніша інформація — для глибокої діагностики

Приклад:

  • Користувач не зміг увійти через неправильний пароль — це WARN.
  • Зламалася база даних — це ERROR.
  • Застосунок стартував — це INFO.
  • Виведення значення змінної в циклі — це DEBUG або TRACE.

Як обирати рівень

Не варто все підряд писати на рівні ERROR — інакше у справжніх помилках ви потонете. Використовуйте рівні свідомо: лише справді критичні збої мають бути помилками.

3. Стандартне логування в Java (java.util.logging)

Java постачається зі вбудованою системою логування — пакет java.util.logging (скорочено JUL). Це «рідний» інструмент, який є завжди, навіть якщо ви не підключали жодних бібліотек.

Основні класи:

  • Logger — головний клас для запису логів.
  • Level — перелічення рівнів логування (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST).
  • Handler — обробник, що визначає, куди писати логи (файл, консоль тощо).
  • Formatter — відповідає за формат повідомлень.

Приклад використання JUL

import java.util.logging.Logger;
import java.util.logging.Level;

public class LoggingExample {
    // Отримуємо логер за ім’ям класу
    private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());

    public static void main(String[] args) {
        logger.info("Застосунок запущено");
        logger.warning("Це попередження!");
        logger.severe("А це вже помилка!");

        int x = 42;
        logger.fine("Налагоджувальна змінна x=" + x); // За замовчуванням не виводиться
    }
}

Важливий момент: за замовчуванням JUL виводить лише рівні INFO і вище. Щоб побачити FINE та інші докладні повідомлення, потрібно налаштувати рівень логування.

Конфігурація через файл

JUL можна налаштовувати через файл logging.properties (зазвичай розташований у папці JRE). Там можна вказати:

  • Мінімальний рівень для логера
  • Куди писати логи (файл, консоль)
  • Форматування повідомлень

Приклад рядка в logging.properties:

.level=INFO

4. Зовнішні бібліотеки логування

Log4j 2 (Apache)

Log4j — одна з найвідоміших бібліотек логування для Java. Вона гнучка, потужна, підтримує різні формати, асинхронний запис, ротацію файлів і багато іншого.

Приклад найпростішої конфігурації Log4j 2 (XML):

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

Приклад використання Log4j 2 у коді:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jExample {
    private static final Logger logger = LogManager.getLogger(Log4jExample.class);

    public static void main(String[] args) {
        logger.info("Hello from Log4j!");
        logger.error("Це помилка через Log4j");
    }
}

Щоб це працювало, потрібно додати залежності Log4j до вашого проєкту (наприклад, через Maven або Gradle).

SLF4J: фасад логування

SLF4J (Simple Logging Facade for Java) — це не окрема система логування, а «прокладка» між вашим кодом і конкретною реалізацією (Log4j, Logback, JUL тощо).

Фасад потрібен, щоб писати код один раз і за потреби змінювати реальну систему логування без переписування коду, навіть якщо різні бібліотеки використовують різні логери.

Приклад використання SLF4J:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jExample {
    private static final Logger logger = LoggerFactory.getLogger(Slf4jExample.class);

    public static void main(String[] args) {
        logger.info("Привіт, логування через SLF4J!");
        logger.warn("Увага: щось може піти не так");
        logger.error("Помилка через SLF4J");
    }
}

Як це працює?

  • Ви пишете код через SLF4J.
  • У classpath додаєте потрібну реалізацію (наприклад, Logback або Log4j).
  • SLF4J сам переспрямовує виклики до обраної реалізації.

Взаємодія SLF4J з іншими логерами: SLF4J може працювати поверх JUL, Log4j, Logback тощо, що дає змогу легко мігрувати без переписування коду.

5. Практика: порівнюємо System.out.println і логування

Приклад 1: System.out.println

public class PrintlnExample {
    public static void main(String[] args) {
        System.out.println("Застосунок запущено");
        System.out.println("Сталася помилка: щось пішло не так");
    }
}

Що не так:

  • Немає рівня повідомлення (помилка, інформація, попередження — усе однакове).
  • Немає часу, імені класу, потоку.
  • Усі повідомлення змішані докупи.
  • Неможливо гнучко налаштувати виведення (наприклад, писати лише помилки).

Приклад 2: Логування через SLF4J + Logback

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingVsPrintln {
    private static final Logger logger = LoggerFactory.getLogger(LoggingVsPrintln.class);

    public static void main(String[] args) {
        logger.info("Застосунок запущено");
        logger.error("Сталася помилка: щось пішло не так");
    }
}

Переваги:

  • Кожне повідомлення супроводжується часом, рівнем, іменем класу, потоком.
  • Можна фільтрувати повідомлення за рівнем.
  • Можна писати логи у файл, надсилати мережею, форматувати, архівувати.
  • Логування є потокобезпечним (актуально для багатопотокових застосунків).

Демонстрація різниці

System.out.println
Логування (SLF4J/Logback)
Застосунок запущено 12:34:56 [main] INFO LoggingVsPrintln - Застосунок запущено
Сталася помилка: щось пішло не так 12:34:56 [main] ERROR LoggingVsPrintln - Сталася помилка: щось пішло не так

6. Як додати логування до свого проєкту

Для стандартного логера (JUL)

Усе вже є в JDK, можна відразу використовувати:

import java.util.logging.Logger;

public class MyApp {
    private static final Logger logger = Logger.getLogger(MyApp.class.getName());

    public static void main(String[] args) {
        logger.info("Це інформаційне повідомлення");
    }
}

Для Log4j або SLF4J

  1. Додайте залежності (наприклад, через Maven).

    Для Log4j 2:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.20.0</version>
    </dependency>
    

    Для SLF4J + Logback:

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.7</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.7</version>
    </dependency>
    
  2. Створіть конфігураційний файл (наприклад, logback.xml для Logback або log4j2.xml для Log4j).
  3. Використовуйте логер у своєму коді (див. приклади вище).

7. Міні-застосунок: додаємо логування

Припустімо, у нас є простий застосунок — калькулятор командного рядка. Додамо до нього логування.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Scanner;

public class CalculatorApp {
    private static final Logger logger = LoggerFactory.getLogger(CalculatorApp.class);

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        logger.info("Калькулятор запущено");

        try {
            System.out.print("Введіть перше число: ");
            int a = Integer.parseInt(scanner.nextLine());
            logger.debug("Введено перше число: {}", a);

            System.out.print("Введіть друге число: ");
            int b = Integer.parseInt(scanner.nextLine());
            logger.debug("Введено друге число: {}", b);

            int sum = a + b;
            logger.info("Сума чисел: {}", sum);
            System.out.println("Результат: " + sum);
        } catch (NumberFormatException ex) {
            logger.error("Помилка введення числа", ex);
            System.out.println("Помилка: введіть коректне число!");
        } catch (Exception ex) {
            logger.error("Невідома помилка", ex);
            System.out.println("Сталася помилка!");
        }
        logger.info("Калькулятор завершив роботу");
    }
}

Що тут відбувається:

  • Усі ключові події фіксуються у логах.
  • Помилки логуються з рівнем ERROR і повним стек-трейсом.
  • Уся інформація доступна для аналізу після роботи програми.

8. Типові помилки під час роботи з логуванням

Помилка № 1: Використання лише System.out.println для налагодження та діагностики.
Це зручно на старті, але геть не підходить для реальних застосунків. Ви не зможете керувати рівнем повідомлень, не побачите часу, не зможете аналізувати логи, якщо програма працює на сервері.

Помилка № 2: Логування занадто великого обсягу інформації на рівні ERROR.
Якщо все підряд писати як помилку, ви перестанете розрізняти, що справді важливо, а що — просто інформація для налагодження.

Помилка № 3: Логування чутливих даних (паролі, токени, номери карток).
Логи мають бути безпечними! Не пишіть туди те, чого не мають бачити інші.

Помилка № 4: Відсутність конфігурації логування.
Якщо не налаштувати рівні, формат, місце зберігання логів — можна неочікувано «потонути» в гігабайтах зайвої інформації або взагалі не побачити помилку.

Помилка № 5: Невикористання фасаду логування (SLF4J) у великих проєктах.
Якщо ваш проєкт зростатиме, переходити з однієї бібліотеки на іншу — без фасаду це буде болісно. SLF4J дає змогу легко змінювати рушій логування без переписування коду.

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