Log4j2
a crash course...
Recently I had to upgrade some projects to use Log4j2, and although it's quite straight-forward in the simple use-case, due to our programmatic usage of the Log4j1.x API, there were some complications. I thought it may benefit others if I documented some of the basic usage.
Firstly, I must note that the Official Log4j2 Manual covers nearly everything in great detail, but there is so much content that it might be overwhelming to read and take in in just one go. Additionally, some of the more complicated use-cases like creating your own PatternConverter or configuring Loggers programmatically didn't seem to be as well documented. So I'll cover a bit of that too in later sections.

Basic Log4j2 configuration
All Log4j2 configs are written in either XML, JSON, Yaml, or a good old .properties
file. I will be using XML in these examples. The configuration file should be named
log4j2.xml and exist somewhere in your classpath.
A log configuration is surrounded by <Configuration>
tags as seen below. Note the status
attribute in the excerpt below.
As well as your loggers, Log4j creates a StatusLogger
for logging
when loading your configurations etc. In practise you would probably use a configuration status
level of INFO
or ERROR
, but while setting up a configuration it may be useful to set this to
DEBUG
.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG" name="example-config">
</Configuration>
When running with the above config, amongst many DEBUG messages, you will notice this warning below indicating that the config is missing a list of Loggers. This is an example of the benefit of using the configuration status setting when building a log config.
2015-02-07 15:47:37,862 WARN No Loggers were configured, using default. Is the Loggers element missing?
java -DLog4jDefaultStatusLevel=DEBUG MyApp
An Example Config File
Let's jump into the deep-end with an example config file, and I'll go through it section by section, explaing the purpose of each construct. Note that this is not necessarily a perfect or great config, but it will demonstrate the fundamentals.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR" name="example-config">
<Properties>
<Property name="LOG_DIR">${sys:web.root}/logs</Property>
<Property name="ARCHIVE">${LOG_DIR}/archive</Property>
<Property name="PATTERN">%-5level %d [%t] %c:%M(%L): %m%n</Property>
</Properties>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="${PATTERN}"/>
</Console>
<RollingFile name="fileWriter"
fileName="${LOG_DIR}/example1.log"
filePattern="${ARCHIVE}/example1.log.%d{yyyy-MM-dd-hh-mm}.gz">
<PatternLayout pattern="${PATTERN}"/>
<TimeBasedTriggeringPolicy/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="fileWriter"/>
</Root>
<Logger name="com.andrew_flower.example.Log4j2Example1" level="TRACE" additivity="false">
<AppenderRef ref="STDOUT"/>
</Logger>
<Logger name="com.andrew_flower.example.Other" level="ERROR" additivity="true"/>
</Loggers>
</Configuration>
Properties — The Properties
section allows you to define a set
of Key/Value properties that can be used elsewhere in the config file. This is not
a foreign concept. In the example, the LOG_DIR
property demonstrates that
one can refer to System Properties by using the sys:
prefix. There are a
number
of other prefixes defined
here and
here. The
ARCHIVE
property then uses the value of the LOG_DIR
property.
Appenders — These define possible output methods for loggers in this config.
The appenders are given a name
attribute, which is an any arbitrary identifier.
The Console
appender shown here, called "STDOUT" will output logs to the standard
output stream, which is your terminal/console usually.
The RollingFile
appender is very common and useful. It can be configured at
chosen events (based on a Triggering Policy) to stop writing to the current file,
and rather rename/move it. Thereafter it will start writing to a new file of the original
name. The fileName
attribute defines the filepath of the active logfile.
The filePattern
attribute defines the naming scheme of rolled-over files. If
the path does not exist, Log4j will try to create the missing directories. In this case,
the filePattern defines a minutely rollover scheme. The
TimeBasedTriggeringPolicy
uses the filePattern to determine when to rotate. Another useful triggering policy is the
SizeBasedTriggeringPolicy
which rollsover according to a maximum filesize.
Layouts — These don't have a top-level entry, but they are defined within
every appender. Their purpose is to defines how a log entry is written to a logfile/appender
This example only demonstrates the
PatternLayout
. In most common case, your application will write single readable
plain-text lines to a logfile as in this example. The
PatternLayout
lets you define the column layout for each line using
special
symbols
called Conversion Patterns. There are other layouts such as
JSONLayout
or
XMLLayout
, to name a couple, which are useful respectivle for output to JSON files,
or Network Sockets for example. This obviously depends on the type of Appender.
Loggers — Finally we define actual loggers. In Java code, you will refer
to these loggers by their names. Traditionally these have been named using Java package/class
names, due to the hierarchial additivity in Log4j. This means that entries logged by
a logger named com.andrew_flower
can automatically be logged by a Logger named
com
. Whether this occurs, depends on the additivity
.
Note that there should always be a RootLogger
, and it will actually exist
automatically if not defined. Logs not caught be any other logger will be caught be the
RootLogger.
Read the Architecture
page
for details and examples of parent loggers, levels and additivity.
In the example above the root logger will log all INFO
entries using the
fileWriter
appender.
The com.andrew_flower.example.Log4jTest
logger is more verbose, logging
all TRACE
(and less verbose levels), but it does so to STDOUT console appender.
Note
here that it specifies additivity="false"
, which means that the entry will not be
logged
by parent/ancestor loggers (ie. the RootLogger
here).
Finally the com.andrew_flower.example.Other
logger will capture all ERROR
logs and
pass them along to the RootLogger
. Note that it doesn't specify an appender ref. It
just increases
the verbosity for that class.
Using the loggers
Now that we've deconstructed a Log4j2 configuration file in parts and understand, in theory, how it should work, we should test it out with some Java code. Have a look at the following pointless code which uses our config file.
package com.andrew_flower.example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4jTest {
private final static Logger log = LogManager.getLogger(Log4jTest.class);
public static void main(String[] args) {
log.entry();
log.info("log INFO");
warningMethod();
log.error("log ERROR");
new Other("first");
log.exit();
}
private static void warningMethod(){
log.entry();
log.warn("log WARN");
log.exit();
}
}
class Other {
private final static Logger log = LogManager.getLogger(Other.class);
public Other(String msg) {
log.entry(msg);
log.info("log INFO");
log.warn("log WARN");
log.error("log ERROR");
log.exit();
}
}
Running the above code will result in two sets of logs, due to our log4j2.xml appender
configuration. Some logs are output to stdout
and others to the file
example1.log
. Usually even INFO
lines would go to the file,
but we configured the xx.Other
logger to only send ERROR
(and worse) logs to the
RootLogger,
using the additivity attribute.
ERROR 2015-02-10 17:59:14,537 [main] com.andrew_flower.example.Other:<init>(35): log ERROR
Stdout:
TRACE 2015-02-10 17:59:14,532 [main] com.andrew_flower.example.Log4jTest:main(13): entry
INFO 2015-02-10 17:59:14,535 [main] com.andrew_flower.example.Log4jTest:main(14): log INFO
TRACE 2015-02-10 17:59:14,535 [main] com.andrew_flower.example.Log4jTest:warningMethod(22): entry
WARN 2015-02-10 17:59:14,535 [main] com.andrew_flower.example.Log4jTest:warningMethod(23):log WARN
TRACE 2015-02-10 17:59:14,536 [main] com.andrew_flower.example.Log4jTest:warningMethod(24): exit
ERROR 2015-02-10 17:59:14,536 [main] com.andrew_flower.example.Log4jTest:main(16): log ERROR
TRACE 2015-02-10 17:59:14,543 [main] com.andrew_flower.example.Log4jTest:main(18):exit
The code also demonstrates special methods entry()
and exit()
which are
logged as TRACE
entries and helping when debbuging program flow. The
Log4j2Example1
class using a logger that logs very verbosely but only to STDOUT.
Maven Configuration
For those of you using Apache Maven, you only have to add two dependencies for Log4j2 as shown below. I've also added the Apache repositories containing these dependencies in case you don't have them.
<project ..>
...
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
...
<repositories>
<repository>
<id>central</id>
<url>http://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>apache.snapshots</id>
<name>Apache Release Distribution Repository</name>
<url>
http://repository.apache.org/snapshots
</url>
</repository>
</repositories>
...
</project>
That's the end of this crash course in Log4j2. I wrote this quite quickly, so I hope it makes sense and is helpful in some way. I'll probably be tidying this up as I read it through. I also plan to add some more information about create Log4j plugins and other customizations soon.
Anyways, leave some comments if things are confusing or useful and I'll try respond. Also if you have any questions I'll try to respond.
Bitcoin

Zap me some sats


This blog post itself is licensed under a Creative Commons Attribution 4.0 International License. However, the code itself may be adapted and used freely.