ZetCode

Java servlet Log4j

last modified July 13, 2020

Java servlet Log4j tutorial shows how to do logging with Log4j in Java servlets. This tutorial covers Log4j version 2.

Java Servlet

Servlet is a Java class which responds to a particular type of network request - most commonly an HTTP request. Java servlets are used to create web applications. They run in servlet containers such as Tomcat or Jetty. Modern-day Java web development uses frameworks that are built on top of servlets.

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 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 or consoles. Layouts are used to define the formatting of log messages.

Java servlet logging example

The following web application is doing logging with Log4j. In Servlet 3.0+ applications, Log4j works out of the box. It automatically starts when the application deploys and shuts down when the application undeploys.

$ tree
.
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── zetcode
        │           ├── service
        │           │   └── MyService.java
        │           └── web
        │               └── MyServlet.java
        ├── resources
        │   └── log4j2.xml
        └── webapp
            ├── index.html
            ├── META-INF
            │   └── context.xml
            └── WEB-INF

This is the project structure. The Log4j configuration file is placed in the src/main/resources/ directory.

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>JavaServletLog4j</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>JavaServletLog4j</name>

    <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>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-web</artifactId>
            <version>2.8.2</version>
        </dependency>
        
    </dependencies>
    
    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
 
        </plugins>
    </build>    

</project>

This is the Maven POM file. We have two artifacts: javax.servlet-api for servlets and log4j-web for logging with Log4j in web applications. The maven-war-plugin is responsible for collecting all artifact dependencies, classes and resources of the web application and packaging them into a web application archive (WAR).

context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/JavaServletLog4j"/>

In the Tomcat context.xml file, we define the context path. It is the name of the web application.

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] %-5p %c- %m%n</Property>
  </Properties>
  
  <Appenders>

    <RollingFile name="LOCALHOST"
        fileName="${logdir}/localhost.log"
        filePattern="${logdir}/localhost.%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="LOCALHOST" />
    </Logger>
    
    <Root level="error">
    </Root>    

  </Loggers>
</Configuration>

Log4j is configured in the log4j2.xml. In our example, we have chosen the XML file format.

<Properties>
  <Property name="logdir">/home/janbodnar/tmp</Property>
  <Property name="layout">%d [%t] %-5p %c - %m%n</Property>
</Properties>

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

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 %-5p outputs the level of the logging event, where the level name has minimum of five characters and the characters are left justified. The %c 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>
...
</Appenders>

Appenders are objects which define where logging messages are saved. There are several possible destinations including consoles, files, database tables, or sockects.

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

We set the location of the log file. We use a rolling file appender, which automatically rolls or archives the current log file and resumes logging in a new file. 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="LOCALHOST" />
  </Logger>
    
  <Root level="error">
  </Root>    

</Loggers>

In the Loggers tag we define loggers. They are named log message destinations. Each logger can have different level of logging configured. We define a logger with info logging level. We append the rolling file appender that we have previously defined to this logger. With additivity set to false, the log messages are not propagated to their ancestors.

com/zetcode/MyServlet.java
package com.zetcode.web;

import com.zetcode.service.MyService;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@WebServlet(name = "MyServlet", urlPatterns = {"/MyServlet"})
public class MyServlet extends HttpServlet {

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

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        logger.info("MyServlet's doGet() called");

        MyService service = new MyService();
        service.doWork();

        response.setContentType("text/plain;charset=UTF-8");

        PrintWriter out = response.getWriter();
        out.print("MyServlet called");
    }
}

This is the MyServlet servlet. It calls a service method and sends text data back to the client.

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

We get the logger from the LogManager.

logger.info("MyServlet's doGet() called");

We log an information level message.

MyService service = new MyService();
service.doWork();

We call a dummy service method.

PrintWriter out = response.getWriter();
out.print("MyServlet called");

We send text data to the client.

com/zetcode/MyService.java
package com.zetcode.service;

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

public class MyService {
    
    final static Logger logger = LogManager.getLogger(MyService.class);
    
    public void doWork() {
    
        logger.info("MyService's doWork() called");
    }
}

The MyService's doWork method logs an information level message.

index.html
<!DOCTYPE html>
<html>
    <head>
        <title>Home Page</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <a href="MyServlet">Call Servlet</a>
    </body>
</html>

The home page contains a link which calls MyServlet.

$ cat localhost.log 
2017-11-14 16:50:30,157 [http-nio-8084-exec-5] INFO  com.zetcode.service.MyService- MyServlet's doGet() called
2017-11-14 16:50:31,044 [http-nio-8084-exec-5] INFO  com.zetcode.service.MyService- MyService's doWork() called

This is a sample output of logged messages.

In this tutorial, we have done some logging with Log4j in a Java web application.