CodeGym /Corsi /JAVA 25 SELF /Fondamenti del logging: java.util.logging, Log4j, SLF4J

Fondamenti del logging: java.util.logging, Log4j, SLF4J

JAVA 25 SELF
Livello 63 , Lezione 0
Disponibile

1. Perché serve il logging

Il logging ≠ l'output su console

Quando inizi a programmare, può sembrare che per cercare i problemi basti inserire ovunque System.out.println("Ero qui!"). Questo funziona finché il programma è piccolo e lo esegui sul tuo computer. Ma immagina un progetto grande, un server che lavora 24/7, o un'applicazione usata da migliaia di persone — non starai certo dietro a ogni utente a guardare la sua console, giusto?

Il logging non è solo stampa di messaggi. È la registrazione sistematica delle informazioni sul funzionamento dell'applicazione: errori, avvisi, eventi di business, dettagli tecnici. I log vengono salvati su file, in database, possono essere inviati in rete — e consentono di analizzare il comportamento del programma dopo la sua esecuzione o anche durante.

A cosa servono i log

  • Diagnostica dei problemi: se qualcosa va storto, i log sono il tuo principale alleato. Grazie a loro puoi capire dove e perché si è verificato l'errore.
  • Audit e sicurezza: i log fissano le azioni importanti degli utenti, aiutando a investigare gli incidenti.
  • Debug: a volte i bug si manifestano solo in condizioni specifiche, e senza log non li catturi.
  • Monitoraggio: dai log puoi tracciare lo stato dell'applicazione, le sue prestazioni e la salute.

Esempio dalla vita reale

Se gli aerei non avessero le «scatole nere» (log di volo), indagare sulle cause degli incidenti sarebbe quasi impossibile. In programmazione i log sono le tue scatole nere.

2. Livelli base di logging

Nel logging si usano i livelli (levels) dei messaggi. È come un semaforo: rosso — pericoloso, giallo — attenzione, verde — tutto bene.

Livelli principali (dal più «rumoroso» al più «silenzioso»):

Livello Descrizione
ERROR
Errore critico, l'applicazione non può continuare a funzionare
WARN
Avviso: qualcosa non è andato come previsto, ma il programma continua a funzionare
INFO
Messaggio informativo su eventi regolari
DEBUG
Informazioni dettagliate per il debug (visibili solo agli sviluppatori)
TRACE
Le informazioni più dettagliate — per diagnosi approfondite

Esempio:

  • Un utente non è riuscito ad accedere per password errata — questo è WARN.
  • Il database si è rotto — questo è ERROR.
  • L'applicazione è stata avviata — questo è INFO.
  • La stampa del valore di una variabile in un ciclo — questo è DEBUG o TRACE.

Come scegliere il livello

Non scrivere tutto a livello ERROR — altrimenti annegherai tra i falsi allarmi. Usa i livelli con consapevolezza: solo i guasti davvero critici dovrebbero essere errori.

3. Logging standard in Java (java.util.logging)

Java viene fornita con un sistema di logging integrato — il package java.util.logging (in breve JUL). È uno strumento nativo sempre disponibile, anche se non hai aggiunto alcuna libreria.

Classi principali:

  • Logger — la classe principale per scrivere i log.
  • Level — l'enumerazione dei livelli di logging (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST).
  • Handler — il componente che indica dove scrivere i log (file, console, ecc.).
  • Formatter — definisce il formato dei messaggi.

Esempio di utilizzo di JUL

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

public class LoggingExample {
    // Otteniamo il logger per nome della classe
    private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());

    public static void main(String[] args) {
        logger.info("L'applicazione è stata avviata");
        logger.warning("Questo è un avviso!");
        logger.severe("E questa è un errore!");

        int x = 42;
        logger.fine("Variabile di debug x=" + x); // Non viene mostrato per impostazione predefinita
    }
}

Punto importante: per impostazione predefinita JUL mostra solo i livelli INFO e superiori. Per vedere fine e altri messaggi dettagliati, occorre configurare il livello di logging.

Configurazione via file

È possibile configurare JUL tramite il file logging.properties (di solito nella cartella della JRE). Lì si può indicare:

  • Il livello minimo per il logger
  • Dove scrivere i log (file, console)
  • La formattazione dei messaggi

Esempio di riga in logging.properties:

.level=INFO

4. Librerie di logging esterne

Log4j (Apache)

Log4j è una delle librerie di logging più conosciute per Java. È flessibile e potente, supporta diversi formati, scrittura asincrona, rotazione dei file e molto altro.

Esempio di configurazione più semplice di 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>

Esempio di utilizzo di Log4j 2 nel codice:

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("Questo è un errore tramite Log4j");
    }
}

Perché funzioni, bisogna aggiungere le dipendenze di Log4j al progetto (ad esempio tramite Maven o Gradle).

SLF4J: facade di logging

SLF4J (Simple Logging Facade for Java) non è un sistema di logging a sé stante, ma uno strato di astrazione tra il tuo codice e una specifica implementazione (Log4j, Logback, JUL ecc.).

Il facade serve per scrivere il codice una sola volta e, se necessario, cambiare l'implementazione reale senza riscrivere il codice, anche se librerie diverse usano logger differenti.

Esempio di utilizzo di 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("Ciao, logging tramite SLF4J!");
        logger.warn("Attenzione: qualcosa potrebbe andare storto");
        logger.error("Errore tramite SLF4J");
    }
}

Come funziona?

  • Scrivi il codice tramite SLF4J.
  • Nel classpath aggiungi l'implementazione desiderata (per esempio, Logback o Log4j).
  • SLF4J inoltra automaticamente le chiamate all'implementazione scelta.

Integrazione di SLF4J con altri logger: SLF4J può funzionare sopra JUL, Log4j, Logback e altri, consentendo di migrare facilmente senza riscrivere il codice.

5. Pratica: confrontiamo System.out.println e il logging

Esempio 1: System.out.println

public class PrintlnExample {
    public static void main(String[] args) {
        System.out.println("L'applicazione è stata avviata");
        System.out.println("Si è verificato un errore: qualcosa è andato storto");
    }
}

Cosa non va:

  • Nessun livello di messaggio (errore, informazione, avviso — tutto è uguale).
  • Mancano orario, nome della classe, thread.
  • Tutti i messaggi finiscono in un unico calderone.
  • Non si può configurare l'output in modo flessibile (ad esempio, scrivere solo gli errori).

Esempio 2: Logging tramite 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("L'applicazione è stata avviata");
        logger.error("Si è verificato un errore: qualcosa è andato storto");
    }
}

Vantaggi:

  • Ogni messaggio è accompagnato da orario, livello, nome della classe, thread.
  • È possibile filtrare i messaggi per livello.
  • Si possono scrivere i log su file, inviarli in rete, formattarli, archiviarli.
  • Il logging è thread-safe (importante per le applicazioni multithread).

Dimostrazione della differenza

System.out.println
Logging (SLF4J/Logback)
L'applicazione è stata avviata 12:34:56 [main] INFO LoggingVsPrintln - L'applicazione è stata avviata
Si è verificato un errore: qualcosa è andato storto 12:34:56 [main] ERROR LoggingVsPrintln - Si è verificato un errore: qualcosa è andato storto

6. Come aggiungere il logging al tuo progetto

Per il logger standard (JUL)

Tutto è già nella JDK, si può usare subito:

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("Questo è un messaggio informativo");
    }
}

Per Log4j o SLF4J

  1. Aggiungi le dipendenze (ad esempio tramite Maven).

    Per Log4j 2:

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

    Per 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. Crea il file di configurazione (ad esempio, logback.xml per Logback o log4j2.xml per Log4j).
  3. Usa il logger nel tuo codice (vedi gli esempi sopra).

7. Mini-applicazione: aggiungiamo il logging

Supponiamo di avere una semplice applicazione — una calcolatrice da riga di comando. Aggiungiamo il logging.

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("Calcolatrice avviata");

        try {
            System.out.print("Inserisci il primo numero: ");
            int a = Integer.parseInt(scanner.nextLine());
            logger.debug("Primo numero inserito: {}", a);

            System.out.print("Inserisci il secondo numero: ");
            int b = Integer.parseInt(scanner.nextLine());
            logger.debug("Secondo numero inserito: {}", b);

            int sum = a + b;
            logger.info("Somma dei numeri: {}", sum);
            System.out.println("Risultato: " + sum);
        } catch (NumberFormatException ex) {
            logger.error("Errore di inserimento del numero", ex);
            System.out.println("Errore: inserisci un numero valido!");
        } catch (Exception ex) {
            logger.error("Errore sconosciuto", ex);
            System.out.println("Si è verificato un errore!");
        }
        logger.info("La calcolatrice ha terminato l'esecuzione");
    }
}

Cosa succede qui:

  • Tutti gli eventi chiave sono registrati nel log.
  • Gli errori sono loggati con livello ERROR e full stack trace.
  • Tutte le informazioni sono disponibili per l'analisi dopo l'esecuzione del programma.

8. Errori tipici nell'uso del logging

Errore n. 1: usare solo System.out.println per il debug e la diagnostica.
È comodo all'inizio, ma è del tutto inadatto per applicazioni reali. Non potrai gestire i livelli dei messaggi, non vedrai l'orario, non potrai analizzare i log se il programma gira su un server.

Errore n. 2: registrare troppa informazione a livello ERROR.
Se scrivi tutto come errore, smetterai di distinguere ciò che è davvero importante da ciò che è solo informazione di debug.

Errore n. 3: loggare dati sensibili (password, token, numeri di carta).
I log devono essere sicuri! Non scriverci dentro ciò che altri non dovrebbero vedere.

Errore n. 4: assenza di configurazione del logging.
Se non configuri livelli, formato e destinazione dei log — potresti «annegare» in gigabyte di informazioni inutili o non vedere affatto l'errore.

Errore n. 5: non usare un facade di logging (SLF4J) nei progetti di grandi dimensioni.
Se il tuo progetto crescerà e passerà da una libreria a un'altra — senza facade sarà doloroso. SLF4J consente di cambiare facilmente il «motore» di logging senza riscrivere il codice.

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