CodeGym /Cursos /JAVA 25 SELF /Fundamentos de logging: java.util.logging, Log4j, SLF4J

Fundamentos de logging: java.util.logging, Log4j, SLF4J

JAVA 25 SELF
Nível 63 , Lição 0
Disponível

1. Por que precisamos de logging

Logging ≠ saída no console

Quando você está começando a programar, parece que para encontrar problemas basta colocar System.out.println("Estive aqui!") em todo lugar. Isso funciona enquanto o programa é pequeno e roda no seu computador. Mas imagine um projeto grande, um servidor que fica no ar 24/7, ou um aplicativo usado por milhares de pessoas — você não vai ficar atrás de cada usuário olhando o console dele, certo?

Logging — não é apenas imprimir mensagens. É o registro sistematizado de informações sobre o funcionamento do aplicativo: erros, avisos, eventos de negócio, detalhes técnicos. Os logs são salvos em arquivos, bancos de dados, podem ser enviados pela rede — e permitem analisar o comportamento do programa após a execução ou em tempo real.

Para que servem os logs

  • Diagnóstico de problemas: se algo deu errado, os logs são seu principal aliado. Com eles é possível entender onde e por que o erro ocorreu.
  • Auditoria e segurança: os logs registram ações importantes dos usuários, o que ajuda a investigar incidentes.
  • Depuração: às vezes os bugs aparecem apenas em certas condições, e sem logs é impossível capturá-los.
  • Monitoramento: pelos logs é possível acompanhar o estado do aplicativo, seu desempenho e a sua “saúde”.

Exemplo da vida real

Se os aviões não tivessem “caixas-pretas” (logs de voo), investigar as causas de acidentes seria quase impossível. Em programação, os logs são as suas caixas-pretas.

2. Níveis básicos de logging

No logging é comum usar níveis (levels) de mensagens. É como um semáforo: vermelho — perigoso, amarelo — atenção, verde — tudo certo.

Níveis principais (do mais “barulhento” ao mais “silencioso”):

Nível Descrição
ERROR
Erro crítico, o aplicativo não pode continuar a execução
WARN
Aviso: algo saiu errado, mas o programa continua funcionando
INFO
Mensagem informativa sobre eventos normais
DEBUG
Informações detalhadas para depuração (visível apenas para desenvolvedores)
TRACE
Informação mais detalhada — para diagnóstico profundo

Exemplo:

  • O usuário não conseguiu entrar por senha incorreta — isso é WARN.
  • Falha no banco de dados — isso é ERROR.
  • O aplicativo iniciou — isso é INFO.
  • Imprimir o valor de uma variável em um loop — isso é DEBUG ou TRACE.

Como escolher o nível

Não escreva tudo no nível ERROR — caso contrário, você vai se afogar entre os erros reais. Use os níveis com consciência: apenas falhas realmente críticas devem ser erros.

3. Logging padrão no Java (java.util.logging)

O Java vem com um sistema de logging embutido — o pacote java.util.logging (abreviado como JUL). É a ferramenta “nativa”, sempre disponível mesmo que você não adicione nenhuma biblioteca.

Classes principais:

  • Logger — a classe principal para gravar logs.
  • Level — enumeração dos níveis de logging (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST).
  • Handler — um handler que indica para onde escrever os logs (arquivo, console etc.).
  • Formatter — responsável pelo formato das mensagens.

Exemplo de uso do JUL

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

public class LoggingExample {
    // Obtemos o logger pelo nome da classe
    private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());

    public static void main(String[] args) {
        logger.info("Aplicativo iniciado");
        logger.warning("Isto é um aviso!");
        logger.severe("E isto já é um erro!");

        int x = 42;
        logger.fine("Variável de depuração x=" + x); // Não é exibido por padrão
    }
}

Ponto importante: por padrão, o JUL exibe apenas os níveis INFO e superiores. Para ver fine e outras mensagens detalhadas, é preciso configurar o nível de logging.

Configuração via arquivo

Você pode configurar o JUL pelo arquivo logging.properties (geralmente na pasta da JRE). Lá é possível definir:

  • O nível mínimo para o logger
  • Para onde escrever os logs (arquivo, console)
  • O formato das mensagens

Exemplo de linha em logging.properties:

.level=INFO

4. Bibliotecas externas de logging

Log4j (Apache)

Log4j é uma das bibliotecas de logging mais conhecidas para Java. É flexível, poderosa, suporta diferentes formatos, gravação assíncrona, rotação de arquivos e muito mais.

Exemplo de configuração mais simples do 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>

Exemplo de uso do Log4j 2 no código:

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("Este é um erro via Log4j");
    }
}

Para isso funcionar, é preciso adicionar as dependências do Log4j ao seu projeto (por exemplo, via Maven ou Gradle).

SLF4J: fachada de logging

SLF4J (Simple Logging Facade for Java) não é um sistema de logging separado, mas sim uma “camada” entre o seu código e uma implementação específica (Log4j, Logback, JUL etc.).

A fachada existe para que você escreva o código uma vez e, se necessário, troque o sistema de logging real sem reescrever o código, mesmo que bibliotecas diferentes usem loggers diferentes.

Exemplo de uso do 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("Olá, logging via SLF4J!");
        logger.warn("Atenção: algo pode dar errado");
        logger.error("Erro via SLF4J");
    }
}

Como isso funciona?

  • Você escreve o código via SLF4J.
  • No classpath você adiciona a implementação desejada (por exemplo, Logback ou Log4j).
  • SLF4J encaminha as chamadas para a implementação escolhida.

Integração do SLF4J com outros loggers: o SLF4J pode operar sobre JUL, Log4j, Logback etc., o que facilita migrar sem reescrever o código.

5. Prática: comparando System.out.println e logging

Exemplo 1: System.out.println

public class PrintlnExample {
    public static void main(String[] args) {
        System.out.println("Aplicativo iniciado");
        System.out.println("Ocorreu um erro: algo deu errado");
    }
}

O que há de errado:

  • Não há nível de mensagem (erro, informação, aviso — tudo fica igual).
  • Não há horário, nome da classe, thread.
  • Todas as mensagens vão para o mesmo lugar.
  • Não é possível configurar a saída de forma flexível (por exemplo, registrar apenas erros).

Exemplo 2: logging com 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("Aplicativo iniciado");
        logger.error("Ocorreu um erro: algo deu errado");
    }
}

Vantagens:

  • Cada mensagem vem acompanhada de horário, nível, nome da classe e thread.
  • É possível filtrar mensagens por nível.
  • É possível escrever logs em arquivo, enviar pela rede, formatar, arquivar.
  • O logging é thread-safe (importante para aplicativos multithread).

Demonstração da diferença

System.out.println
Logging (SLF4J/Logback)
Aplicativo iniciado 12:34:56 [main] INFO LoggingVsPrintln - Aplicativo iniciado
Ocorreu um erro: algo deu errado 12:34:56 [main] ERROR LoggingVsPrintln - Ocorreu um erro: algo deu errado

6. Como adicionar logging ao seu projeto

Para o logger padrão (JUL)

Tudo já está na JDK; você pode usar imediatamente:

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("Esta é uma mensagem de informação");
    }
}

Para Log4j ou SLF4J

  1. Adicione as dependências (por exemplo, via Maven).

    Para Log4j 2:

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

    Para 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. Crie o arquivo de configuração (por exemplo, logback.xml para o Logback ou log4j2.xml para o Log4j).
  3. Use o logger no seu código (veja os exemplos acima).

7. Miniaplicativo: adicionando logging

Suponha que temos um aplicativo simples — uma calculadora de linha de comando. Vamos adicionar logging a ele.

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("Calculadora iniciada");

        try {
            System.out.print("Digite o primeiro número: ");
            int a = Integer.parseInt(scanner.nextLine());
            logger.debug("Primeiro número digitado: {}", a);

            System.out.print("Digite o segundo número: ");
            int b = Integer.parseInt(scanner.nextLine());
            logger.debug("Segundo número digitado: {}", b);

            int sum = a + b;
            logger.info("Soma dos números: {}", sum);
            System.out.println("Resultado: " + sum);
        } catch (NumberFormatException ex) {
            logger.error("Erro ao digitar o número", ex);
            System.out.println("Erro: digite um número válido!");
        } catch (Exception ex) {
            logger.error("Erro desconhecido", ex);
            System.out.println("Ocorreu um erro!");
        }
        logger.info("Calculadora finalizou a execução");
    }
}

O que está acontecendo aqui:

  • Todos os eventos-chave são registrados no log.
  • Os erros são registrados no nível ERROR com o stack trace completo.
  • Todas as informações ficam disponíveis para análise após a execução do programa.

8. Erros comuns ao trabalhar com logging

Erro nº 1: Usar apenas System.out.println para depuração e diagnóstico.
Isso é conveniente no início, mas não serve para aplicativos reais. Você não conseguirá controlar o nível das mensagens, não verá o horário e não poderá analisar os logs quando o programa estiver rodando no servidor.

Erro nº 2: Registrar informação demais no nível ERROR.
Se você marcar tudo como erro, deixará de distinguir o que é realmente importante do que é apenas informação para depuração.

Erro nº 3: Registrar dados sensíveis (senhas, tokens, números de cartão).
Os logs devem ser seguros! Não escreva neles o que outras pessoas não devem ver.

Erro nº 4: Falta de configuração do logging.
Se você não configurar níveis, formato e local de armazenamento dos logs, pode acabar “se afogando” em gigabytes de informação desnecessária ou nem enxergar um erro.

Erro nº 5: Não usar uma fachada de logging (SLF4J) em projetos grandes.
Se o seu projeto crescer e você precisar mudar de uma biblioteca para outra, sem uma fachada isso será doloroso. SLF4J permite trocar o “motor” de logging facilmente sem reescrever o código.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION