andrew Flower

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.

Tokyo skyline

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?
info Set the StatusLogger to DEBUG — setting a system property that tells Log4j to log everything it's doing will be helpful in debugging your setup if you have problems. The easiest way to set this property is on the command line when running your app.
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.

example1.log:
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.


Donate

Bitcoin

Zap me some sats

THANK YOU

Creative Commons License
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.