By Pavel Sheida
Last Validated on May 31 2021 · Originally Published on May 31, 2021 · Viewed 1.1k times

Introduction

The log4j is a logging framework for Java. It supports multiple logging targets, structured output, logging hierarchy, and everything a modern logging framework should support. In the long list of its features, you can find:

  • Custom formatting layout
  • Support of structured logging, that allows them to be treated as data sets rather than text
  • Compatibility with asynchronous applications and systems
  • Multiple logging targets, such as files, console, console, email, and many other outputs

Log4j is useful in the simplest small applications as well as in large and complex ones. Due to its rich configuration abilities, you can use it in all your projects.

Also, Log4j has great documentation, a lot of related materials, and a big developer community.

In the tutorial you learn how to:

  • Dreate a Command Line Application project in IntelliJ IDEA
  • Install log4j and its dependencies
  • Create and configure the log4j logger with structured output
  • Integrate the logger into the Java Command Line Application

Prerequisites

For this tutorial you will need:

  • Ubuntu 20.04 distribution including the non-root user with sudo access.
  • Java installed.
  • IntelliJ IDEA installed.

Step 1 — Creating a Project

To get started, you need to create a new project. To simplify the work, we are going to use a default IntelliJ template for the Console Applications, the Command Line App template.

You can create a Command Line Application Let's break it down into simple steps, the first thing you need to do is to open the New Project windows. There are several ways how to do it in the IntelliJ IDEA.

The first one is to select Create a new project in the welcome window.

The second one, If the IntelliJ is already open, you can follow the path on the top menu bar File > New > Project.

In the New Project window click on Java, and select the Command Line App template.


Step 2 — Installing Dependencies

Before starting work on the application, you need to install some dependency packages. IntelliJ provides multiple ways to do it. However, in the tutorial we will use Maven —  a software project management and comprehension tool. Based on the concept of a project object model.

The dependency management in Java may seem complicated, but we are going to break it down into small steps.

So, the first thing you have to do is to open the Project Structure window. You can do it using File > Project Structure. Alternatively, you can  press CTRL + SHIFT + ALT + S.

In the window that opens, select the Libraries tab, then press ALT + INSERT and select From Maven.

Now, you can enter a Maven package you'd like to install and press OK. The package will be available from any part of the project!

For our project, we are going to install the latest available version at the time of writing — org.apache.logging.log4j:log4j-1.2-api:2.11.2.


Step 3 — Creating a Logger

Preparatory to creating a logger, we have to import 2 packages: log4j.Logger and log4j.LogManager.

Main.java
package com.company;

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

public class Main {
    public static void main(String[] args) {

    }
}

At the moment, you are ready to create a logger. For the application, we are going to use the simplest way to do it in log4j — the LogManager class. It provides the getLogger method to get a unique logger per each class. Unlike the globally configured logger, the logger per class helps you to easily capture the source of the log message.

The getLogger method has many overloads. However, the most useful are the ones accepting string or Class as the only parameter. You can manually specify a class type or a string name for each logger. However, we are going to use the recommended way, the built-in tools to identify the caller class.

Main.java
package com.company;

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

public class Main {
    // create a static logger
    private static final Logger logger = LogManager.getLogger(Main.class);
    
    public static void main(String[] args) {

    }
}

The logger will use log4j's default log levels system. The system consists of the 6 levels:

  • Fatal — used for reporting about errors that are forcing shutdown of the application.
  • Error — used for logging serious problems occurred during execution of the program.
  • Warn  — used for reporting non-critical unusual behavior.
  • Info — used for informative messages highlighting the progress of the application for sysadmin and end user.
  • Debug — used for debugging messages with extended information about application processing.
  • Trace — used for tracing the code.

Step 4 — Creating a Config

Log4j provides 2 ways how to configure the logging system: programmatically or via a configuration file. You can get advanced information about each of them in the documentation.

In the tutorial we're going to configure the logger using log4j2.xml file.

The first thing to do is to create a log4j2.xml file in the root of your application, in the src folder. You can do it by pressing right-clicking the src tab in the sidebar and clicking the New option. The only thing left is to enter the log4j2.xml name in the opened window.

The log4j2.xml file should look like:

log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders></Appenders>
    <Loggers>
        <Root></Root>
    </Loggers>
</Configuration>

Now, let's write a config. The config is written in the XML language. If you are not familiar with the XML language, you may dive in the great XML tutorial by w3school.

In our case the config consists of 2 parts: setting up the loggers and setting up the appenders.

We've decided to add 3 logging targets for the application: the console and a file. In the config file, each of them will be represented by a separate XML element. Let's write the appenders in the config file.

The first appender is the console. It will log all data from the loggers.

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></Root>
    </Loggers>
</Configuration>

The second appender is the logs/all.log file. It will write all data from the loggers.

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>

        <File name="all_logs_file" fileName="logs/all.log">
            <PatternLayout>
                <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Root></Root>
    </Loggers>
</Configuration>

The third appender is the logs/important.log file. It will write all warnings and higher severity logs.

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>

        <File name="all_logs_file" fileName="logs/all.log">
            <PatternLayout>
                <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
        </File>

        <File name="important_logs_file" fileName="logs/important.log">
            <Filters>
                <ThresholdFilter level="warn"  onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout>
                <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Root></Root>
    </Loggers>
</Configuration>

Now, let's specify the minimum level for the root logger and appenders used by the logger.

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>

        <File name="all_logs_file" fileName="logs/all.log">
            <PatternLayout>
                <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
        </File>

        <File name="important_logs_file" fileName="logs/important.log">
            <Filters>
                <ThresholdFilter level="warn"  onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout>
                <pattern>%d %p %c{1.} [%t] %m%n</pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="console"/>
            <AppenderRef ref="all_logs_file"/>
            <AppenderRef ref="important_logs_file"/>
        </Root>
    </Loggers>
</Configuration>

Step 5 — Creating Extra Classes

We're going to use 2 extra classes. To create a new file for a class, you can right-click your package's folder in the sidebar and click New, then select Java Class.

The first file will contain a Person class. You can see its code in the snippet below.

Person.java
package com.company;

public class Person {
    public String name;
    public String lastName;

    public Person(String name, String lastName) {
        this.name = name;
        this.lastName = lastName;
    }

    public String toString() {
        return "[" + name + " " + lastName + "]";
    }
}

The second file will contain a Car class. You can see its code in the snippet below.

Car.java
package com.company;

public class Car {
    public String model;
    public int yearReleased;
    public Person owner;

    public Car(String model, int yearReleased, Person owner) {
        this.model = model;
        this.yearReleased = yearReleased;
        this.owner = owner;
    }

    public String toString()
    {
        return "[" + model + "(" + yearReleased + ")" + ", owned by " + owner.toString() + "]";
    }
}

Step 6 — Logging

To demonstrate how the logger works, we will log some simple messages, at least one of each level. It's a pretty straightforward task, because of the clear log4j API.

More specifically, log4j logger provides 6 methods for logging: trace,debug, info, warn, error, and fatal.

Main.java
package com.company;

import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import java.time.LocalDateTime;

public class Main {
    // create a static logger
    private static final Logger logger = LogManager.getLogger(Main.class);

    public static void main(String[] args) {
        // create 2 persons
        var person1 = new Person("Jonh", "Gold");
        var person2 = new Person("James", "Miller");
        // create 2 cars
        var car1 = new Car("Tesla Model S", 2020, person1);
        var car2 = new Car("Tesla Model X", 2020, person2);
        // logging
        logger.debug("Some debug log");
        logger.info("Person1: " + person1);
        logger.info("Car2: " + car2);
        logger.warn("Warning accrued at " + LocalDateTime.now());
        logger.error("Error accrued at " + LocalDateTime.now());
        logger.fatal("Serious problem with car " + car1 + " accrued at " + LocalDateTime.now());
    }
}

Now, let's build and run the program. You can simply do this by pressing SHIFT + F10.

After the execution, your console's output should look like:

Output
08:10:14.035 [main] DEBUG com.company.Main - Some debug log
08:10:14.057 [main] INFO  com.company.Main - Person1: [Jonh Gold]
08:10:14.096 [main] INFO  com.company.Main - Car2: [Tesla Model X(2020), owned by [James Miller]]
08:10:14.173 [main] WARN  com.company.Main - Warning accrued at 2021-05-31T08:10:14.165315085
08:10:14.182 [main] ERROR com.company.Main - Error accrued at 2021-05-31T08:10:14.182102075
08:10:14.187 [main] FATAL com.company.Main - Serious problem with car [Tesla Model S(2020), owned by [Jonh Gold]] accrued at 2021-05-31T08:10:14.184349032

Now, let's check the logs written in the all.log file.

all.log
2021-05-31 08:10:14,035 DEBUG c.c.Main [main] Some debug log
2021-05-31 08:10:14,057 INFO c.c.Main [main] Person1: [Jonh Gold]
2021-05-31 08:10:14,096 INFO c.c.Main [main] Car2: [Tesla Model X(2020), owned by [James Miller]]
2021-05-31 08:10:14,173 WARN c.c.Main [main] Warning accrued at 2021-05-31T08:10:14.165315085
2021-05-31 08:10:14,182 ERROR c.c.Main [main] Error accrued at 2021-05-31T08:10:14.182102075
2021-05-31 08:10:14,187 FATAL c.c.Main [main] Serious problem with car [Tesla Model S(2020), owned by [Jonh Gold]] accrued at 2021-05-31T08:10:14.184349032

At the end, let's check the logs written in the important.log file.

important.log
2021-05-31 08:10:14,173 WARN c.c.Main [main] Warning accrued at 2021-05-31T08:10:14.165315085
2021-05-31 08:10:14,182 ERROR c.c.Main [main] Error accrued at 2021-05-31T08:10:14.182102075
2021-05-31 08:10:14,187 FATAL c.c.Main [main] Serious problem with car [Tesla Model S(2020), owned by [Jonh Gold]] accrued at 2021-05-31T08:10:14.184349032

Conclusion

Proper logging can greatly assist in the support and development of your application. This may seem like a daunting task, but log4j is a fast and configurable logging framework that greatly simplifies the task.

In the tutorial, you have configured your logging system with multiple logging targets for a Java console application with log4j.

Now developing and maintaining your Java applications will be much easier!