CodeGym /Kursy /JAVA 25 SELF /Podstawy logowania: java.util.logging, Log4j, SLF4J

Podstawy logowania: java.util.logging, Log4j, SLF4J

JAVA 25 SELF
Poziom 63 , Lekcja 0
Dostępny

1. Po co potrzebne jest logowanie

Logowanie ≠ wypisywanie na konsolę

Kiedy dopiero zaczynasz programować, wydaje się, że do szukania problemów wystarczy wszędzie wstawić System.out.println("Tu byłem!"). To działa, dopóki program jest mały i uruchamiasz go na własnym komputerze. Ale wyobraź sobie duży projekt, serwer działający 24/7 albo aplikację używaną przez tysiące ludzi — przecież nie będziesz siedzieć każdemu użytkownikowi za plecami i patrzeć na jego konsolę.

Logowanie — to nie tylko wypisywanie komunikatów. To usystematyzowany zapis informacji o pracy aplikacji: błędów, ostrzeżeń, zdarzeń biznesowych, szczegółów technicznych. Logi zapisuje się do plików, baz danych, mogą być wysyłane przez sieć — i pozwalają analizować działanie programu po jego wykonaniu lub w trakcie działania.

Do czego służą logi

  • Diagnostyka problemów: jeśli coś poszło nie tak, logi to twój główny pomocnik. Na ich podstawie można zrozumieć, gdzie i dlaczego wystąpił błąd.
  • Audyt i bezpieczeństwo: logi rejestrują ważne działania użytkowników, co pomaga badać incydenty.
  • Debugowanie: czasem błędy ujawniają się tylko w określonych warunkach i bez logów nie da się ich uchwycić.
  • Monitorowanie: z logów można odczytać stan aplikacji, jej wydajność i „zdrowie”.

Przykład z życia

Gdyby samoloty nie miały „czarnych skrzynek” (logów lotu), dochodzenie przyczyn wypadków byłoby niemal niemożliwe. W programowaniu logi to twoje czarne skrzynki.

2. Podstawowe poziomy logowania

W logowaniu przyjęło się używać poziomów (levels) komunikatów. To jak sygnalizacja świetlna: czerwone — niebezpieczeństwo, żółte — ostrożnie, zielone — wszystko w porządku.

Główne poziomy (od najbardziej „głośnego” do najbardziej „cichego”):

Poziom Opis
ERROR
Krytyczny błąd, aplikacja nie może kontynuować pracy
WARN
Ostrzeżenie: coś poszło nie tak, ale program działa
INFO
Informacyjny komunikat o zdarzeniach rutynowych
DEBUG
Szczegółowe informacje do debugowania (widoczne tylko dla programistów)
TRACE
Najbardziej szczegółowe informacje — do głębokiej diagnostyki

Przykład:

  • Użytkownik nie mógł się zalogować z powodu błędnego hasła — to WARN.
  • Zepsuła się baza danych — to ERROR.
  • Aplikacja wystartowała — to INFO.
  • Wypisywanie wartości zmiennej w pętli — to DEBUG lub TRACE.

Jak wybierać poziom

Nie warto wszystkiego pisać na poziomie ERROR — inaczej utoniesz w fałszywych alarmach. Używaj poziomów świadomie: tylko naprawdę krytyczne awarie powinny być błędami.

3. Standardowe logowanie w Javie (java.util.logging)

Java jest dostarczana z wbudowanym systemem logowania — pakietem java.util.logging (w skrócie JUL). To narzędzie dostępne zawsze, nawet jeśli nie dołączyłeś żadnych bibliotek.

Główne klasy:

  • Logger — główna klasa do zapisywania logów.
  • Level — wyliczenie poziomów logowania (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST).
  • Handler — wskazuje, dokąd pisać logi (plik, konsola itd.).
  • Formatter — odpowiada za format komunikatów.

Przykład użycia JUL

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

public class LoggingExample {
    // Pobieramy logger na podstawie nazwy klasy
    private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());

    public static void main(String[] args) {
        logger.info("Aplikacja została uruchomiona");
        logger.warning("To jest ostrzeżenie!");
        logger.severe("A to już błąd!");

        int x = 42;
        logger.fine("Zmienna debug x=" + x); // Domyślnie nie jest wyświetlana
    }
}

Ważna uwaga: domyślnie JUL wypisuje tylko poziomy INFO i wyższe. Aby zobaczyć fine i inne szczegółowe komunikaty, trzeba ustawić odpowiedni poziom logowania.

Konfiguracja przez plik

JUL można konfigurować przez plik logging.properties (zwykle leży w katalogu JRE). Można tam określić:

  • Minimalny poziom dla loggera
  • Dokąd pisać logi (plik, konsola)
  • Formatowanie komunikatów

Przykładowa linia w logging.properties:

.level=INFO

4. Zewnętrzne biblioteki logowania

Log4j (Apache)

Log4j — jedna z najbardziej znanych bibliotek logowania dla Javy. Jest elastyczna, wydajna, obsługuje różne formaty, zapis asynchroniczny, rotację plików i wiele więcej.

Przykład najprostszej konfiguracji 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>

Przykład użycia Log4j 2 w kodzie:

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("To błąd z użyciem Log4j");
    }
}

Aby to działało, należy dodać zależności Log4j do projektu (np. przez Maven lub Gradle).

SLF4J: fasada logowania

SLF4J (Simple Logging Facade for Java) — to nie jest oddzielny system logowania, lecz „warstwa pośrednia” między twoim kodem a konkretną implementacją (Log4j, Logback, JUL itd.).

Fasada jest potrzebna, aby pisać kod raz, a w razie potrzeby zmieniać rzeczywisty system logowania bez przepisywania kodu, nawet jeśli różne biblioteki używają różnych loggerów.

Przykład użycia 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("Witaj, logowanie przez SLF4J!");
        logger.warn("Uwaga: coś może pójść nie tak");
        logger.error("Błąd z użyciem SLF4J");
    }
}

Jak to działa?

  • Piszesz kod przez SLF4J.
  • Do classpath dodajesz potrzebną implementację (np. Logback lub Log4j).
  • SLF4J sam „przekazuje” wywołania do wybranej implementacji.

Współpraca SLF4J z innymi loggerami: SLF4J może działać nad JUL, Log4j, Logback itd., co pozwala łatwo migrować bez przepisywania kodu.

5. Praktyka: porównujemy System.out.println i logowanie

Przykład 1: System.out.println

public class PrintlnExample {
    public static void main(String[] args) {
        System.out.println("Aplikacja została uruchomiona");
        System.out.println("Wystąpił błąd: coś poszło nie tak");
    }
}

Co jest nie tak:

  • Brak poziomu komunikatu (błąd, informacja, ostrzeżenie — wszystko wygląda tak samo).
  • Brak czasu, nazwy klasy, wątku.
  • Wszystkie komunikaty trafiają do jednego „worka”.
  • Nie można elastycznie skonfigurować wyjścia (np. wypisywać tylko błędy).

Przykład 2: Logowanie przez 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("Aplikacja została uruchomiona");
        logger.error("Wystąpił błąd: coś poszło nie tak");
    }
}

Zalety:

  • Każdy komunikat jest opatrzony czasem, poziomem, nazwą klasy i wątkiem.
  • Można filtrować komunikaty według poziomu.
  • Można pisać logi do pliku, wysyłać przez sieć, formatować, archiwizować.
  • Logowanie jest bezpieczne dla wątków (istotne w aplikacjach wielowątkowych).

Demonstracja różnicy

System.out.println
Logowanie (SLF4J/Logback)
Aplikacja została uruchomiona 12:34:56 [main] INFO LoggingVsPrintln - Aplikacja została uruchomiona
Wystąpił błąd: coś poszło nie tak 12:34:56 [main] ERROR LoggingVsPrintln - Wystąpił błąd: coś poszło nie tak

6. Jak dodać logowanie do swojego projektu

Dla standardowego loggera (JUL)

Wszystko jest już w JDK, można od razu używać:

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("To komunikat informacyjny");
    }
}

Dla Log4j lub SLF4J

  1. Dodaj zależności (np. przez Maven).

    Dla Log4j 2:

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

    Dla 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. Utwórz plik konfiguracyjny (np. logback.xml dla Logback lub log4j2.xml dla Log4j).
  3. Używaj loggera w swoim kodzie (zob. przykłady powyżej).

7. Miniaplikacja: dodajemy logowanie

Załóżmy, że mamy prostą aplikację — kalkulator wiersza poleceń. Dodajmy do niej logowanie.

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("Kalkulator uruchomiony");

        try {
            System.out.print("Wprowadź pierwszą liczbę: ");
            int a = Integer.parseInt(scanner.nextLine());
            logger.debug("Wprowadzono pierwszą liczbę: {}", a);

            System.out.print("Wprowadź drugą liczbę: ");
            int b = Integer.parseInt(scanner.nextLine());
            logger.debug("Wprowadzono drugą liczbę: {}", b);

            int sum = a + b;
            logger.info("Suma liczb: {}", sum);
            System.out.println("Wynik: " + sum);
        } catch (NumberFormatException ex) {
            logger.error("Błąd wprowadzania liczby", ex);
            System.out.println("Błąd: wprowadź poprawną liczbę!");
        } catch (Exception ex) {
            logger.error("Nieznany błąd", ex);
            System.out.println("Wystąpił błąd!");
        }
        logger.info("Kalkulator zakończył pracę");
    }
}

Co się tutaj dzieje:

  • Wszystkie kluczowe zdarzenia są rejestrowane w logu.
  • Błędy są logowane z poziomem ERROR i pełnym stack trace’em.
  • Wszystkie informacje są dostępne do analizy po zakończeniu działania programu.

8. Typowe błędy przy pracy z logowaniem

Błąd nr 1: używanie tylko System.out.println do debugowania i diagnostyki.
To wygodne na starcie, ale kompletnie nie nadaje się do prawdziwych aplikacji. Nie możesz zarządzać poziomem komunikatów, nie zobaczysz czasu, nie przeanalizujesz logów, gdy program działa na serwerze.

Błąd nr 2: logowanie zbyt dużej ilości informacji na poziomie ERROR.
Jeśli wszystko jak leci zapisujesz jako błąd, przestaniesz rozróżniać, co naprawdę ważne, a co jest tylko informacją do debugowania.

Błąd nr 3: logowanie danych wrażliwych (hasła, tokeny, numery kart).
Logi muszą być bezpieczne! Nie zapisuj tam tego, czego inni nie powinni widzieć.

Błąd nr 4: brak konfiguracji logowania.
Jeśli nie ustawisz poziomów, formatu i miejsca przechowywania logów — możesz niespodziewanie „utonąć” w gigabajtach niepotrzebnych informacji albo w ogóle nie zauważyć błędu.

Błąd nr 5: niewykorzystywanie fasady logowania (SLF4J) w dużych projektach.
Gdy projekt rośnie i przechodzisz z jednej biblioteki na inną — bez fasady to będzie bolesne. SLF4J pozwala łatwo zmienić „silnik” logowania bez przepisywania kodu.

1
Zadanie
JAVA 25 SELF, poziom 63, lekcja 0
Niedostępne
Inżynier w fabryce
Inżynier w fabryce
1
Zadanie
JAVA 25 SELF, poziom 63, lekcja 0
Niedostępne
Nowy programista
Nowy programista
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION