Becoming log4j2 Configuration Expert – Part 1

I’ve used the log4j2 for some time. But from now on I am going to consider myself as a beginner. So let’s learn what it is and how it supposed to use, particularly log4j2 configuration. At the end of this series, I’m planning to become a log4j2 expert. What about you?

What is log4j2

log4j2 is a logging library for java and its the successor of log4j 1. It turns out it has many improvements over its predecessor and competing libraries such as Logback. Both you and I are beginners, why bother about those for now?

Breakdown of log4j2 distribution

I’m referring to the 2.13.3 release. Trust me it has 70 jar files there. Let’s ignore Javadoc and sources jars, still, there are 24 jars to be analyzed. So It turns out as beginners we only need to focus at its API and general implementation. So we need to grab them in your favourite dependency management or build tool. Mine is maven, so maven coordinates of two jars are as follows.

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.13.3</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.13.3</version>
</dependency>

You know its enough for many dependency management tools. Also, log4j2-api is a transitive dependency of log4j2-core, so you could skip it. But for maven its good practice to have compile-time dependencies listed.

First application with log4j2

Becoming an expert is not easy. It requires regular practice. So let’s dig in. Let’s log some messages with log4j2. I know you can get it to work with your favourite build tool in your favourite IDE. I’ll give you some tips if you are using maven. log4j2 has a BOM, Sounds cool right? So here is my pom file.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.aptkode</groupId>
    <artifactId>log4j2-playground</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-bom</artifactId>
                <version>2.13.3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>5.7.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Log4j2 Configuration

We need a configuration to make log4j2 work as we want. I think most of the power lies in this configuration. I’m not going to explain how log4j2 resolve its configuration file. You can read well-documented configuration resolution here. Log4J2 supports XML, JSON and YAML configuration formats. I’m going to use XML. The XML format is widely used and easy to start. I’ll explain the main elements in the configuration very briefly. In later articles, we’ll explore them much broader.

Appenders

Appender is responsible for delivering log events to an output. Console appender emits log events to the console (standard output). Same as console appender there is file appender which will output log events to a file. So there are inbuilt appenders. Also, we could have our appenders or third-party ones.

Configuring appenders is different from each other. We will examine how we can configure different appenders in future articles.

Loggers

A logger is responsible for routing log events to appender by name and level. There is a logger hierarchy which determines which logger will emit the log event. We’ll discuss the logger hierarchy later.

Root Logger

There is a particular logger called root logger. If you don’t explicitly define it, still it is activated with an ERROR log level. If there is no configuration to be found, root logger gets configured automatically.

Additivity

As I mentioned in the Loggers, there is a hierarchy of loggers. The only option we can avoid propagating log event in the tree is additivity. We can set it to false otherwise. The default value for additivity for any logger is True. We’ll explore this by applying.

Log Levels

Why we need log levels? Assume there are lots of things happening in a production application. There may be functions that get executed regularly and no functional importance rather technical/performance aspect. So we need to turn on and off particular logs based on the situation. In a production system, we only need information and errors. So we could have different configurations for prod and dev environments. When developing, we need to identify application behaviour through the log. In this case, debug and trace log levels are required.

There are inbuilt log levels in log4j2. We could have our log levels as well. Following diagram shows how the log event level and matching logger log level determines whether to log or not.

log4j2 log level assignment

Becoming log4j2 expert by practice

I think now we know very little about how it works. Also, I have the pom ready. I assume you have your dev environment up. So let’s dive in.

Creating the first logger

We need to use the LogManager class to create a logger. You can pass a name to its getLogger method and viola you will get a logger instance. Remember that every time you invoke this with the same argument, it will return the same logger instance, meaning it is a singleton logger for your application.

So here is my application, pretty simple right?

package com.aptkode.log4j2.playground;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Test {

    private static final Logger logger = LogManager.getLogger("x.y.z");

    public static void main(String[] args) {
        logger.info("hello!");
    }

}

And here is the configuration, mind the location of the configuration file. It should be in the application classpath. I placed mine in src/main/resources. Also it should have the log4j2.xml name.

<?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>
        <Logger name="x.y.z" level="info">
            <AppenderRef ref="Console"/>
        </Logger>
        <Root level="info"> <!-- default level of root logger is error -->
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>
log output with additivity

Let’s try the additivity changed.

<?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>
        <Logger name="x.y.z" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <Root level="warn"> <!-- default level of root logger is error -->
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

What did you expect? Here we will get only one line. So as we mentioned without additivity log4j2 stops propagating log event up in the hierarchy.

I’ll give a tip. You can change the log4j2 log level of configuration element by changing the status value. By setting it to a more detail level, it will log more information about log4j2 internals.

log4j2 debug logs – by setting status to DEBUG

Final words

We have taken a tiny step to the journey of log4j2 learning. To help with your log4j2 setup, I’ve built a simple tool to determine log level assignment and logger assignment. You can go to our Aptkode dev tools and try out.

Latest Posts

Leave a Comment

Your email address will not be published. Required fields are marked *