Java Log4j tutorial

Java Log4j tutorial defines logging, introduces Log4j library, and demonstrates logging in several code examples.

Logging

Logging is the process of writing information into log files. Log files include information about various events that happened in operating system, software, or in communication.

Purpose of logging

Logging is done for the following purposes:

Logging is not limited to identifying errors in software development. It is also used in detecting security incidents, monitoring policy violations, providing information in case of problems, finding application bottlenecks, or generating usage data.

Which events to log

Events that should be logged include input validation failures, authentication and authorization failures, application errors, configuration changes, and application start-ups and shut-downs.

Which events not to log

Events that should not be logged include application source code, session identification values, access tokens, sensitive personal data, passwords, database connection strings, encryption keys, bank account and card holder data.

Logging best practices

The following are some best practices for doing logging:

Log4j

Apache Log4j is a Java-based logging utility. It is a project of the Apache Software Foundation. Log4j can be configured through Java code or in a configuration file. Configuration files can be written in XML, JSON, YAML, or properties file format.

Log4j components

Log4j has three main components: loggers, appenders, and layouts. Loggers are named destinations that capture capture log messages and send them to appenders. Appenders deliver log messages to their destinations, such as files, sockets, or consoles. Layouts are used to define the formatting of log messages.

Root logger

Log4j has a specific built-in logger called the root looger. It sits at the top of the hierarchy and is always present, event if not configured. It writes messages for all classes in the application. If we do not want messages from a specific logger to be passed to the root logger, we change the additivity attribute of the looger to false.

Package specific logging

We may want to limit logging to some Java package. In case of XML configuration, we set the package specific logging with the name attribute.

<Logger name="com.zetcode.work" level="info" additivity="false" >
    <AppenderRef ref="MyFile" />
</Logger>

With this logger, we deliver info level event messages to a log file destination from the com.zetcode.work package. With additivity set to false, the messages are not propagated to the root logger.

Log4j event levels

Levels are used for identifying the severity of an event. Levels are organized from most to least specific:

The following table shows how logging levels works.

Logging level rules
Event level Configuration level
ALL TRACE DEBUG INFO WARN ERROR FATAL OFF
ALL YES YES YES YES YES YES YES NO
TRACE YES YES NO NO NO NO NO NO
DEBUG YES YES YES NO NO NO NO NO
INFO YES YES YES YES NO NO NO NO
WARN YES YES YES YES YES NO NO NO
ERROR YES YES YES YES YES YES NO NO
FATAL YES YES YES YES YES YES YES NO

The table illustrates how event and configuration levels work. If we log a message at a Debug level and the configuration is Warn, the message is not passed. If we log a message at an Info level and the configuration level is Debug, then the message is passed to its destination.

Log4j basic example

In the first example, we set up Log4j for a simple Java console application.

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           └── JavaLog4jEx.java
    │   └── resources
    │       └── log4j2.xml
    └── test
        └── java

This is the project structure. The Log4j configuration file is located in the src/main/resources directory. We use the XML format.

pom.xml
<?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.zetcode</groupId>
    <artifactId>JavaLog4jEx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.9.1</version>
        </dependency>

    </dependencies>
    
</project>

This is the Maven pom.xml file. We include the log4j-core dependency.

JavaLog4jEx.java
package com.zetcode;

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

public class JavaLog4jEx {

    private static final Logger logger = LogManager.getLogger(JavaLog4jEx.class);
    
    public static void main(String[] args) {
        
        logger.info("The main() method is called");
        
        doWork();
        
        logger.warn("Warning message");
        logger.error("Error message");
    }
    
    public static void doWork() {
        
        // doing some work
        
        logger.info("The doWork() method is called");
    }
}

This is a simple Java console example.

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

We import the LogManager and Logger classes.

private static final Logger logger = LogManager.getLogger(JavaLog4jEx.class);

From the LogManager, we get the logger.

logger.info("The main() method is called");

doWork();

logger.warn("Warning message");
logger.error("Error message");

We generate information, warning, and error messages.

log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="layout">%d [%t] %-5level %logger - %m%n</Property>
    </Properties>
  
    <Appenders>
  
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${layout}" />
        </Console>     

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode" level="info" additivity="false" >
            <AppenderRef ref="Console" />
        </Logger>
    
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>
</Configuration>

Log4j is configured in the log4j2.xml. We have chosen the XML file format.

<Properties>
    <Property name="layout">%d [%t] %-5level %logger - %m%n</Property>
</Properties>

In the Properties tag, we set the logging directory and layout. The layouts define the format of the log message.

The pattern layout consists of conversion specifiers. Each specifier starts with a percent sign and is followed by optional format modifier and mandatory conversion character. The %d outputs the date of the logging event. The %t outputs the name of the thread that generated the logging event. The %-5level outputs the level of the logging event, where the level name has minimum of five characters and the characters are left justified. The %logger outputs the name of the logger that published the logging event. The %m prints the application message associated with the logging event and the %n is the platform dependent line separator character or characters.

<Appenders>

    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout pattern="${layout}" />
    </Console>     

</Appenders>

Appenders are objects which define where logging messages are sent. We define one console appender; it writes messages to the standard output using the above mentioned layout.

<Loggers>

    <Logger name="com.zetcode" level="info" additivity="false" >
        <AppenderRef ref="Console" />
    </Logger>

    <Root level="error">
        <AppenderRef ref="Console" />
    </Root>    

</Loggers>

We have two loggers. The com.zetcode logger has level info and the root logger has level error. Both loggers use the Console appender, e.g. they deliver messages to the console. With additivity set to false, the com.zetcode's messages are not propagated to the root logger. In other words, the messages are not printed twice to the console.

2017-11-17 15:17:36,899 [main] INFO  com.zetcode.JavaLog4jEx - The main() method is called
2017-11-17 15:17:36,903 [main] INFO  com.zetcode.JavaLog4jEx - The doWork() method is called
2017-11-17 15:17:36,903 [main] WARN  com.zetcode.JavaLog4jEx - Warning message
2017-11-17 15:17:36,903 [main] ERROR com.zetcode.JavaLog4jEx - Error message

After running the example, we have these messages in the console.

Log4j basic example II

In the next example, we explain additional features of Log4j. We will write messages to a file and define a package-specific logger.

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── main
    │   │           │   └── JavaLog4jEx.java
    │   │           └── work
    │   │               └── MyWork.java
    │   └── resources
    │       └── log4j2.xml
    └── test
        └── java

This is the project structure.

pom.xml
<?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.zetcode</groupId>
    <artifactId>JavaLog4jEx2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.9.1</version>
        </dependency>

    </dependencies>
    <name>JavaLog4jEx2</name>
</project>

This is the pom.xml file.

JavaLog4jEx2.java
package com.zetcode.main;

import com.zetcode.work.MyWork;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


public class JavaLog4jEx2 {

    private static final Logger logger = LogManager.getLogger(JavaLog4jEx2.class);
    
    public static void main(String[] args) {
        
        logger.info("The main() method is called");
        
        doJob();
        
        MyWork mw = new MyWork();
        mw.doMyWork();
    }
    
    public static void doJob() {
        
        // doing some job
        
        logger.info("The doJob() method is called");
    }
}

This is the main application file. It calls a couple of methods which do some logging.

MyWork.java
package com.zetcode.work;

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

public class MyWork {

    private static final Logger logger = LogManager.getLogger(MyWork.class);
    
    public void doMyWork() {
        
        // doing some work
        
        logger.info("doMyWork() method called");
    }
}

We have a simple method that logs an information message. Its class is in the com.zetcode.work package. We define a logger that will log message only from this package.

log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="layout">%d [%t] %-5level %logger{36} - %m%n</Property>
    </Properties>
  
    <Appenders>
  
        <Console name="Console">
            <PatternLayout pattern="${layout}" />
        </Console>     
        
        <File name="MyFile" fileName="/home/janbodnar/tmp/mylog.log" append="false">
            <PatternLayout pattern="${layout}"/>
        </File>        

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode.work" level="info" additivity="false" >
            <AppenderRef ref="MyFile" />
        </Logger>
    
        <Root level="info">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>
</Configuration>

In the log4j2.xml configuration file, we define two appenders and two loggers.

<File name="MyFile" fileName="/home/janbodnar/tmp/mylog.log" append="false">
    <PatternLayout pattern="${layout}"/>
</File>   

We define a file appender which writes log messages into the specified file. The file name is specified with the fileName attribute. With append attribute set to false, the file is always overwritten.

<Logger name="com.zetcode.work" level="info" additivity="false" >
    <AppenderRef ref="MyFile" />
</Logger>

We define a logger that logs information messages from the com.zetcode.work package. The logger writes messages to the file.

<Root level="info">
    <AppenderRef ref="Console" />
</Root>

The rest of the messages, in our case from the com.zetcode.main package, are handled by the root logger.

2017-11-17 15:35:22,718 [main] INFO  com.zetcode.main.JavaLog4jEx2 - The main() method is called
2017-11-17 15:35:22,721 [main] INFO  com.zetcode.main.JavaLog4jEx2 - The doJob() method is called

These two messages were written to the console.

$ cat mylog.log 
2017-11-17 15:35:22,722 [main] INFO  com.zetcode.work.MyWork - doMyWork() method called

This message was written to the mylog.log file.

Log4j RollingFileAppender

RollingFileAppender is a special type of appender which backs up the log files when they reach a certain size or meet a time criterion. A rolling file appender automatically rolls or archives the current log file and resumes logging in a new file.

The following application uses a RollingFileAppender.

$ tree
.
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── zetcode
        │           └── JavaLog4jRollingFileEx.java
        └── resources
            └── log4j2.xml

This is the project structure.

pom.xml
<?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.zetcode</groupId>
    <artifactId>JavaLog4jRollingFileEx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
    
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.9.1</version>
        </dependency>

    </dependencies>    
</project>

This is the pom.xml file, which contains the log4j-core dependency.

JavaLog4jRollingFileEx.java
package com.zetcode;

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

public class JavaLog4jRollingFileEx {

    private static final Logger logger = LogManager.getLogger(
        JavaLog4jRollingFileEx.class);
    
    public static void main(String[] args) {
        
        logger.info("Information message");
        logger.warn("Warning message");
        logger.error("Error message");
    }
}

In the JavaLog4jRollingFileEx class, we log three messages.

log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="logdir">/home/janbodnar/tmp</Property>
        <Property name="layout">%d [%t] %-5level %logger{36} - %m%n</Property>
    </Properties>
  
    <Appenders>
        
        <Console name="Console">
            <PatternLayout pattern="${layout}" />
        </Console>           
  
        <RollingFile name="MyFile" fileName="${logdir}/app.log"
                     filePattern="${logdir}/app.%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="${layout}" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="1 MB" />
            </Policies>
            <DefaultRolloverStrategy max="10" />
        </RollingFile>

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode" level="info" additivity="false">
            <AppenderRef ref="MyFile" />
        </Logger>
    
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>
</Configuration>

Log4j is configured in the log4j2.xml.

<RollingFile name="MyFile" fileName="${logdir}/app.log"
                filePattern="${logdir}/app.%d{yyyy-MM-dd}-%i.log">
    <PatternLayout pattern="${layout}" />
...
    <DefaultRolloverStrategy max="10" />
</RollingFile>

A rolling file appender is created with RollingFile tag. We set the location of the log file with the fileName attribute. The PatternLayout sets the layout of the log messages. The DefaultRolloverStrategy deletes older archives if the number of archives reaches ten.

<Policies>
  <TimeBasedTriggeringPolicy />
  <SizeBasedTriggeringPolicy size="1 MB" />
</Policies>

The triggering policies are defined in the Policies tag. They control the conditions under which rollovers occur. Here we use two policies: TimeBasedTriggeringPolicy and SizeBasedTriggeringPolicy. The TimeBasedTriggeringPolicy starts a rollover according to the most specific date and time pattern; in our case, every hour. The SizeBasedTriggeringPolicy starts a rollover if the size of the log file reaches 1 MB.

<Loggers>

    <Logger name="com.zetcode" level="info" additivity="false">
        <AppenderRef ref="MyFile" />
    </Logger>

    <Root level="error">
        <AppenderRef ref="Console" />
    </Root>    

</Loggers>

We have two loggers defined. The com.zetcode logger logs into a file appender. The root logger is not utilized in this application.

$ cat app.log 
2017-11-17 16:44:14,251 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2017-11-17 16:44:14,254 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2017-11-17 16:44:14,255 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message
2017-11-17 16:44:28,158 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2017-11-17 16:44:28,160 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2017-11-17 16:44:28,161 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message
2017-11-17 18:11:58,189 [main] INFO  com.zetcode.JavaLog4jRollingFileEx - Information message
2017-11-17 18:11:58,207 [main] WARN  com.zetcode.JavaLog4jRollingFileEx - Warning message
2017-11-17 18:11:58,208 [main] ERROR com.zetcode.JavaLog4jRollingFileEx - Error message

This is a sample output of the log file.

Log4j with Spring Boot

The next example shows how to use Log4j in a Spring Boot application. The application is a console Java program.

Spring Boot uses Logback for logging by default. Therefore, we need to configure Spring Boot to exclude Logback and include Log4j.

General logging settings are set in the application.properties file. To configure the more fine-grained settings of a logging system, we need to use the native configuration format. In our case, Log4j's settings.

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           └── MyRunner.java
    │   └── resources
    │       ├── app.log
    │       └── log4j2.xml
    └── test
        └── java

This is the project structure. The log messages are written to the app.log file.

pom.xml
<?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.zetcode</groupId>
    <artifactId>JavaLog4jSpringBootEx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>    
    
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>     

    </dependencies>    

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>            
        </plugins>
    </build>      
    
</project>

In the pom.xml file, we exclude the spring-boot-starter-logging dependency and add the spring-boot-starter-log4j2 dependency.

log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="info">
    <Properties>
        <Property name="layout">%d [%t] %-5level %logger{36} - %m%n</Property>
    </Properties>
  
    <Appenders>
  
        <Console name="Console">
            <PatternLayout pattern="${layout}" />
        </Console>     
        
        <File name="MyFile" fileName="src/main/resources/app.log">
            <PatternLayout pattern="${layout}" />
        </File>        

    </Appenders>
    
    <Loggers>

        <Logger name="com.zetcode" level="info" additivity="false" >
            <AppenderRef ref="MyFile" />
        </Logger>
    
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>    

    </Loggers>
</Configuration>

Spring Boot locates the log4j2.xml configuration file in the src/main/resources directory.

<File name="MyFile" fileName="src/main/resources/app.log">
    <PatternLayout pattern="${layout}" />
</File> 

The log messages are written to the src/main/resources/app.log file.

MyRunner.java
package com.zetcode;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyRunner implements CommandLineRunner {

    private static final Logger logger = LogManager.getLogger(MyRunner.class);

    @Override
    public void run(String... args) throws Exception {

        logger.info("Information message");
        logger.warn("Warning message");
    }
}

This is our command line runner. The run() method generates information and warning messages.

Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

In the Application class, we set up the Spring Boot application.

In this tutorial, we have worked with the Log4j library. You might also be interested in the related tutorials: Java servlet Log4j tutorial, Java tutorial, Reading text files in Java, and Reading and writing ICO images in Java.