Log4j2 offers robust and flexible configuration options for managing application logs effectively. Understanding these advanced features allows for fine-grained control over log output, performence, and maintenance.
1. Configuring Log Output to Files
To direct log events to a file, especially with rolling capabilities for log rotation, the RollingFile appender is used. This appender manages log files based on defined policies, such as time or size.
<RollingFile name="FileAppender" fileName="/var/log/app/application.log"
filePattern="/var/log/app/archive/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</RollingFile>
In this example, fileName specifies the active log file path, while filePattern defines the naming convention for archived log files. The TimeBasedTriggeringPolicy ensures log files roll over based on a time interval, here daily (interval="1" for the yyyy-MM-dd implicit pattern).
2. Utilizing Configuration Variables
For improved maintainability and reusability, Log4j2 supports defining variables within the configuration. These variables can then be referenced throughout the configuration XML.
<Properties>
<Property name="logRootDir">/opt/app/logs</Property>
<Property name="currentLogPath">${logRootDir}/app.log</Property>
<Property name="archivePattern">${logRootDir}/archive/$${date:yyyy-MM}/app-%d{yyyy-MM-dd}-%i.log.zip</Property>
</Properties>
<RollingFile name="DynamicFileAppender" fileName="${currentLogPath}"
filePattern="${archivePattern}">
<Policies>
<TimeBasedTriggeringPolicy />
</Policies>
<PatternLayout pattern="%d{ISO8601} %-5level %c{1.} [%t] %msg%n"/>
</RollingFile>
Here, logRootDir, currentLogPath, and archivePattern are defined once and referenced using ${propertyName} syntax.
3. Dynamic Configuration with System Properties
Log4j2 can dynamically pull values from system properties, allowing for external control over logging paths or other settings without modifying the configuration file. This is achieved using ${sys:propertyName}.
// Set a system property programmatically or via JVM arguments (-Dlog.base.path=/tmp/app-logs)
System.setProperty("app.log.base.path", "/temp/application-logs");
And in the Log4j2 configuration:
<Properties>
<Property name="baseLogDirectory">${sys:app.log.base.path}</Property>
<Property name="mainLogFile">${baseLogDirectory}/application.log</Property>
</Properties>
<RollingFile name="SystemPropAppender" fileName="${mainLogFile}"
filePattern="${baseLogDirectory}/archived/$${date:yyyy-MM}/app-%d{MM-dd}-%i.log.gz">
<Policies>
<TimeBasedTriggeringPolicy />
</Policies>
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
</RollingFile>
4. Advanced Rollover Strategies and Cleanup
The DefaultRolloverStrategy provides powerful capabilities for managing log file archives, including retention policies. The <Delete> action allows for sophisticated cleanup based on various conditions.
<DefaultRolloverStrategy max="10">
<Delete basePath="${sys:app.log.base.path}/archived" maxDepth="2">
<IfFileName glob="*.log.gz" />
<IfLastModified age="30d" />
<IfAccumulatedFileSize exceeds="50 GB" />
<IfAccumulatedFileCount exceeds="100" />
</Delete>
</DefaultRolloverStrategy>
This strategy will keep a maximum of 10 archived files (from max="10"). The Delete element further specifies that within maxDepth="2" subdirectories of basePath, any files matching *.log.gz that are older than 30 days, or contribute to a total archive size exceeding 50GB, or cause the total file count to exceed 100, should be deleted.
5. Combined Generation and Compression Policies
Log4j2 allows combining multiple triggering policies and configuring compression for archived logs. This example demonstrates rolling based on both time and size, with gzip compression.
<RollingFile name="CombinedPolicyAppender" fileName="${mainLogFile}"
filePattern="${baseLogDirectory}/archived/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
<Policies>
<!-- Roll over every 6 hours, aligned to 00, 06, 12, 18 -->
<TimeBasedTriggeringPolicy interval="6" modulate="true"/>
<!-- Roll over if current log file exceeds 250 MB -->
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
<!-- Retain a maximum of 20 compressed archive files -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
Here, a new compressed log file (.gz) will be created either every six hours or when the current log file reaches 250MB. modulate="true" with interval="6" ensures the rollover occurs at specific 6-hour marks (e.g., 00:00, 06:00, 12:00, 18:00) rather than simply every 6 hours from application start. The DefaultRolloverStrategy max="20" ensures only the 20 most recent archive files are retained.
6. Controlling Third-Party Library Log Levels
Applications often depend on numerous third-party libraries that can produce excessive log output. Log4j2 allows setting specific log levels for these external components to reduce verbosity.
<Loggers>
<Logger name="org.springframework" level="WARN"/>
<Logger name="io.netty" level="ERROR"/>
<Logger name="org.hibernate" level="INFO"/>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Root>
</Loggers>
This configuration ensures that logging from org.springframework is set to WARN, io.netty to ERROR, and org.hibernate to INFO, overriding the Root logger's info level for these specific packages.
7. Layout Format Configuration
Log4j2's PatternLayout provides extensive options for formatting log messages. It allows developers to define the exact structure and content of each log entry using conversion patterns. For a comprehensive list of available pattern converters, refer to the official Log4j2 PatternLayout documentation.
8. Asynchronous Logging for Performance
Asynchronous loggers can significantly improve application performance by offloading log event processing to a separate thread. This minimizes the impact of logging on the main application threads. To use asynchronous logging, the disruptor library (version 3.0.0 or higher) must be included in the classpath.
There are two main ways to configure asynchronous logging:
-
Asynchronous Loggers: Specific loggers can be configured as async.
<Loggers> <AsyncLogger name="com.example.AsyncService" level="trace" includeLocation="false"> <AppenderRef ref="Console"/> <AppenderRef ref="FileAppender"/> </AsyncLogger> <Root level="info"> <AppenderRef ref="Console"/> <AppenderRef ref="FileAppender"/> </Root> </Loggers> -
Asynchronous Root Logger: The entire logging system can be made asynchronous by using
asyncRoot.<Loggers> <AsyncRoot level="trace" includeLocation="false"> <AppenderRef ref="Console"/> <AppenderRef ref="FileAppender"/> </AsyncRoot> </Loggers>
includeLocation="false" is often recomended for asynchronous loggers as capturing location information (%l, %L, %M) can be very expensive and negate some of the performance benefits.
9. Managing Logger Hierarchies with additivity
Log4j2 loggers exist in a hierarchy, where events typically flow upwards from child loggers to parent loggers, eventual reaching the root logger. The additivity attribute controls this behavior. If additivity="false" is set on a logger, events processed by that logger will not be passed up to its parent loggers.
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileAppender"/>
</Root>
<!-- This logger will only log to its configured appender, not to the Root's appenders -->
<Logger name="com.mycompany.service" level="debug" additivity="false">
<AppenderRef ref="ServiceFileAppender"/>
</Logger>
</Loggers>
In this setup, com.mycompany.service log events will only be handled by ServiceFileAppender and will not be forwarded to the appenders attached to the Root logger.
10. Implementing Logging Filters
Log4j2 filters provide a powerful mechanism to control which log events are processed by an appender or a logger. Filters can be applied at various levels (Logger, Appender, or even globally) and can accept, deny, or remain neutral regarding an event.
onMatchandonMismatchActions:ACCEPT: The event will be processed, and no further filters in the chain will be evaluated.DENY: The event will be dropped, and no further filters in the chain will be evaluated.NEUTRAL: The filter does not make a final decision, and the event passes to the next filter in the chain (or is processed if no more filters).
<Filters>
<!-- Only process events with TRACE level or higher. Deny anything below TRACE. -->
<ThresholdFilter level="TRACE" onMatch="NEUTRAL" onMismatch="DENY"/>
<!-- Only process messages containing the phrase "transaction ID" -->
<RegexFilter regex=".*transaction ID.*" onMatch="NEUTRAL" onMismatch="DENY"/>
<!-- Only allow logging between 08:00 and 17:00 -->
<TimeFilter start="08:00:00" end="17:00:00" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
When multiple filters are chained (as shown above), using NEUTRAL for onMatch allows an event to pass through successive filters. An event must satisfy all NEUTRAL filters to be processed. If an ACCEPT is encountered, the event is immediately passed on. If a DENY is encountered, the event is immediately discarded.