Log4j2 Overview
In software development, testing, and production environments, logs capture critical information about application behavior and error stack traces. Proper use of logging frameworks significantly improves problem-solving efficiency.
Log4j 1.x was widely used but suffered from memory leaks, maintenance difficulties, and reliance on older JDK versions, leading to its end-of-life in August 2015. Alternatives such as SLF4J, Logback, and Log4j2 introduced substantial improvements.
- SLF4J: A facade or abstraction layer for various logging frameworks, providing a unified API without concrete implementations.
- Logback: A native implementation of SLF4J, offering better performance, memory usage, and flexibility compared to Log4j 1.x.
- Log4j2: Similar to Logback but with enhanced performance, concurrency, asynchronous logger capabilities, and an extensible architecture. It is now widely adopted.
Log4j2 Configuration Components
The Log4j2 architecture consists of several key components:
(1) LoggerContext: The logging system context, central to managing loggers and configurations.
(2) Configuration: Each LoggerContext has one active Configuration instance containing all Appenders, Filters, LoggerConfigs, StrSubstitutor references, and Layout formatting rules.
(3) Logger: Inherits from AbstractLogger. Its behavior changes dynamically when the configuration is modified, as it becomes associated with different LoggerConfig instances.
(4) LoggerConfig: Created when a Logger is declared. Holds references to Appenders for processing events and Filters for pre- or post-processing. It effectively represents a collection of Appenders.
(5) Appender: Defines the output destination for log events. Log4j2 supports multiple Appender types: Console, File, Socket, Apache Flume, JMS, UNIX Syslog, databasse, etc. A single Logger can use multiple Appenders.
(6) Filter: Applied before or after a LoggerConfig processes an event. Filters return one of three actions:
- Accept: The event is accepted and no further filters are evaluated.
- Deny: The event is rejected and processing stops.
- Neutral: The event passes to the next filter. If no filters are configured, all events are processed directly.
(7) Layout: Formats log events into a desired output format, commonly configured via PatternLayout.
(8) StrSubstitutor and StrLookup: Enable dynamic variable substitution within configuration values.
(9) Log Levels: Each LoggerConfig has an assigned level. Standard levels, in ascending order: TRACE, DEBUG, INFO, WARN, ERROR, FATAL.
Default Configuration Behavior
When Log4j2 jars (log4j-core and log4j-api) are on the classpath but no configuration file is present, the framework falls back to a default configuration: only ERROR and FATAL messages are printed to the console. The console will display:
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
Minimal POM Dependencies
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.0</version>
</dependency>
Basic XML Configuration (log4j2.xml)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<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="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Loading Configuration
Web application via web.xml:
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>classpath:log4j2.xml</param-value>
</context-param>
<listener>
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
Programmatic loading in Java:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.Configurator;
public class App {
public static void main(String[] args) {
try {
File configFile = new File("src/main/resources/log4j2.xml");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(configFile));
ConfigurationSource source = new ConfigurationSource(bis);
Configurator.initialize(null, source);
Logger logger = LogManager.getLogger();
logger.info("Logger initialized, recording...");
logger.error("Error message");
logger.warn("Warning message");
logger.info("Info message");
logger.debug("Debug message");
logger.trace("Trace message");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Observed Output and Level Hierarchy
In the example above, the root logger level is set to info, and the console Appender uses the same level. The output will include messages of level INFO and higher (INFO, WARN, ERROR). The level order is:
TRACE < DEBUG < INFO < WARN < ERROR < FATAL
If a LoggerConfig defines a level of WARN, only WARN, ERROR, and FATAL are output. However, if a specific Appender reference overrides the level, messages below the root level may still appear. In the configuration above, the root level is INFO, so INFO, WARN, and ERROR appear.