Exemplo de Tutorial Log4j2 – Configuração, Níveis, Appenders

Bem-vindo ao Tutorial de Exemplo do Apache Log4j2. Se perguntar a um desenvolvedor especializado sobre a coisa mais irritante em uma aplicação, a resposta pode estar relacionada ao registro. Se não houver um registro adequado em uma aplicação, a manutenção será um pesadelo. A maioria das aplicações passa por testes de desenvolvimento, testes de unidade, testes de integração. Mas quando se trata de produção, você sempre enfrentará cenários e exceções únicos. Portanto, a única maneira de descobrir o que aconteceu em um caso específico é depurar através dos registros. Muitos frameworks fornecem alguma forma de registro padrão, mas sempre é melhor seguir o mecanismo de registro padrão da indústria. O Apache Log4j é um dos frameworks de registro mais amplamente utilizados. O Apache Log4j 2 é a próxima versão, que é muito melhor que o Log4j.

Tutorial de Exemplo do Log4j

Neste Tutorial de Exemplo do Log4j2, você aprenderá como começar com o Apache Log4j2. Também exploraremos a arquitetura do Log4j2, a configuração do log4j2, os níveis de registro do log4j2, os anexadores, os filtros e muito mais.

  1. Visão Geral do Log4j2
  2. Arquitetura do Log4j2
  3. Configuração do Log4j2
  4. Níveis do Log4j2
  5. Consultas do Log4j2
  6. Anexadores do Log4j2
  7. Filtros do Log4j2
  8. Layouts do Log4j2
  9. Qual nível do Log4j2 você deve usar
  10. Resumo do Tutorial do Log4j2

Visão Geral do Log4j2

O uso da API de registro em um aplicativo não é um luxo, é uma necessidade. Log4j é uma biblioteca de código aberto publicada e licenciada sob a Apache Software. Você pode depurar um aplicativo usando o Eclipse Debugging ou algumas outras ferramentas, mas isso não é suficiente e viável em um ambiente de produção. O mecanismo de registro fornecerá vários benefícios que você não encontrará na depuração normal.

Category / Operation (Debugging, Logging) Debugging Logging
Human Intervention There’s a need for human intervention No need for human intervention
Persistent Medium Can’t be integrated with persistent storage Can be integrated with persistent storage (Files, Database, NoSQL database, etc.)
May used for Auditing Can’t be used for achieving auditing Can be used for achieving auditing if it’s used efficiently
Sufficient for complicated structure and flow Not sufficient; you may get lost with flow. Sufficient
Productivity Less productive More productive

Como você pode ver acima, o uso do mecanismo de registro será mais eficiente com menor custo de manutenção. O Apache Log4j é a ferramenta líder para registro em aplicativos Java, então você deve usá-lo.

Arquitetura do Log4j2

Antes de avançarmos para o tutorial do Exemplo Log4j, é bom dar uma olhada na arquitetura do Log4j2. A imagem abaixo mostra as classes importantes na API do Log4j2. Aqui está a explicação detalhada para a arquitetura mostrada acima:

  • As aplicações solicitarão ao LogManager um Logger com um nome específico.

  • O LogManager localizará o LoggerContext apropriado e então obterá o Logger dele.

  • Se o Logger ainda não foi criado, ele será criado e associado ao LoggerConfig de acordo com três opções abaixo:

    1. A instância do Logger será criada e associada ao LoggerConfig que possui o mesmo nome. Por exemplo, a classe App em getLogger(App.class) será avaliada como uma String com.journaldev.App. O nome do LoggerConfig é idêntico ao nome da classe totalmente qualificada (Componente de software).
    2. A instância do Logger será criada e associada ao LoggerConfig que possui o mesmo pacote pai dos Loggers. Por exemplo, com.journaldev em getLogger("com.journaldev")
    3. A instância do Logger será criada e associada ao LoggerConfig Raiz. O LoggerConfig Raiz será usado quando não houver arquivo de configuração ou quando você estiver obtendo um logger com um nome não definido nas declarações do logger.
  • Objetos LoggerConfig são criados a partir da declaração do Logger no arquivo de configuração. O LoggerConfig também é usado para lidar com LogEvents e delegá-los para seus Log4j2 Appenders definidos.

  • O logger raiz é um caso excepcional, em termos de sua existência. Ele sempre existe e está no topo de qualquer hierarquia de loggers.

  • Você pode obter o logger raiz usando as seguintes instruções:

    Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    Logger logger = LogManager.getRootLogger();
    
  • O nome dos loggers do log4j2 são sensíveis a maiúsculas e minúsculas.

  • Exceto pelo logger raiz, todos os loggers podem ser obtidos passando seu nome para LogManager.getLogger().

  • LoggerContext é um ponto vocal para o sistema de registro, pois você pode ter vários LoggerContexts dentro de sua aplicação. Para cada LoggerContext, uma configuração ativa deve ser definida.

  • A configuração do Log4j2 contém todos os ativos do sistema de registro; LoggerConfig(s), Appender(s), Filter(s) e muitos outros.

  • A chamada de LogManager.getLogger() passando o mesmo nome sempre retornará a referência para a mesma instância exata do logger.

  • A configuração do sistema de registro é tipicamente feita com a inicialização da aplicação. Isso pode assumir diferentes formas; programaticamente ou lendo um arquivo de configuração do log4j2.

Cada registrador está associado a um objeto LoggerConfig, conjunto de objetos LoggerConfig compõe uma Hierarquia de registradores. Este conceito é conhecido como Hierarquia de Registradores. Hierarquia de Registradores é composta por conjunto de objetos LoggerConfig com uma relação pai-filho. O elemento mais alto em cada Hierarquia de Registradores é o Registrador Raiz. Se o Log4j2 não encontrar o arquivo de configuração, apenas o Registrador Raiz será usado para registrar com o nível de registro como ERRO. A mensagem de aviso que você receberá nesse caso é mostrada na imagem abaixo. Erro StatusLogger Nenhum arquivo de configuração do log4j2 encontrado. Usando configuração padrão: registrando apenas erros no console. A tabela abaixo mostra a relação pai-filho na Hierarquia de Registradores.

LoggerConfig (Is A) Root com com.journaldev com.journaldev.logging
Root X Child descendant descendant
com Parent X Child descendant
com.journaldev Ancestor Parent X Child
com.journaldev.logging Ancestor Ancestor Parent X

Para esclarecer a relação pai-filho, a tabela acima seria lida da seguinte forma:

  • Root é pai de com.
  • Root é ancestral de com.journaldev.
  • Root é ancestral de com.journaldev.logging.
  • com é filho de Root.
  • com é pai de com.journaldev.
  • com é ancestral de com.journaldev.logging.
  • com.journaldev.logging é filho de com.journaldev e assim por diante.

Uma instância de LoggerConfig é considerada um ancestral de outra LoggerConfig se o seu nome seguido de um ponto for um prefixo do nome descendente. Uma instância de LoggerConfig é considerada um pai para outra LoggerConfig se não houver nomes intercalados entre ambas.

Configuração do Log4j2

Há muitas maneiras de usar a configuração do Log4j2 em sua aplicação.

  1. Usando um arquivo de configuração escrito em XML, JSON, YAML ou arquivo de propriedades.
  2. Programaticamente, criando uma fábrica de configuração e implementação de configuração.
  3. Programaticamente, chamando APIs expostas na interface de configuração.
  4. Programaticamente, chamando métodos na classe de logger interna.

Vamos focar principalmente no arquivo de configuração. No entanto, é bom conhecer também a abordagem de programação, caso queira configurar uma estratégia de logging específica para algum Logger específico. Primeiro, vamos considerar o caso em que você não forneceu um arquivo de configuração. A implementação do Log4j2 assume que existe uma variável de Sistema chamada log4j.configurationFile para apontar a localização do arquivo de configuração do log4j2.

package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App
{
    public static void main( String[] args ) {
    	Logger logger = LogManager.getRootLogger();
    	logger.trace("Configuration File Defined To Be :: "+System.getProperty("log4j.configurationFile"));
    }
}

A simple log4j2 configuration file will look like below. configuration.xml:

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

E aqui está a explicação detalhada para o código listado acima:

  • O aplicativo referenciou o logger raiz chamando o método getRootLogger do LogManager.
  • A referência ao logger do LogManager iniciou o sistema Log4j.
  • Log4j irá inspecionar a propriedade do sistema log4j.configurationFile para determinar o arquivo de configuração do log4j2. A configuração do Log4j pode ser escrita em JSON, YAML e XML.
  • Podemos definir a propriedade do sistema log4j.configurationFile através de System.setProperties("log4j.configurationFile","CAMINHO_DO_ARQUIVO") ou passando-a como um parâmetro JVM como mostrado na figura abaixo. Observe também o prefixo do protocolo File.

  • No caso de nenhuma propriedade do sistema estar definida a ordem de configuração segue a seguinte precedência:
    • A Configuração da Propriedade irá procurar por log4j2-test.properties no classpath.
    • A Configuração YAML irá procurar por log4j2-test.yaml ou log4j2-test.yml no classpath.
    • A Configuração JSON irá procurar por log4j2-test.jsn ou log4j2-test.json no classpath.
    • A Configuração XML irá procurar por log4j2-test.xml no classpath.
    • A Configuração da Propriedade irá procurar por log4j2.properties no classpath.
    • A Configuração YAML irá procurar por log4j2.yml ou log4j2.yaml no classpath.
    • A Configuração JSON irá procurar por log4j2.jsn ou log4j2.json no classpath.
    • A Configuração XML irá procurar por log4j2.xml no classpath.
    • Se nenhum arquivo de configuração foi fornecido, a Configuração Padrão será utilizada e isso levará a um conjunto de comportamentos padrão:
      • O logger Raiz será utilizado.
      • O nível do logger Raiz será definido como ERROR.
      • O logger Raiz propagará mensagens de log para o console.
      • O PatternLayout é configurado como %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n

O uso do arquivo de configuração log4j2 torna a configuração do log4j2 tão simples, mas vamos ver como podemos configurá-lo programaticamente. Isso é tudo sobre o uso do ConfigurationFactory.

package com.journaldev;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
public class App
{
    public static void main( String[] args ) throws FileNotFoundException, IOException {
 
    	// Obtenha uma instância da fábrica de configuração; suas opções são Default ConfigurationFactory, XMLConfigurationFactory,
    	// YamlConfigurationFactory & JsonConfigurationFactory
    	ConfigurationFactory factory =  XmlConfigurationFactory.getInstance();
 
    	// Localize a origem desta configuração, este arquivo localizado é um arquivo fictício contendo apenas uma Tag de configuração vazia
    	ConfigurationSource configurationSource = new ConfigurationSource(new FileInputStream(new File("C:/dummyConfiguration.xml")));
 
    	// Obtenha uma referência da configuração
    	Configuration configuration = factory.getConfiguration(configurationSource);
 
    	// Crie um apêndice padrão de console
    	ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
 
    	// Adicione o apêndice de console na configuração
    	configuration.addAppender(appender);
 
    	// Crie LoggerConfig
    	LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
 
    	// Adicione o apêndice
    	loggerConfig.addAppender(appender,null,null);
 
    	// Adicione o logger e associe-o com a instância do LoggerConfig
    	configuration.addLogger("com", loggerConfig);
 
    	// Obtenha uma instância de contexto
    	LoggerContext context = new LoggerContext("JournalDevLoggerContext");
 
    	// Inicie o sistema de registro de log
    	context.start(configuration);
 
    	// Obtenha uma referência para o logger
    	Logger logger = context.getLogger("com");
 
    	// LogEvent de mensagem DEBUG
    	logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
 
    	// LogEvent de mensagem de erro para o Logger configurado como FATAL
    	logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
 
    	// LogEvent de mensagem de ERRO que será tratada por Root
    	logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
    }
}
  • Você pode usar qualquer ConfigurationFactory fornecida pelo Log4j2 ou usar a padrão. Usamos XMLConfigurationFactory para obter uma instância de ConfigurationFactory.
  • A fábrica fornecerá uma instância da referência de Configuração necessária passando o arquivo de configuração correspondente.
  • A instância de Configuração será usada em conjunto com LoggerContext para iniciar o Sistema de Registro de Log.
  • A console Appender has been configured and added into configuration instance with default layout. This Appender would print out messages into your console.
  • A instância de LoggerConfig foi criada com o nome, NÍVEL e sem filtro fornecidos. O Apêndice Criado será atribuído a esta instância de LoggerConfig.
  • A instância de LoggerConfig foi adicionada à instância de configuração.
  • A new instance of LoggerContext is created with defined name.
  • A instância de configuração foi passada para a instância LoggerContext e invocou o start nesta última.
  • A logger instance has been acquired from LoggerContext. This logger instance will be used to fire set of Log events.
  • A instância de Logger disparou três eventos que serão explicados na seção Níveis do Log4j2.
  • O logger com foi configurado para imprimir mensagens cujos níveis são FATAIS.
  • Por padrão, o logger Root está configurado para imprimir mensagens cujos níveis são ERRO.
  • As mensagens de ERRO não serão registradas pelo logger ‘com’ porque seu nível é FATAL.

A mesma configuração pode ser feita usando YAML, JSON ou arquivo de propriedades. No entanto, a configuração do arquivo de propriedades do log4j2 é diferente do arquivo de propriedades do log4j, portanto, certifique-se de não tentar usar a configuração do arquivo de propriedades do log4j com o log4j2. Isso resultará no seguinte erro;

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.

Ao processar o código acima, você obterá a seguinte saída:

Logger Name :: com :: Passed Message ::
00:01:27.705 [main] ERROR - Root Logger:: Passed Message As Root Is Configured For ERROR Level messages

A primeira linha de logs é do logger com e a segunda é do Root Logger. A mensagem de erro do logger com não é impressa porque seu nível é Fatal.

Níveis do Log4j2

Você pode ver nos exemplos de código acima que toda vez que definimos um LoggerConfig, também fornecemos o nível de registro. Por padrão, o registro do log4j2 é aditivo. Isso significa que todos os loggers pai também serão usados quando um logger específico for usado. A imagem abaixo esclarece essa situação. E aqui estão os pontos de esclarecimento para isso:

  • Como mencionamos anteriormente, cada logger tem associada uma instância de LoggerConfig. Este LoggerConfig foi definido no escopo de configuração.
  • O nível de registro pode ser determinado no escopo do LoggerConfig.
  • Você pode obter o logger pelo seu nome, pacote pai ou apontando diretamente para o Root Logger.
  • O Root Logger é o nó de nível superior para toda hierarquia de LoggerConfig.
  • Uma vez que você obtenha o logger com nome `com.journaldev` e inicie um logEvent para registro, o loggerConfig (net.journaldev) registrará a mensagem e a mensagem será propagada também na hierarquia sem respeitar os níveis de registro dos pais. Assim, o evento de log será propagado para os loggers com e Root e eles também registrarão a mensagem respectivamente de acordo com os níveis definidos.
  • Uma vez que você obtenha o logger com nome `com` e inicie um logEvent para registro, o loggerConfig(com) registrará a mensagem e a mensagem será propagada também na hierarquia sem respeitar os níveis de registro dos pais. Ou seja, o Root logger será propagado para o evento de log e ele também registrará a mensagem. O mesmo caso para a hierarquia net.journaldev.
  • O mesmo caso se aplica à hierarquia net.journaldev.
  • Nas próximas seções, adicionaremos mais esclarecimentos sobre o conceito aditivo.
  • Há uma chance de o pai ignorar a mensagem usando o conceito de filtro ou definindo o indicador aditivo como falso, de modo que os eventos de log não sejam propagados aos pais.
  • Há uma chance de o logger ignorar a mensagem se o nível do loggerConfig correspondente for MAIOR do que o nível dos eventos de log.

Agora, vejamos o exemplo associado ao conceito de aditividade explicado acima:

import net.NetApp;
import net.journaldev.NetJournalDevApp;
import com.ComApp;
import com.journaldev.ComJournalDevApp;
public class Main {
	public static void main(String [] args){
		new ComApp();
		new ComJournalDevApp();
		new NetApp();
		new NetJournalDevApp();
	}
}
package com.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ComJournalDevApp {
	public ComJournalDevApp(){
		Logger logger = LogManager.getLogger(ComJournalDevApp.class);
		logger.trace("COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::");
	}
}
package net;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetApp {
	public NetApp(){
		Logger logger = LogManager.getLogger(NetApp.class);
		logger.error("NET :: LEVEL :: NetApp ERROR Message ::");
	}
}
package net.journaldev;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NetJournalDevApp {
	public NetJournalDevApp(){
		Logger logger = LogManager.getLogger(NetJournalDevApp.class);
		logger.error("NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::");
	}
}

Considerando que o arquivo de configuração log4j2 se parece com o abaixo:

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

Se você executar a classe principal, encontrará os resultados abaixo:

10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.168 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.170 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
10:34:47.171 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

Aqui está a explicação detalhada para o código listado acima:

  • O arquivo de configuração contém cinco instâncias de loggerConfig definidas e elas são Root, com, com.journaldev, net e net.journaldev. Assim como a Hierarquia de Logger mostrada acima.
  • O nível do Root está configurado para ser ERROR, e esse é na verdade o valor padrão.
  • Os níveis de com e com.journaldev estão configurados para serem TRACE.
  • Os níveis de net e net.journaldev estão configurados para serem ERROR.
  • Você pode ter notado que as mensagens dos loggers ComAPP e ComJournalDevApp foram mostradas duas e três vezes, respectivamente. Essas mensagens são mostradas de acordo com a Hierarquia de Logger para ComApp e ComJournalDevApp, onde eles estão nos pacotes com e com.journalDev, respectivamente. Temos um caso semelhante com as classes NetApp e NetJournalDevApp.
  • Os pais são propagados porque o indicador aditivo está definido como verdadeiro por padrão.

O Logging Space leva em consideração os Níveis de eventos de log e o nível do loggerConfig, além da Hierarquia do Logger.

Então, e se mudarmos o LoggerConfig para com ser INFO e deixarmos o programa inteiro como está:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="INFO">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

Então, o resultado seria como abaixo:

11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.305 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.307 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
11:08:10.308 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
  • Com certeza, você pode notar que o evento de log ComAPP foi ignorado e isso ocorre devido ao nível definido do loggerConfig para o pacote com. O nível INFO (400) é menor que o nível do evento de log, que aqui é TRACE (600). Portanto, a mensagem de ComApp não será mostrada mais e, para exibi-la, você precisa modificar o nível do LoggerConfig para com ser TRACE (600) ou ALL (Integer.MAX_VALUE).

Para garantir que os eventos de log tenham sido exibidos, o Nível do LoggerConfig deve ser maior ou igual ao nível do evento de log.

A tabela abaixo mostra os Níveis do log4j2 e o peso para cada um deles:

LEVEL Weight
OFF 0
FATAL 100
ERROR 200
WARN 300
INFO 400
DEBUG 500
TRACE 600
ALL Integer.MAX_VALUE

Com certeza, a tabela acima esclarece muito mais do que palavras e ela lhe dá a principal causa para o evento de log TRACE não ser exibido enquanto o nível do LoggerConfig é INFO.

Observe que a propagação dos eventos de log na hierarquia do logger está além deste cálculo e ignora os níveis.

Mas o que acontece se removermos o LoggerConfig de com.journaldev da configuração e adicionarmos um novo para com.journaldev.logging para que o arquivo de configuração fique assim:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

Você pode achar a figura abaixo mais conveniente para entender o que aconteceu na configuração log4j2 acima. Aqui está uma explicação para a figura mostrada acima e como isso pode afetar o comportamento dos eventos de registro:

  • Quando eventos de registro são lançados por um Logger chamado com.journaldev.logging, o LoggerConfig associado a esse nome (ou seja, com.journaldev.logging) é usado para lidar com ele e imprimir a mensagem.
  • Como o atributo aditivo do LoggerConfig com.journaldev.logging está definido por padrão como true, o evento de log é propagado para o pai, que neste caso se refere a com.journaldev.
  • Como o LoggerConfig com.journaldev não está definido na configuração, nenhuma ação ocorre e o evento de log será propagado até os instâncias do LoggerConfig com e então Root.
  • Com & Root receberão o evento de log e o imprimirão, independentemente do nível com que foi enviado.

Como resultado dos pontos mencionados, você verá as seguintes saídas:

14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.634 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.636 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.637 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.638 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:08:37.640 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

E você pode notar o seguinte:

  • O evento de log no pacote com é mostrado duas vezes. Uma para com e outra para Root.
  • Evento de registro em com.journaldev foi mostrado duas vezes. Uma para com e a segunda para Root. Mesmo que tenha sido três vezes antes, mas por agora o LoggerConfig de com.journaldev está ausente e assim nenhum registro pode ter acontecido no pacote com.journaldev e o evento seria propagado para com e Root.
  • Evento de registro em com.journaldev.logging foi mostrado três vezes, uma para o pacote com.journaldev.logging e a segunda para com e a terceira para Root. De acordo com a propagação da hierarquia do Logger, deveria ser exibido quatro vezes, mas devido à ausência de LoggerConfig com.journaldev, ele é exibido três vezes.

No caso de você ter definido uma instância de LoggerConfig com.journaldev com nenhum Nível especificado, ela herdará o Nível de seu pai.

Mas e se você tiver definido LoggerConfig com.journaldev em seu arquivo de configuração e esquecido de especificar o nível de LoggerConfig. Felizmente, o conceito de Hierarquia de Logger irá salvá-lo aqui e com.journaldev herdaria seu valor de nível de seu pai. Abaixo está um exemplo de arquivo de configuração seguido pela tabela de nível de registro de cada configuração de logger.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>
Logger Name Assigned LoggerConfig LoggerConfig Level Logger Level
Root Root ERROR ERROR
com com TRACE TRACE
com.journaldev com TRACE TRACE
com.journaldev.logging com.journaldev.logging TRACE TRACE
  • O pacote com.journaldev.logging já está associado a um LoggerConfig com o nível de registro TRACE.
  • O pacote com.journaldev já está associado a um LoggerConfig sem nível de registro especificado, então ele herdaria o Nível de Registro de seu Pai e com certeza o valor seria TRACE para o pacote com.
  • O pacote com já está associado a Loggerconfig com o nível de registro TRACE.
  • Por padrão, Root tem ERROR como nível de registro.
  • No caso de o pacote com não ser declarado, LoggerConfig com.journaldev herdará o nível de registro de Root.

Abaixo está o resultado da execução enquanto com.journaldev herda o nível de log com:

14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.419 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.421 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.422 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:41:37.423 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

E abaixo está o resultado se você remover a declaração LoggerConfig para o pacote com:

14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.809 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.811 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::
14:43:28.812 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

Você pode notar que nenhuma mensagem foi registrada para com e com.journaldev, abaixo estão os motivos.

  • A exclusão do LoggerConfig associado ao pacote com fará com que todos os eventos de log mencionados nesse pacote sejam ignorados.
  • Já que não há LoggerConfig definido para o pacote com na configuração, o LoggerConfig associado ao com.journaldev herdará o Nível de Log de seu pai. Com não está definido, e a Hierarquia de Log é alcançada até o Topo e agora está se referindo ao Root. O nível de log raiz é ERROR(200) e o nível de evento de log em com.journaldev é TRACE(600) – Veja ComJournalDevApp – e de acordo com a equação definida anteriormente, o nível de LoggerConfig deve ser maior ou igual ao evento de Log e isso é falso, então nenhuma mensagem seria exibida aqui para com.journaldev.

Por último, mas não menos importante, a seguir a Tabela mostra todos os cenários de registro possíveis que você pode enfrentar ao usar o sistema de Registro:

X (N/A) LoggerConfig Level OFF(0) FATAL(100) ERROR(200) WARN(300) INFO(400) DEBUG(500) TRACE(600) ALL(MAX)
Event Level X X X X X X X X X
OFF(0) X YES NO NO NO NO NO NO NO
FATAL(100) X NO YES YES YES YES YES YES YES
ERROR(200) X NO NO YES YES YES YES YES YES
WARN(300) X NO NO NO YES YES YES YES YES
INFO(400) X NO NO NO NO YES YES YES YES
DEBUG(500) X NO NO NO NO NO YES YES YES
TRACE(600) X NO NO NO NO NO NO YES YES
ALL(MAX) X NO NO NO NO NO NO NO YES
  • Não há um método direto que possa ser usado para lançar eventos de log OFF/ALL.
  • Principalmente, para lançar eventos de log OFF/ALL, você pode usar logger.log(Level.OFF, “Msg”) ou logger.log(LEVEL.ALL, “Msg”), respectivamente.
  • O método de log é responsável por lidar com o evento de log de acordo com a equação mencionada.

A equação de manipulação diz: Se o Nível do LoggerConfig for maior ou igual ao nível do evento de log, o evento será aceito para processamento adicional.

O evento de registro seria aceito para processamento adicional – isso é tão importante porque você tem a capacidade de evitar que algum evento seja manipulado mesmo que seja aceito usando Filtros do Log4j2. Você pode definir a propriedade aditiva como falsa para evitar a propagação do evento de registro para os loggers pais. Seguindo abaixo o mesmo exemplo que você viu antes, mas desta vez com um atributo de aditividade, para que você possa notar a diferença.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="Console"/>
    </Root>
  	<logger name="com" level="TRACE" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="com.journaldev.logging" level="TRACE" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net" level="ERROR" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  	<logger name="net.journaldev" level="ERROR" additivity="false">
  		<AppenderRef ref="Console"/>
  	</logger>
  </Loggers>
</Configuration>

E o resultado da execução seria como abaixo:

17:55:30.558 [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
17:55:30.560 [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
17:55:30.561 [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
17:55:30.561 [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
17:55:30.562 [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

E você pode notar que não há propagação de eventos de registro para os loggers pais.

Log4j2 Lookups

Idealmente, você pode definir lookups como uma maneira de passar valores para o arquivo de configuração do Log4j2. O Log4j2 fornece um conjunto diferente de Lookups que podem ser usados independentemente para definir valores de diferentes contextos:

  • Lookup do Mapa de Contexto
  • Lookup de Data
  • Lookup de Ambiente
  • Lookup Java
  • Lookup JNDI
  • Lookup de Argumento de Entrada da JVM (JMX)
  • Lookup de Argumentos Principais
  • Lookup de Mapa
  • Lookup de Dados Estruturados
  • Lookup de Propriedades do Sistema
  • Lookup da Web

Pode consultar a documentação do Log4j2 para obter mais detalhes sobre todos os tipos de pesquisas, mas vamos ver alguns exemplos aqui para cobrir o básico da Pesquisa do Log4j2. Pesquisa de Ambiente representa a maneira pela qual você pode passar um valor de ambiente (seja pelo Linux etc/profile, ambiente do sistema Windows ou scripts de inicialização para a aplicação. Como a maioria de nós sabe, temos a capacidade de definir um conjunto de valores ambientais para a aplicação usar. Vamos ver as maneiras mais famosas de definir suas variáveis ambientais.

  1. Defina a variável de ambiente usando a facilidade de ambiente do Windows:
    • Clique com o botão direito no ícone do seu computador e selecione propriedades. A página inicial do painel de controle deve ser exibida.
    • Clique em Configurações avançadas do sistema e depois abra a janela Variáveis de Ambiente.
    • No seção Variáveis do Sistema, defina a variável JournalDevVar com o valor JournalDev.
    • Atualizando o PatternLayout dentro do seu log4j2.xml para conter sua variável recém-adicionada.

  1. Defina suas variáveis de ambiente usando a facilidade de script de inicialização.
    • Em vez de usar o script padrão normal, você pode usar a facilidade de execução do script do Eclipse IDE, clique no seu menu Run e escolha Run Configuration.
    • Navegue até a guia Environment e defina sua variável lá.

Agora, observe o arquivo log4j2.xml modificado e perceba o uso de variáveis de ambiente.

<Console name="Console" target="SYSTEM_OUT">
    <PatternLayout pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n"/>
</Console>

E o resultado da execução ficaria assim:

23:57:02.511 JournalDev www.journaldev.com [main] TRACE com.ComApp - COM :: LEVEL :: ComApp TRACE Message ::
23:57:02.517 JournalDev www.journaldev.com [main] TRACE com.journaldev.ComJournalDevApp - COM :: JournalDev :: LEVEL :: ComJournalDevApp TRACE Message ::
23:57:02.520 JournalDev www.journaldev.com [main] TRACE com.journaldev.logging.ComJounralDevLoggingApp - COM :: JournalDev :: LOGGING :: LEVEL :: ComJounralDevLoggingApp TRACE Message ::
23:57:02.523 JournalDev www.journaldev.com [main] ERROR net.NetApp - NET :: LEVEL :: NetApp ERROR Message ::
23:57:02.527 JournalDev www.journaldev.com [main] ERROR net.journaldev.NetJournalDevApp - NET :: JournalDev :: LEVEL :: NetJournalDevApp ERROR Message ::

Mas você pode enfrentar um pequeno problema aqui e especialmente ao definir as variáveis de ambiente de um sistema operacional e é o Cache do Eclipse. Idealmente, o Eclipse tem em cache todas as variáveis do sistema quando é iniciado e você pode encontrá-las em Executar – Configuração de Execução – Aba Ambiente – Clicar no botão Selecionar. Portanto, você pode ficar confuso quando as tiver definido mas o Aplicativo não as reconhecer. Mesmo que você reinicie seu Eclipse, não conseguirá a solução e para resolvê-lo, você deve executar eclipse.exe -clean em sua instalação do Eclipse. Para garantir que suas variáveis de ambiente estejam definidas corretamente e seu sistema as encontre facilmente, você pode usar o tipo de plugin de correspondência fornecido pela API do Log4j2. Faça uma instância de EnvironmentLookup e peça a ele para procurar uma determinada variável e se estiver definida, você as encontrará facilmente.

EnvironmentLookup lookup = new EnvironmentLookup();
LogManager.getRootLogger().error(lookup.lookup("JournalDevSecondVar"));

Aplicativos Log4j2

Você viu anteriormente como pode usar Pesquisas para injetar variáveis no seu arquivo de configuração. No entanto, você pode querer modificar o meio pelo qual suas mensagens são transmitidas. Em vez de usar o console diretamente, você pode querer usar um arquivo ou repositório de banco de dados para garantir que suas mensagens sejam retidas permanentemente. O Log4j2 forneceu muitos Apêndices, e você pode consultar a documentação do log4j2 para obter mais detalhes sobre os Apêndices. De maneira breve, abaixo está a lista de todos os Apêndices do Log4j2.

  1. ConsoleAppender
  2. AsyncAppender
  3. FailoverAppender
  4. FileAppender
  5. FlumeAppender
  6. JDBCAppender
  7. JMSAppender
  8. JPAAppender
  9. MemoryMappedFileAppender
  10. NoSQLAppender
  11. OutputStreamAppender
  12. RandomAccessFileAppender
  13. RewriteAppender
  14. RollingFileAppender
  15. RollingRandomAccessFileAppender
  16. RoutingAppender
  17. SMTPAppender
  18. SocketAppender
  19. SyslogAppender

Os meios mais famosos usados para registrar eventos são console, arquivo e banco de dados. Uma vez que o arquivo salvaria suas mensagens, o banco de dados pode ser usado para auditá-las. Para esse propósito, esta seção se concentrará em como o JDBCAppender pode ser usado eficientemente.

JDBCAppender

O principal objetivo do JDBCAppender é escrever eventos de log em uma tabela relacional por meio de conexões JDBC. Não vamos gastar muito tempo explicando como você pode otimizar seus pools de conexão, já que este tutorial não se destina a esse fim. Mas com certeza você obterá um exemplo totalmente funcional que ajuda a escrever seus eventos de log no banco de dados. Antes de prosseguirmos, vamos ver todos os parâmetros necessários e uma descrição de cada um para configurar corretamente o JDBCAppender.

Parameter Name Type Description
Name String Required, The name of the Appender
ignoreExceptions boolean Default value is set to true, making exceptions thrown to be logged also and then ignored. False value means the exception will be propagated for the caller.
filter Filter The filter that should be used to make a decision whether the log events are going to be handled by this Appender or not.
bufferSize int Default value is zero, indicating there’s no buffering have been done upon log events. Value greater than 0 would lead the Appender to buffer log events and then flush them once the buffer reaches the limit specified.
connectionSource ConnectionSource Required, the connections source from which the database connections should be retrieved.
tableName String Required, the name of the Table on which your log events should be persisted.
columnConfigs ColumnConfig[] Required, additional information may be set upon those used columns and how the data should be persisted on each of them. This can be handled with multiple <Column> elements.
Parameter Name Type Description
jndiName String Required, full prefixed JNDI name that the javax.sql.Datasource is bound to.
Parameter Name Type Description
class String Requird, The fully qualified name for a class containg a static factory method for obtaining JDBC connections.
method boolean Required, The name of the static factory method for obtaining JDBC connections.
Parameter Name Type Description
name String Required, the name of the database column
pattern String Ability to specify any legal pattern that Log event would be formatted with
literal String Ability to specify literal value in this column (i.e. SEQ.NEXTVAL)
isEventTimestamp boolean Indicating whether the event would consider Timestamp
isUnicode boolean For unicode purpose as you may refer for Log4j2 documentation for further details
isClob boolean For storing character large object, you may refer for Log4j2 documentation for further details.

Já que você é obrigado a usar JNDI, nosso exemplo configuraria uma fonte de dados de conexão para o banco de dados Oracle e o Apache Tomcat 7.

  • Se você não instalou o banco de dados Oracle em seu ambiente, é apreciado se puder fazer isso. Se você não está muito familiarizado com o Oracle, recomendo instalar sua Edição Express.
  • Instale o Apache Tomcat 7 em seu ambiente.
  • Crie um projeto Maven WebApp em seu Eclipse.

  • Assegure-se de que seu projeto seja criado com sucesso e, se perceber algum erro no pom, certifique-se de corrigi-los.
  • Adicione as dependências do Log4j2.
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-api</artifactId>
   <version>2.2</version>
</dependency>
<dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-core</artifactId>
   <version>2.2</version>
</dependency>
  • Configure seu contexto para incluir uma declaração de fonte de dados MySQL. De acordo com a documentação do Apache, este arquivo deve estar dentro da pasta META-INF da sua Aplicação Web.Configure seu banco de dados e crie sua Tabela de Log.

<Context path="/JournalDevWebLogging"
	privileged="true" antiResourceLocking="false" antiJARLocking="false">
	<Resource name="jdbc/JournalDevDB" auth="Container"
			factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
			type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000"
			username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
			url="jdbc:mysql://localhost:3306/journaldev" />
</Context>
  • Configure sua base de dados e crie sua Tabela de Log,

CREATE TABLE `logging` (
  `EVENT_ID` int(11) NOT NULL AUTO_INCREMENT,
  `EVENT_DATE` datetime DEFAULT NULL,
  `LEVEL` varchar(45) DEFAULT NULL,
  `LOGGER` varchar(45) DEFAULT NULL,
  `MSG` varchar(45) DEFAULT NULL,
  `THROWABLE` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`EVENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
  • Configure seu log4j2.xml para ficar como abaixo:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{HH:mm:ss.SSS} $${env:JournalDevVar} $${env:JournalDevSecondVar} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
		<JDBC name="databaseAppender" tableName="journaldev.logging">
			<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
			<Column name="EVENT_DATE" isEventTimestamp="true" />
			<Column name="LEVEL" pattern="%level" />
			<Column name="LOGGER" pattern="%logger" />
			<Column name="MSG" pattern="%message" />
			<Column name="THROWABLE" pattern="%ex{full}" />
		</JDBC>
	</Appenders>
	<Loggers>
		<Root level="ERROR">
			<AppenderRef ref="Console" />
		</Root>
		<logger name="com" level="TRACE" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
		<logger name="com.journaldev" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
	</Loggers>
</Configuration>
  • Crie qualquer recurso da Web que permita obter uma referência para um logger e, em seguida, registrar um evento.
package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
	private static final long serialVersionUID = 1L;
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
			Logger logger = LogManager.getLogger(JournalDevServlet.class);
			logger.trace("JournalDev Database Logging Message !");
	}
}
  • Você pode optar por configurar um ServletContextListener que pode garantir a inicialização do data source de forma adequada.
package com.journaldev;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.logging.log4j.LogManager;
public class JournalDevServletContextListener implements ServletContextListener{
	private InitialContext context = null;
	public void contextDestroyed(ServletContextEvent event) {
	}
	public void contextInitialized(ServletContextEvent event) {
		try {
			// Obter o contexto inicial 
			context = new InitialContext();
			// Obter uma referência para o subcontexto env 
			Context envContext = (Context)context.lookup("java:comp/env");
			// Obter uma referência para o subcontexto jdbc e, em seguida, localizar o data source definido 
			LogManager.getRootLogger().error(((Context)envContext.lookup("jdbc")).lookup("JournalDevDB"));
		} catch (NamingException e) {
			LogManager.getRootLogger().error(e);
		}
	}
}
  • Defina seu Servlet dentro do arquivo web.xml.
  • Execute o aplicativo e acesse o Servlet definido acima. Você verá os logs abaixo.
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jdk1.6.0_26\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Java\jdk1.6.0_26\jre\bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin/server;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/bin;C:/Program Files/Java/jdk1.6.0_26/bin/../jre/lib/amd64;D:\OracleWebCenter\OracleWC\Oracle11g\app\oracle\product\11.2.0\server\bin;;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;D:\OracleDB\app\product\11.2.0\dbhome_1\bin;org.C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;D:\SpringRoo\spring-roo-1.2.5.RELEASE\bin;D:\Ant\apache-ant-1.9.2\bin;C:\Python27;C:\Program Files\Java\jdk1.6.0_26\bin;D:\Maven\apache-maven-3.2.1/bin;D:\bower-master\bin;C:\Program Files (x86)\Git\cmd;C:\Program Files\nodejs\;C:\Program Files\Microsoft Windows Performance Toolkit\;D:\Grails\grails-2.4.0\bin;D:\Gradle\gradle-2.0\bin;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files\TortoiseSVN\bin;D:\Strawberry\perl\bin;D:\Strawberry\perl\site\bin;D:\Strawberry\c\bin;C:\Users\mohammad.amr\AppData\Roaming\npm;D:\JournalDev\eclipse;;.
Mar 15, 2015 2:31:41 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:JournalDevWebLogging' did not find a matching property.
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:41 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:41 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1020 ms
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Mar 15, 2015 2:31:41 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.35
14:31:43.847 [localhost-startStop-1] ERROR  - org.apache.tomcat.jdbc.pool.DataSource@10fd0a62{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=com.mysql.jdbc.Driver; maxActive=100; maxIdle=30; minIdle=10; initialSize=10; maxWait=10000; testOnBorrow=false; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=root; url=jdbc:mysql://localhost:3306/journaldev; username=root; validationQuery=null; validatorClassName=null; validationInterval=30000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; }
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Mar 15, 2015 2:31:43 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Mar 15, 2015 2:31:43 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1909 ms

Filtros Log4j2

Mesmo que haja um LoggerConfig candidato para lidar com o evento de log, você pode configurá-lo para negar a passagem dos eventos de log para os Appenders de back end. Isso pode ser feito por meio do filtro log4j2. Essa seção não tem como objetivo fornecer a você um tutorial invasivo, massivo e enorme sobre o uso de filtros no Log4j2, pois eles precisam de muitos artigos abordando cada um deles. Mas aqui, você verá como usar o filtro mais simples para aprender o conceito. Um dos filtros mais simples que você pode usar é o BurstFilter, que fornece um mecanismo para controlar a taxa na qual os LogEvents são processados, descartando silenciosamente eventos após o limite máximo ter sido atingido. Por enquanto, você pode ver abaixo todos os detalhes necessários para usar o BurstFilter.

Parameter Name Type Description
level String Level of messages to be filtered
rate float The average number of events per second to allow
maxBurst integer The maximum number of events that can occur before events are filtered for exceeding the average rate. The default is 10 times the rate.
onMatch String Action to take when filter matches. May be Accept, DENY or NEUTRAL. The default is NEUTRAL
onMismatch String Action to tale when filter doesn’t match. May be Accept, DENY or NEUTRAL. The default is NEUTRAL

Agora, observe a localização do BurstFilter dentro do seu Database Appender.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</Console>
		<JDBC name="databaseAppender" tableName="journaldev.logging">
			<DataSource jndiName="java:/comp/env/jdbc/JournalDevDB" />
			<BurstFilter level="TRACE" rate="20" maxBurst="2"/>
			<Column name="EVENT_DATE" isEventTimestamp="true" />
			<Column name="LEVEL" pattern="%level" />
			<Column name="LOGGER" pattern="%logger" />
			<Column name="MSG" pattern="%message" />
			<Column name="THROWABLE" pattern="%ex{full}" />
		</JDBC>
	</Appenders>
	<Loggers>
		<Root level="ERROR">
			<AppenderRef ref="Console" />
		</Root>
		<logger name="com" level="TRACE" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
		<logger name="com.journaldev" additivity="false">
			<AppenderRef ref="databaseAppender" />
		</logger>
	</Loggers>
</Configuration>
  • O Database Appender considera o BurstFilter, enquanto o console Appender não.
  • O uso do Console logger levaria você a registrar todos os eventos de log, mas o Database Appender não faria isso, pois o BurstFilter negaria alguns deles de prosseguir.
  • Essa negação de LogEvents é alcançada mesmo que os Loggers utilizados sejam candidatos para lidar com os LogEvents. Isso é tão razoável como mostrado no JournalDevServlet abaixo.

package com.journaldev;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JournalDevServlet extends HttpServlet{
	private static final long serialVersionUID = 1L;
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		Logger logger = LogManager.getLogger(JournalDevServlet.class);
		for(int i = 0 ; i < 1000 ; i++){
			logger.trace("Index :: "+i+" :: JournalDev Database Logging Message !");
			LogManager.getRootLogger().error("Index :: "+i+" :: JournalDev Database Logging Message !");
		}
	}
}

Mesmo que essas LoggerConfigs sejam candidatas para lidar com os Log Events jogados lá, mas o Filter impediu que alguns deles fossem tratados e, em seguida, registrados. Você pode adicionar isso para o conceito de Espaço de Log para entender todo o conceito de registro.

Layouts Log4j2

Devido aos diferentes Apêndices que consomem Eventos de Registro e à natureza de cada apêndice, os layouts são feitos para formar o LogEvent no formato que atenda às necessidades de quem estará consumindo o evento de registro. Nas APIs Log4j 1.x e Logback, a transformação de layout de Log Events era em uma String, enquanto os layouts do Log4j2 consideraram uma maneira diferente de transformação; e isso é feito transformando o LogEvent em uma matriz de bytes. Esse novo tipo de transformação exigiria que você configurasse o Charset para garantir que a matriz de bytes contenha os valores corretos. É altamente recomendável retornar ao site oficial do Apache Log4j2 e ver mais sobre Layout e os diferentes tipos que o Log4j2 fornece. Nesta seção, vamos considerar o Layout mais famoso que é sempre usado pela maioria de nossos desenvolvedores e com certeza você pode ter ouvido falar sobre ele; é o PatternLayout.

Log4j2 PatternLayout

O layout de padrão é um padrão de string configurável e flexível destinado a formatar o LogEvent. Esse tipo de formatação depende do conceito de padrão de conversão. Esta seção irá representar para você as características mais importantes que o layout de padrão oferece. O padrão de conversão está relacionado ao padrão de conversão que printf na linguagem C fornece. Geralmente, o padrão de conversão é composto por texto literal e expressões de controle de formato chamadas especificadores de conversão. A figura abaixo representa de que partes o padrão de conversão é composto: Esta figura acima é uma tentativa de simplificar o Padrão de Conversão, mas com certeza é melhor você consultar a documentação do Apache Log4j2 para mais detalhes sobre Layouts e Especificamente Layout de Padrão. Além disso, você pode consultar acima para eventos de log e ver a cada momento qual Padrão de Conversão está sendo usado para formatar as mensagens.

Qual nível de Log4j2 você deve usar

A maior questão que você pode se perguntar é quando um nível específico de evento de log deve ser usado. No campo de desenvolvimento, é normal usar DEBUG enquanto na produção devemos usar INFO ou WARN. A tabela abaixo deve orientá-lo sobre qual nível de log4j2 deve ser usado em cada caso.

Log Event Level When It Should Be Used
OFF When no events will be logged
FATAL When a severe error will prevent the application from continuing
ERROR When an error in the application, possibly recoverable
WARN When an event that might possible lead to an error
INFO When an event for informational purposes
DEBUG When a general debugging event required
TRACE When a fine grained debug message, typically capturing the flow through the application
ALL When all events should be logged

Resumo do Tutorial do Log4j2

O Log4j2 é a versão reformulada do framework de logging Apache. O Log4j2 trouxe uma série de novos recursos e melhorias de desempenho em relação ao Log4j1.x. Este tutorial do log4j2 tem como objetivo ajudá-lo a encontrar todas as informações em um único local. Como alguns desses conceitos não são tão fáceis de cobrir de uma vez, decidimos concentrar nossos esforços em explicar o conceito e usar alguns exemplos para mais esclarecimento. Apêndices, filtros, layouts e pesquisas estão sujeitos a esta regra. Alguns pontos importantes Para garantir que você consiga executar a aplicação abaixo e evitar quaisquer obstáculos, verifique o seguinte:

  • O seu IDE Eclipse está habilitado para o Maven.
  • O seu Apache Tomcat possui um arquivo JAR mysql-connector dentro da pasta lib do Apache Home.
  • Você sabe como usar o Maven.

Download do Projeto de Exemplo do Apache Log4j 2

Isso é tudo para o tutorial do log4j2, espero que a maioria dos pontos importantes esteja coberta para ajudá-lo a começar a usar o Log4j2 em sua aplicação.

Source:
https://www.digitalocean.com/community/tutorials/log4j2-example-tutorial-configuration-levels-appenders