Tutorial

Log4j2 Example Tutorial - Configuration, Levels, Appenders

Published on August 3, 2022
Default avatar

By Pankaj

Log4j2 Example Tutorial - Configuration, Levels, Appenders

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Welcome to the Apache Log4j2 Example Tutorial. If you ask an expert developer about the most annoying thing about an application, the answer might be related to logging. If there is no suitable logging in an application, maintenance will be a nightmare. Most of the application go through Development testing, unit testing, integration testing. But when it comes to production, you will always face unique scenarios and exception. So the only way to figure out what happened in a specific case is to debug through the logs. Many frameworks provide some way of default logging, but it’s always best to go with the industry standard logging mechanism. Apache Log4j is one of the most widely used logging frameworks. Apache Log4j 2 is the next version, that is far better than Log4j.

Log4j Example Tutorial

In this Log4j2 Example Tutorial, you will learn how to get started with Apache Log4j2. We will also explore Log4j2 architecture, log4j2 configuration, log4j2 logging levels, appenders, filters and much more.

  1. Log4j2 Overview
  2. Log4j2 Architecture
  3. Log4j2 Configuration
  4. Log4j2 Levels
  5. Log4j2 Lookups
  6. Log4j2 Appenders
  7. Log4j2 Filters
  8. Log4j2 Layouts
  9. Which Log4j2 Level should you use
  10. Log4j2 Tutorial Summary

Log4j2 Overview

Using Logging API in application isn’t a luxury, it’s a must have. Log4j is an open source library that’s published and licensed under Apache Software. You can debug an application using Eclipse Debugging or some other tools, but that is not sufficient and feasible in a production environment. Logging mechanism will provide you several benefits that you will not find in normal debugging.

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

As you can see above, using of logging mechanism will be more efficient with less maintenance cost. Apache Log4j is the front runner tool for logging in Java applications, so you should use it.

Log4j2 Architecture

Before we proceed for Log4j Example tutorial, it’s good to look into Log4j2 architecture. Below image shows the important classes in Log4j2 API. Log4j2 classes, log4j2 architecture, log4j2 tutorial, log4j2 Here’s the detailed explanation for the architecture shown above:

  • Applications will ask LogManager for a Logger with a specific name.

  • LogManager will locate the appropriate LoggerContext and then obtain Logger from it.

  • If the Logger isn’t created yet, it will be created and associated with LoggerConfig according to three choices below:

    1. Logger instance will be created and associated with the LoggerConfig that have the same name. For example App.class in getLogger(App.class) will be evaluated to be a String com.journaldev.App. LoggerConfig name is identical to fully qualified class name (Software component).
    2. Logger instance will be created and associated with the LoggerConfig that have the same Loggers parent package. For example com.journaldev in getLogger("com.journaldev")
    3. Logger instance will be created and associated with the Root LoggerConfig. Root LoggerConfig will be used when there is no configuration file or when you’re obtaining a logger with name not defined in the logger declarations.
  • LoggerConfig objects are created from Logger declaration in the configuration file. LoggerConfig is also used to handle LogEvents and delegate them for their defined Log4j2 Appenders.

  • Root logger is an exceptional case, in terms of its existence. It always exists and at the top of any logger hierarchy.

  • You may obtain the root logger by using the below statements:

    Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
    Logger logger = LogManager.getRootLogger();
    
  • The name of log4j2 loggers are case sensitive.

  • Except root logger, all loggers can be obtained through passing their name into LogManager.getLogger().

  • LoggerContext is a vocal point for Logging system as you may have multiple LoggerContexts inside your application. Per each LoggerContext an active configuration should be set.

  • Log4j2 configuration contains all Logging system assets; LoggerConfig(s), Appender(s), Filter(s) and many others.

  • Calling of LogManager.getLogger() by passing the same name will always return the reference for the exact same logger instance.

  • Configuration of Logging system is typically done with the application initialization. This can take different forms; programmatically or by reading a log4j2 configuration file.

Every logger is associated with a LoggerConfig object, set of LoggerConfig objects made up a Hierarchy of loggers. This concept is known as Logger Hierarchy. Logger Hierarchy is made up of set of LoggerConfig objects with a parent-child relationship. The topmost element in every Logger Hierarchy is the Root Logger. If Log4j2 doesn’t find the configuration file, only Root Logger will be used for logging with logging level as ERROR. Below image shows the warning message you will get in this case. Error StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Error StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. The table below shows the parent-child relationship in the Logger Hierarchy.

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

To clarify Parent-Child relationship, table above would be read as follows:

  • Root is a parent for com.
  • Root is an ancestor for com.journaldev.
  • Root is an ancestor for com.journaldev.logging.
  • com is a child for Root.
  • com is a parent for com.journaldev.
  • com is an ancestor for com.journaldev.logging.
  • com.journaldev.logging is a child for com.journaldev and so on.

An instance of LoggerConfig is said to be an ancestor of another LoggerConfig; if its name followed by a dot is a prefix for the descendant name. An instance of LoggerConfig is said to be a parent for another LoggerConfig; if there are no interleaving names between both of them.

Log4j2 Configuration

There are many ways to use Log4j2 configuration in you application.

  1. Using a configuration file written in XML, JSON, YAML or properties file.
  2. Programmatically, by creating a configuration factory and configuration implementation.
  3. Programmatically, by calling APIs exposed in the configuration interface.
  4. Programmatically, by calling methods on the internal logger class.

We will focus mainly on the configuration file. However, it’s good to know the programming approach too, in case you want to configure a specific logging strategy for some specific Logger. First of all, let’s consider the case where you didn’t provide a configuration file. Log4j2 implementation assumes that there is a System variable called log4j.configurationFile to point the location of log4j2 configuration file.

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>

And here is the detailed explanation for the code listed above:

  • App has referenced the Root logger by calling LogManager getRootLogger method.
  • Referencing of logger from LogManager has started Log4j system.
  • Log4j will inspect log4j.configurationFile system property to determine log4j2 configuration file. Log4j configuration can be written in JSON, YAML and XML.
  • We can set log4j.configurationFile system property through System.setProperties("log4j.configurationFile","FILE_PATH") or by passing it as a JVM parameter like you see in the figure below. Notice also File protocol prefix.

log4j2 configuration, log4j.configurationFile, Passing log4j property as a JVM Parameter

  • In case no system property is defined the configuration order takes below precedence:
    • Property ConfigurationFactory will look for log4j2-test.properties in the classpath.
    • YAML ConfigurationFactory will look for log4j2-test.yaml or log4j2-test.yml in the classpath.
    • JSON ConfigurationFactory will look for log4j2-test.jsn or log4j2-test.json in the classpath.
    • XML ConfigurationFactory will look for log4j2-test.xml in the classpath.
    • Property ConfigurationFactory will look for log4j2.properties on the classpath
    • YAML ConfigurationFactory will look for log4j2.yml or log4j2.yaml in the classpath.
    • JSON ConfigurationFactory will look for log4j2.jsn or log4j2.json in the classpath.
    • XML ConfigurationFactory will look for log4j2.xml in the classpath.
    • If no configuration file was provided, the DefaultConfiguration takes place and that would lead you for set of default behaviors:
      • Root logger will be used.
      • Root logger level will be set to ERROR.
      • Root logger will propagate logging messages into console.
      • PatternLayout is set to be %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n

Using log4j2 configuration file makes the log4j2 configuration so simple, but let’s see how we can configure it programmatically. This is all about using 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 {
 
    	// Get instance of configuration factory; your options are default ConfigurationFactory, XMLConfigurationFactory,
    	// 	YamlConfigurationFactory & JsonConfigurationFactory
    	ConfigurationFactory factory =  XmlConfigurationFactory.getInstance();
 
    	// Locate the source of this configuration, this located file is dummy file contains just an empty configuration Tag
    	ConfigurationSource configurationSource = new ConfigurationSource(new FileInputStream(new File("C:/dummyConfiguration.xml")));
 
    	// Get a reference from configuration
    	Configuration configuration = factory.getConfiguration(configurationSource);
 
    	// Create default console appender
    	ConsoleAppender appender = ConsoleAppender.createDefaultAppenderForLayout(PatternLayout.createDefaultLayout());
 
    	// Add console appender into configuration
    	configuration.addAppender(appender);
 
    	// Create loggerConfig
    	LoggerConfig loggerConfig = new LoggerConfig("com",Level.FATAL,false);
 
    	// Add appender
    	loggerConfig.addAppender(appender,null,null);
 
    	// Add logger and associate it with loggerConfig instance
    	configuration.addLogger("com", loggerConfig);
 
    	// Get context instance
    	LoggerContext context = new LoggerContext("JournalDevLoggerContext");
 
    	// Start logging system
    	context.start(configuration);
 
    	// Get a reference for logger
    	Logger logger = context.getLogger("com");
 
    	// LogEvent of DEBUG message
    	logger.log(Level.FATAL, "Logger Name :: "+logger.getName()+" :: Passed Message ::");
 
    	// LogEvent of Error message for Logger configured as FATAL
    	logger.log(Level.ERROR, "Logger Name :: "+logger.getName()+" :: Not Passed Message ::");
 
    	// LogEvent of ERROR message that would be handled by Root
    	logger.getParent().log(Level.ERROR, "Root Logger :: Passed Message As Root Is Configured For ERROR Level messages");
    }
}
  • You may use any of ConfigurationFactory provided by Log4j2 or use the default one. We’ve used XMLConfigurationFactory to get an instance of ConfigurationFactory.
  • Factory will give you an instance of required Configuration reference by passing the corresponding configuration file.
  • Configuration instance will be used in conjunction with LoggerContext for starting the Logging System.
  • A console Appender has been configured and added into configuration instance with default layout. This Appender would print out messages into your console.
  • LoggerConfig instance has been created with provided name, LEVEL and with no filter used. Created Appender will assigned for this instance of LoggerConfig.
  • LoggerConfig instance added into configuration instance.
  • A new instance of LoggerContext is created with defined name.
  • The configuration instance has been passed for LoggerContext instance and invoked start upon the latter.
  • A logger instance has been acquired from LoggerContext. This logger instance will be used to fire set of Log events.
  • Logger instance has fired three events that would be explained in the Log4j2 Levels section.
  • com logger has configured to print out messages whose levels are FATAL.
  • By default, Root logger is configured to print out messages whose levels is ERROR.
  • ERROR messages will not be logged by ‘com’ logger because it’s level is FATAL.

The same configuration can be done through using of YAML, JSON or properties file. However log4j2 property file configuration is different from the log4j property file, so make sure you are not trying to use the log4j property file configuration with log4j2. It will throw below error;

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

While processing the above code would give you the below output:

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

The First line of logs is from com logger and the second is from the Root Logger. com logger error message is not printed because its level is Fatal.

Log4j2 Levels

You can see in above code examples that every time we define a LoggerConfig, we also provide logging level. By default log4j2 logging is additive. It means that all the parent loggers will also be used when a specific logger is used. Below image clarifies this situation. Logging Space, Logging Hierarchy And herein points of clarification for it:

  • As we’ve stated earlier, each logger has associated with LoggerConfig instance. This loggerConfig has been defined at the configuration scope.
  • Level of logging can be determined at the LoggerConfig scope.
  • You may obtain the logger by its name, parent package or by pointing the Root Logger itself.
  • Root Logger is the top level node for every LoggerConfig hierarchy.
  • Once you obtain the com.journaldev logger and initiate a logEvent for logging, the loggerConfig (net.journaldev) will log the message and the message will be propagated as well up in the hierarchy without any respect for parents logging levels. So log event will be propagated to com and Root loggers and they will also log the message respectively according to the levels defined.
  • Once you obtain the com logger and initiate a logEvent for logging, the loggerConfig(com) will log the message and the message will be propagated as well up in the hierarchy without any respect for parents’ logging levels. That is, Root logger will be propagated the log event and it also will log the message.
  • The same case for net.journaldev hierarchy.
  • Next sections, will add more clarifications for additive concept.
  • There’s a chance for the parent to ignore the message by using Filter concept or by setting the additive indicator to false, so log events will not be propagated to parents.
  • There’s a chance for the logger to ignore the message if the respective loggerConfig’s level is GREATER THAN log events level.

Now, let’s see the example that’s associated with the concept of additivity explained above:

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 ::");
	}
}

Whereas the log4j2 configuration file looks like below:

<?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>

If you get Main class executed you would find the below results:

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 ::

Here’s below detailed explanation for the code listed above:

  • Configuration file contains five loggerConfig instances defined and they’re Root, com, com.journaldev, net & net.journaldev. Just like Logger Hierarchy shown above.
  • Root’s level is configured to be ERROR and that’s actually the default value.
  • com & com.journaldev levels are configured to be TRACE.
  • net & net.journaldev levels are configured to be ERROR.
  • You may noticed that the ComAPP and ComJournalDevApp loggers messages have been shown two and three times respectively. These messages shown according to the Logger Hierarchy for ComApp & ComJournalDevApp where they’re in the com & com.journalDev packages respectively. We have similar case with NetApp & NetJournalDevApp classes.
  • Parents are propagated as additive indicator is set to true by default.

Logging Space takes into consideration Levels of log events and the loggerConfig’s level in addition to Logger Hierarchy.

So what if we changed the LoggerConfig for com to be INFO and left the whole program as is:

<?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>

Then the result it would be like below:

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 ::
  • For sure you may notice that the ComAPP log event has ignored and that is because of the defined level of loggerConfig for com package. INFO (400) level is less than log event’s level which is here TRACE(600). So, the message of ComApp won’t be shown anymore and to make it shown, you need to modify the LoggerConfig’s level for com to be TRACE(600) or ALL(Integer.MAX_VALUE).

To make sure Log events have been displayed, the LoggerConfig’s Level should be greater than or equal to Log event’s level.

Table below shows you the log4j2 Levels and the weight for each of them:

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

For sure Table above clarifies much more than words and it gives you the main cause for being the Log event TRACE isn’t displayed while the LoggerConfig’s level is INFO.

Notice that the propagation of log events up in the logger hierarchy is beyond this computation and it ignores the levels.

But what happens if we remove LoggerConfig of com.journaldev from the configuration and added a new one for com.journaldev.logging to make the configuration file looks like below:

<?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>

You may find the figure below more convenient for you to understand what’s happened in the above log4j2 configuration. log4j2, log4j2 levels, Logger Hierarchy - Remove Level Of Hierarchy and Adding New Level Here’s some clarification for the figure shown above and how it may affect the behavior of logging events:

  • When log events have thrown by a Logger named com.journaldev.logging, the LoggerConfig that’s associated with that name (i.e. com.journaldev.logging) has been used to handle it and print out the message.
  • Being com.journaldev.logging LoggerConfig’s additive attribute is set by default to true, the log event has been propagated for the parent which’s in this case referring for com.journaldev.
  • Being com.journaldev LoggerConfig isn’t defined in the configuration, no action happens and the Log event will be propagated up to com and then Root LoggerConfig instances.
  • Com & Root will receive the Log event and print it out regardless of the level it’s sent with.

As a result for the points mentioned, you would see the following outputs:

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 ::

And you may notice the following:

  • Log event at com package has been shown twice. One for com while second for Root.
  • Log event at com.journaldev has been shown twice. One for com and second for Root. Even though it was three times before, but for now the LoggerConfig of com.journaldev is absent and so no logging might have happened at com.journaldev package and the event would be propagated for com and Root.
  • Log event at com.journaldev.logging has been shown three times, one for com.journaldev.logging package and second for com and third for Root. According to Logger Hierarchy propagation, it should be displayed forth times, but due to absence of com.jounraldev LoggerConfig, it displays three times.

In case you’ve defined a com.journaldev LoggerConfig instance with no Level specified, it will inherit Level of its parent.

But what if you have defined com.journaldev LoggerConfig at your configuration file and missed out specifying the LoggerConfig’s level. Fortunately, the concept of Logger Hierarchy will save you here and com.journaldev would inherit its level value from its parent. Below is a sample configuration file followed by the table for logging level of each logger config.

<?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
  • com.journaldev.logging package has already associated with a LoggerConfig with Log level TRACE.
  • com.journaldev package has already associated with a LoggerConfig with no Log Level specified, so it would inherit its Parent Log Level and for sure the value would be TRACE for com package.
  • com package has already associated with Loggerconfig with log level TRACE.
  • By default Root has ERROR as a log level.
  • In case com package isn’t declared, com.journaldev LoggerConfig will inherit the log level of Root.

Below is the result of execution while com.journaldev inherits com log level:

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 ::

And below result would be if you remove declaring LoggerConfig for com package:

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 ::

You may notice that no messages have logged for com and com.journaldev, below are the reasons.

  • Deletion of LoggerConfig associated with com package will make all Log events mentioned at that package to be ignored.
  • Since there’s no LoggerConfig defined for com package in the configuration, LoggerConfig that’s associated with com.journaldev will inherit Log Level from its parent. Com isn’t defined, and the Logger Hierarchy is reached to the Top and it’s referring now for Root. The root Log level is ERROR(200) and Log event’s level in com.journaldev is TRACE(600) - See ComJournalDevApp - and according to previous equation defined, LoggerConfig level should be greater than or equal to Log Event and that’s false, so no messages would be displayed here for com.journaldev.

Last but not least, following below Table shows you all possible logging scenarios that you may face when using Logging system:

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
  • There’s no direct method that can be used for throwing an OFF/ALL log events.
  • Mainly, for throwing OFF/ALL log events you may use logger.log(Level.OFF, “Msg”) or logger.log(LEVEL.ALL,“Msg”), respectively.
  • The log method is responsible to handle log event according for the mentioned equation.

Handling equation says: If the LoggerConfig Level’s is greater than or equal to Log event’s level the event would be accepted for further processing.

The log event would be accepted for further processing - this is so important because you have the ability to prevent some event from being handled even if it’s accepted by using Log4j2 Filters. You can set additive property to false to avoid log event propagation to parent loggers. Following below the same example that you did see before but this time with an additivity attribute, so you may notice the difference.

<?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>

And the result of execution would be like below:

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 ::

And you may notice that there’s no propagation of log events to its parent loggers.

Log4j2 Lookups

Ideally, you may define lookups as a way in which you can pass values for your Logging configuration file. Log4j2 provides you a different set of Lookups that can be used independently for setting values from different contexts:

  • Context Map Lookup
  • Date Lookup
  • Environment Lookup
  • Java Lookup
  • JNDI Lookup
  • JVM Input Argument Lookup (JMX)
  • Main Arguments Lookup
  • Map Lookup
  • Structured Data Lookup
  • System Properties Lookup
  • Web Lookup

You may refer for Log4j2 documentation for further details on every type of lookups, but let’s look at some example here to cover the basics of the Log4j2 Lookup. Environment Lookup represents the way in which you can pass an environment value (either by Linux etc/profile, windows system environment or Startup scripts for the application. As most of us know, we have the ability to define a set of environmental values for Application to use. Let’s see the most famous ways to define your environmental variables.

  1. Define environment variable by using Windows environment facility:
    • Right click on you computer icon and select properties. Control panel home should be displayed.
    • Click on Advanced system settings and then open Environment Variables window.
    • Under System Variables section, define variable JournalDevVar with JournalDev value.
    • Updating PatternLayout inside your log4j2.xml to contain your newly added variable.

Define JournalDevVar System Variable, log4j2 lookups

  1. Define your environmental variables by using the Startup script facility.
    • Instead, of using normal default script, you may use Eclipse IDE Running Script facility, click on your Run Menu and choose Run Configuration.
    • Navigate into Environment Tab & define your variable therein.

Define JournalDevSecondVar Startup Script Variable Look now at the modified log4j2.xml file and notice the use of environment variables.

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

And the result of execution would look like below:

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 ::

But you may face a little issue here and especially when you’re defining an OS’s environment variables and it’s the Eclipse Cache. Ideally, Eclipse has cached all system variables when it gets running and you may find all of them under Run - Run Configuration - Environment Tab - Click Select button. Eclipse IDE - System Variables Cache So, you may be confusing when you have defined it but the Application doesn’t recognize it. Even if you restart your Eclipse, you won’t get the solution and to solve it you must execute eclipse.exe -clean upon your Eclipse installation. To make sure your environment variables are defined properly and your system is going to find them willingly, you may use the correspondence plugin type that’s provided by Log4j2 API. Make an instance of EnvironmentLookup and ask it for looking up certain variable and if it’s defined, so you would find them easily.

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

Log4j2 Appenders

You did see previously how can use Lookups for injecting variables into your configuration file. Though, you may want to modify the medium that your messages went through. Instead of using console directly, you may want such a file or database repository to make sure your messages are retained permanently. Log4j2 has provided a lot of Appenders, and you may refer for log4j2 documentation to get further details for Appender. In a brief manner, below is the list of all Log4j2 Appenders.

  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

The most famous mediums used for logging events are console, file, and Database. Since the file would save your messages, the database might be used for auditing them. For this purpose, this section would focus on how JDBCAppender can be used efficiently.

JDBCAppender

The main goal of JDBCAppender is to write Log events into a relational table through JDBC connections. We don’t consume much time explaining how you can optimize your connection pools as this tutorial isn’t intended for this purpose. But for sure you will get a full functional example that helps writing your log events into database. Before we may proceed in, let’s see all needed parameters and a description for each to get JDBCAppender configured properly.

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.

Since you’re enforced to use JNDI, our example would configure a connection data source for Oracle database and Apache Tomcat 7.

  • If you didn’t install Oracle database into your environment, it’s appreciated if you can do so. If you’re not much aware of Oracle, i recommend you installing its Express Edition.
  • Install Apache Tomcat 7 into your environment.
  • Create a Maven WebApp project into your Eclipse.

Eclipse IDE - Create Maven Project

  • Make sure your project is created successfully and if you notice any error at the pom, make sure your fix them.
  • Add Log4j2 dependencies.
<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 your context to include a MySQL data source declaration. According for Apache documentation, this file should be inside your Web Application META-INF folder.

log4j2 tutorial, log4j2 example, JournalDevWeb Application Files Structure

<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 your database and create your Logging Table,

Logging Table View

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 your log4j2.xml to look like below:
<?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>
  • Create any Web resource that enable you from getting a reference for a logger and then log an event.
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 !");
	}
}
  • You may optionally configure a ServletContextListener that may ensure the initialization of data source is done properly.
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 {
			// Get initial context
			context = new InitialContext();
			// Get a reference for sub context env
			Context envContext = (Context)context.lookup("java:comp/env");
			// Get a reference for sub context jdbc and then locating the data source defined
			LogManager.getRootLogger().error(((Context)envContext.lookup("jdbc")).lookup("JournalDevDB"));
		} catch (NamingException e) {
			LogManager.getRootLogger().error(e);
		}
	}
}
  • Define your Servlet inside your web.xml file.
  • Run the application and access defined Servlet above. You will see below logs.
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

JDBCAppender example, Logging Table - Log Events Populated

Log4j2 Filters

Even if there’s a LoggerConfig candidate for handling Log event, you may configure it to deny passing the Log events into back end Appenders. This can be done by log4j2 Filter. This section isn’t intended for providing you an invasive, massive and huge amount of tutorial about using filters in Log4j2, as they need a lot of articles covering every one of them. But here, you would see how to use the most simple filter to learn the concept. One of the most simple filters that you may use is BurstFilter that provides you with a mechanism to control the rate at which LogEvents are processed by silently discarding events after the maximum limit has been reached. For now, you may see below all details needed to use 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

Now, look at the location of BurstFilter inside your 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>
  • Database Appender does consider BurstFilter while console Appender doesn’t.
  • While using of Console logger would lead you for full log events to be logged, database Appender wouldn’t do that as BurstFilter will deny some of them from proceeding into.
  • This LogEvents denial is achieved even that the Loggers used is the candidate for handling the LogEvents. This is so reasonable as shown in the JournalDevServlet below.

log4j2 filters, Logging Table - Log Events Populated With Filter Applying log4j2 filters, Console Logging - Log Events Populated Without Filter Applying

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 !");
		}
	}
}

Even that those LoggerConfigs are the candidate for handling Log Events thrown there, but the Filter has prevented some of them from being handled and then logged. You may add this for Logging Space concept to get the whole concept of logging.

Log4j2 Layouts

Due to different Appenders that consume Log Events and nature of each appender, the layouts are made to form the LogEvent in the format that meets the needs of whoever will be consuming the log event. In Log4j 1.x and Logback APIs, the layout transformation of Log Events was into a String, while Log4j2 layouts have considered a different way of transformation; and that’s by transforming the LogEvent into the array of bytes. This new type of transformation would enforce you configuring the Charset to ensure that byte array contains the correct values. It’s highly recommended to return back into Apache Log4j2 official site and see more about Layout and different types that Log4j2 provides. In this section, we will consider the most famous Layout that is always used by most of our developers and for sure you may hear about it; it’s PatternLayout.

Log4j2 PatternLayout

Pattern layout is a configurable, flexible String pattern aimed to format the LogEvent. This kind of formatting is dependent on the conversion pattern concept. This section will depict you the most important features pattern layout provides. Conversion pattern is related to the conversion pattern that printf in language C provides. Generally, the conversion pattern is composed of literal text and format control Expressions called conversion specifiers. Figure below depicts you what parts the conversion pattern composed from: log4j2 pattern layout, Conversion Pattern Components This figure above is a trial to simplify the Conversion Pattern, but for sure it’s better for you to refer Apache Log4j2 documentation for further details about Layouts and Pattern Layout specifically. Also, you may refer above for log events and see at every time what’s Conversion Pattern it is used for format the messages.

Which Log4j2 Level should you use

The biggest question that you may ask yourself is when specific Log Event level should be used. In the development field, it’s normal to use DEBUG log event whereas in production we should INFO or WARN level. This table below should guide you on which log4j2 level should be used in which case.

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

Log4j2 Tutorial Summary

Log4j2 is revamped version of Apache Logging framework. Log4j2 has provided a set of new features and performance enhancements from Log4j1.x. This log4j2 tutorial is aimed to help you get it all in one location. Since some of these concepts aren’t so easy to cover them all at once, we’re decided to enclose our efforts in explaining the concept and using some samples for more clarification. Appenders, Filters, layouts, and lookups are subject to this rule. Some important points To make sure you’re able to get the below application running and to avoid any obstacles, verify the below:

  • Your Eclipse IDE is maven enabled.
  • Your Apache Tomcat has a mysql-connector JAR inside your Apache Home lib folder.
  • You are aware of how to use Maven.

Download Apache Log4j 2 Example Project

That’s all for the log4j2 tutorial, I hope most of the important points are covered to get you started using Log4j2 in your application.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Pankaj

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
January 31, 2022

Hi, I am programmatically loading log4j config file using logger context and creating logger instance from logger context. I am doing this my main class. How can i create and use logger instance in subsequent classes? . for example if i call a method in another class how should i create a logger instance in that class using the same logger context. Please advice

- Vignesh

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    December 21, 2019

    How to change the name and location of log4j2.xml file? where we need to configure for the changed xml/property name and different location of the file configuration?

    - Nageswarareddy Chaduvula

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      April 4, 2019

      Nice article on log4j2 for beginners. I think the Logger Config section is bit lengthy and can be refactored.

      - Rupesh

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        September 25, 2018

        You’re missing the Http appender

        - Kevin

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          August 6, 2018

          Declarative configuration of logging is only “so simple” when it works. When it doesn’t, good luck trying to debug it.

          - Sridhar Sarnobat

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            March 23, 2018

            As of current version factory.getConfiguration(configurationSource) is undefined, an additional LoggerContext parameter is required.

            - Luke

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              March 20, 2018

              Very good over view of log4j2 logging framework. This was very helpful. Thanks for your effort.

              - Ravi

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                November 24, 2017

                very good and detailed explanation of log4j2 concepts and demo examples. Was really helpful in understanding them log4j2 version. The pattern layout could have been in more details like how the pattern works along with the diagram

                - pkkumar

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  May 18, 2017

                  System.setProperties(“log4j.configurationFile”,“FILE_PATH”) should be System.setProperty(“log4j.configurationFile”,“FILE_PATH”)

                  - Himadri

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    October 14, 2016

                    Great ideas , Incidentally , people are looking for a CA BOE-58-AH , my colleagues filled out a fillable version here https://goo.gl/dhsh23

                    - tillie arend

                      Try DigitalOcean for free

                      Click below to sign up and get $200 of credit to try our products over 60 days!

                      Sign up

                      Join the Tech Talk
                      Success! Thank you! Please check your email for further details.

                      Please complete your information!

                      Get our biweekly newsletter

                      Sign up for Infrastructure as a Newsletter.

                      Hollie's Hub for Good

                      Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

                      Become a contributor

                      Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                      Welcome to the developer cloud

                      DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

                      Learn more
                      DigitalOcean Cloud Control Panel