Back-End

26 nov, 2012

Usando Log4J em projetos Java

Publicidade

Olá, pessoal! Tudo bem? Bom, no artigo de hoje vamos ver como usar o LOG4J em projetos Java. O uso de log em software é algo extremamente importante, principalmente para sabermos o que está acontecendo com o sistema. Sentimos a importância dos logs principalmente quando precisamos corrigir um bug. Só então percebemos o quanto é importante ter um sistema de LOG eficiente. É claro que o vai depender do framework/ API que você usar, pois o sistema de LOG não faz milagre por si só – depende dos desenvolvedores implementarem os logs corretos, de acordo com o tipo da mensagem que devemos logar. Identificar o que deve e não deve ser logado é extremamente importante.

O uso de uma API de LOG é extremamente importante, principalmente durante o desenvolvimento, pois, mais cedo ou mais tarde, vamos precisar analisar o LOG por algum motivo. Agumas situações bem comuns nas quais precisamos de um LOG:

  • Sua aplicação conecta à banco de dados e este está down. Com certeza verá ‘communication link failure’ como exceção e uma ‘connection refused’. Para que tudo isso não apareça na tela do usuário, mandamos para um arquivo de log e lá vamos investigar quando e que dia aquilo aconteceu;
  • Sua aplicação vai ler um arquivo que é criado a partir de outra aplicação. Se não encontrar o arquivo, teremos uma exceção e a mandaremos para o arquivo de log. Só então vamos saber o dia e o horário em que a outra aplicação parou de gerar aquele tal arquivo. Depois disso poderemos iniciar uma investigação.

Claro que há vários contextos do uso de LOG, os citados acima foram apenas umas das situações mais tradicionais.

Entendendo Log4J

Para entender o framework, é preciso saber apenas três aspectos: logger, appender, layout.

  • Logger: é o que recebe as solicitações de log. Podemos criar um logger para cada classe da aplicação, porém o framework já fornece uma padrão, caso não seja informado nenhum.
  • Appender: os loggers precisam saber para quem enviar as informações que recebeu, e é o appender que faz esse trabalho, dizendo: “logger, passe as informações que você recebeu para mim, que eu saberei o que fazer com elas”. Com o appender podemos decidir, salvar as informações de logger em um arquivo, imprimir no console, enviar via e-mail etc.
  • Layout: precisamos definir o formato no qual essas informações serão armazenadas. Para isso precisamos de layout. Podemos, organizar em um formato HTML, Simple text etc. Com o layout podemos definir data, hora e linha onde o log foi gerado etc.

 Níveis de logger

Todo logger possui um dos cinco níveis disponíveis: DEBUG, INFO, WARN, ERROR e FATAL. Por padrão, o DEBUG é configurado pelo framework – caso você não defina nenhum.

Os níveis vão ajudar a saber que tipo de informação deseja gravar no seu LOG, por exemplo: você define que apenas os erros iguais ou maiores que WARN serão salvo no seu arquivo LOG. Isso é importante, pois às vezes não importa ter DEBUG, ou INFO em seu arquivo de LOG, quando ele está em produção. Para o team de QA, apenas interessa a partir de INFO, por exemplo.

Mas, você pode criar dois arquivos de LOG: um para DEV e lá você põe todas as info desde DEBUG e outro para o log que vai junto com aplicação a partir de INFO. O framework permite esta flexibilidade de mandarmos diferentes níveis para arquivos diferentes.

A ordem: DEBUG <  INFO < WARN < ERROR < FATAL.

Leitura simples: se você configura para WARN, só será enviadas mensagens igual ou acima de WARN. Quem é maior que WARN? (ERROR)

  • Segundo passo: Adicionar ao projeto Java (ou crie um projeto Java)

O nosso projeto Java vai salvar um dado no banco via Hibernate, pois iremos simular um erro e ver se este dado consta no arquivo de log.

  • Terceiro passo: A configuração de um arquivo do LOG4J pode ser feita de forma programada ou através de um arquivo de properties. Vamos mostrar aqui ambos. Primeiro veremos a configuração do arquivo de properties, onde os comentários já possuem as devidas explicações:
#### Usando 2 appenders, 1 para logar no console, outro para um arquivo
log4j.rootCategory=WARN,stdout,fileOut

# Imprime somente mensagens com 'priority' WARN ou mais alto para o logger
#lembrando a ordem: DEBUG - INFO - WARN - ERROR - FATAL
log4j.category.lopes=INFO

#### O primeiro appender escreve no console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
### Pattern que mostra o nome do arquivo e numero da linha, porem sem data e hora
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

#### O segundo appender escreve em um arquivo e faz um bkp ao atingir o max 
log4j.appender.fileOut =org.apache.log4j.RollingFileAppender
log4j.appender.fileOut.File=camilolog.log
### Controla o tamanho maximo do arquivo
log4j.appender.fileOut.MaxFileSize=100KB
### Faz backup dos arquivos de log (apenas 1)
log4j.appender.fileOut.MaxBackupIndex=1
log4j.appender.fileOut.layout=org.apache.log4j.PatternLayout
#####este formato esta em ingles: 2011-04-24 e imprime o nro da linha L
log4j.appender.fileOut.layout.ConversionPattern=%d [%t] %5p %c:%L - %m%n
####imprime no formato dia/mes/ano 
#log4j.appender.fileOut.layout.ConversionPattern=%-2d{dd/MM/yy HH:mm} [%t] %5p %c:%L - %m%n

O interessante nesta configuração é a linha :log4j.category.lopes=INFO. Nela eu criei meu LOGGER e disse que ele teria o nível INFO, mas agora preciso dizer em que classe este logger será utilizado. Vamos usar a classe que tem o main.

public static void main(String[] args) {
  Logger logger = Logger.getLogger("lopes");
  logger.info("iniciando aplicação");
  logger.debug("debug here");
   Usuario usuario = new Usuario();
   usuario.setNome("camilo");
   usuario.setEmail("log4j");
   UsuarioDao usuarioDao = new UsuarioDao();
   usuarioDao.save(usuario);
  logger.info("usuario salvo no banco com sucesso");

Observe o nome que passei para o método getLogger(): o mesmo que definimos no arquivo properties, assim ele vai saber o nível a ser configurado, nesse caso foi INFO; então nada de debug é gravado no arquivo.

Note que eu habilitei o Hibernate para imprimir a instrução SQL, via Log4j.

#configurando o hibernate no log4j 
log4j.category.org.hibernate.SQL=DEBUG

Detalhes: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/session-configuration.html#configuration-logging

Na imagem abaixo temos o resultado do console:

Aqui temos o console e o que foi gravado no arquivo de log (camilolog.log):

Vamos provocar uma exceção e deixar o banco de dados (no meu caso MySQL) down. Para isso, basta dar um stop em seu banco:

Assim conseguimos ver o erro de conexão no arquivo de LOG.

Claro que não vamos fazer isso para cem classes que tivermos em um projeto, apenas para aquelas que precisam receber um tratamento diferenciado, que precisam ir para o LOG. As demais ficam com o nível default definido no rootCategory.

Nomeação do logger

Não sei se perceberam, mas há uma desvantagem em criarmos qualquer nome para o logger, como “lopes”, pois o resultado é: 2011-04-24 21:45:17,578 [main]  INFO lopes:16 – iniciando aplicação

Quem é lopes? O melhor é colocar o path da classe, assim:

log4j.category.br.com.camilolopes.dao=INFO

E precisamos mudar nossa variavel logger, passando apenas a classe:

Logger logger = Logger.getLogger(DAO.class);

Ao executar, teremos o seguinte resultado:

Observe que agora sabemos de onde vem INFO, pois temos a classe e a linha. Para isso, alteramos o ConvertionPattern que grava no arquivo:

log4j.appender.fileOut.layout.ConversionPattern=%d [%t] %5p %F:%L - %m%n

Trocamos o %c por %F.  Dê uma olhada na documentação, pois tem muita coisa bacana!

O resultado:

Não esqueça de dar um refresh, em seu projeto no Eclipse, para que o arquivo de LOG apareça, um atalho é selecionar o projeto e apertar F5.

Bom, fico por aqui e espero que tenham gostado do artigo!