1. Introduction

Log4J is a popular, open-source logging framework written in Java. Various Java-based applications widely use Log4j. Moreover, it’s thread-safe, fast, and provides a named Logger hierarchy. Log4j is distributed under the open-source Apache Software License.

Log4j 1.x reached the end of life on August 5, 2015. Therefore, as of today, Log4j2 is the latest upgrade to Log4j.

In this tutorial, we’ll learn about Log4j and how to configure the core Log4j components using the log4j.properties file in Java.

2. Maven Setup

We’ll need the log4j-core dependency in our pom.xml to start with:

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

The latest version of log4j-core can be found here.

3. The Log4j API

The Log4j API provides the mechanism to pass on the logging information based on various levels of priorities and direct it to various destinations such as files, consoles, databases, etc. It also supports filtering log events before passing them to loggers or appenders.

The Log4j API has a layered architecture that provides two types of objects in the Log4j framework – core objects and support objects.

4. Log4j Components

There are three main components of Log4j – loggers, appenders, and layouts – that could be used together to print the customized log statements at the desired destinations. Let’s look at them in brief.

4.1. Logger

The Logger object is responsible for representing the logging information. It’s the first mandatory layer in Log4j architecture. The Logger class is defined in package org.apache.log4j.

Generally, we create one Logger instance per application class to log important events belonging to that class. Also, we generally create this instance at the beginning of the class using a static factory method that accepts the class name as a parameter:

private static final Logger logger = Logger.getLogger(JavaClass.class.getName());

Subsequently, we can use various methods of the Logger class to log or print important events depending on their categories. These methods are trace(), debug(), info(), warn(), error(), fatal(). These methods determine the level of a logging request.

The priority order of the Logger methods is: TRACE < DEBUG < INFO < WARN < ERROR < FATAL. Therefore, these methods print the log messages depending on the logger level set in the log4j.properties file. This means if we set the logger level as INFO, then all the INFO, WARN, ERROR, and FATAL events will be logged.

4.2. Appender

Appender denotes the destination of log output. We can print the log out to multiple preferred destinations using Log4j like console, files, remote socket server, database, etc. We refer to these output destinations as Appenders. Moreover, we can attach multiple appenders to a Logger.

Appenders work according to the appender additivity rule. *This rule states that the output of a log statement of any Logger will go to all of its appenders and its ancestors –* *the appenders that are higher in the hierarchy*.

Log4j has multiple appenders defined for files, consoles, GUI components, remote socket servers, JMS, etc.

4.3. Layout

We use layouts for customizing the format of the log statements. We can do this by associating a layout with the already defined appender. Thus, a combination of layout and appenders helps us send the formatted log statements to the desired destinations.

We can specify the format of log statements using conversion patterns. The class PatternLayout explains more about conversion characters that we can use based on our needs.

We’ll also understand about few of the conversion characters through examples in the following sections.

5. The log4j.properties File

We can configure Log4j using XML or the properties file. The log4j.properties file stores the configurations in key-value pairs.

The default name of the log4j properties configuration file is log4j.properties. The Logger looks for this file name in the CLASSPATH. However, if we need to use a different configuration file name, we can set it using the system property log4j.configuration.

The log4j.properties file contains the specifications of appenders, their names and types, and layout patterns. It also contains specifications about the default root Logger and its log levels.

6. Syntax of the log4j.properties File

In a general log4j.properties file, we define the following configurations:

  • The root logger and its level. We also provide a name for the appender here.
  • Then, we assign a valid appender to the defined appender name.
  • Finally, we define the layout, target, level, etc., for the defined appender.

Let’s see the syntax of a general log4.properties file:

# The root logger with appender name 
log4j.rootLogger = DEBUG, NAME
  
# Assign NAME a valid appender  
log4j.appender.NAME = org.apache.log4j.FileAppender

# Define the layout for NAME
log4j.appender.NAME.layout=org.apache.log4j.PatternLayout  
log4j.appender.NAME.layout.conversionPattern=%m%n  

Here, NAME is the name of the Appender. As discussed earlier, *we can attach multiple appenders to a Logger to direct logs to different destinations.*

7. Examples

Now, let’s understand the log4j.properties file configurations for different appenders with the help of some examples.

7.1. Sample Program

Let’s start with an example application that logs some messages:

import org.apache.log4j.Logger;

public class Log4jExample {

    private static Logger logger = Logger.getLogger(Log4jExample.class);

    public static void main(String[] args) throws InterruptedException {
        for(int i = 1; i <= 2000; i++) {
            logger.info("This is the " + i + " time I say 'Hello World'.");
            Thread.sleep(100);
        }
    }
}

The application is a simple one – it writes some messages in a loop, with a short delay between iterations. It has 2,000 iterations, and there’s a pause of 100 ms in each iteration. Therefore, it should take around three and a half minutes to finish the execution. We’ll use this application in our examples below.

7.2. Console Logging

The console is the default place for logging messages if no configuration file is located. Let’s create a log4j.properties configuration for ConsoleAppender with the root logger and also define the logging level for it:

# Root Logger
log4j.rootLogger=INFO, stdout

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

Here, we’ve defined a log4j.properties file with the following specifications:

  • We’ve defined the level of the root logger as INFO. This means that all the log events with level INFO and above will be logged. We’ve also defined a name for the appender as stdout.
  • Since we want to direct the logs to the console, we assigned the Appender as org.apache.log4j.ConsoleAppender and the target as System.out.
  • Finally, we’ve specified the format for PatternLayout in which we want to print the logs using ConversionPattern.

Let’s also understand the meaning of each of the conversion characters in the ConversionPattern that we’ve used:

  • %d adds the timestamp in the defined format.
  • %-5p adds the log-level information to each log statement. It signifies that the priority of the logging event should be left justified to a width of five characters.
  • %c{1} prints the qualified class name, optionally followed by package names (precision qualifier), that is, logging the specific log statement.
  • %L prints the line number of the specific log event.
  • %m prints the actual log message.
  • %n adds a new line after every log statement.

Thus, when we run our sample application, we get the following lines printed on the console:

2023-08-01 00:27:25 INFO Log4jExample:15 - This is the 1 time I say 'Hello World'.
...
...
2023-08-01 00:27:25 INFO Log4jExample:15 - This is the 2000 time I say 'Hello World'.

The documentation for the PatternLayout class explains more about conversion characters that we can use based on our needs.

7.3. Multiple Destinations

As discussed earlier, we can redirect the log events to multiple destinations:

# Root logger  
log4j.rootLogger=INFO, file, stdout  
  
# Direct to a file
log4j.appender.file=org.apache.log4j.RollingFileAppender  
log4j.appender.file.File=C:\\Baeldung\\app.log  
log4j.appender.file.MaxFileSize=5KB  
log4j.appender.file.MaxBackupIndex=2  
log4j.appender.file.layout=org.apache.log4j.PatternLayout  
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n  
   
# Direct to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender  
log4j.appender.stdout.Target=System.out  
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout  
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n  

Here, we’ve used two appenders to redirect the log messages to both the file and console. Also, we’ve assigned a RollingFileAppender to our file Appender. We use a RollingFileAppender when we know that the log files may grow in size over time.

In our example above, we’ve used RollingFileAppender, which rolls the log files based on both size and the count of log files using the MaxFileSize and the MaxBackupIndex parameters. Thus, the log file will roll when its size reaches 5KB, and we’ll keep a maximum of two rolled log files as backup.

When we run our sample application, we obtain the following files containing the same log statements as in the previous example:

31/01/2023  10:28    138 app.log
31/01/2023  10:28  5.281 app.log.1
31/01/2023  10:28  5.281 app.log.2

We can find a detailed explanation of the execution in our examples on rolling log files based on size.

8. Conclusion

In this article, we’ve explored Log4j and its three components – loggers, appenders, and layouts. We’ve also understood the syntax of a log4j.properties file and some simple examples of configuring a log4j.properties file.

As always, the examples that accompany the article are available over on GitHub.