Log4j MDC

I hope that Log4j does not need any introduction and I assume that you already have basic understanding of Log4j. If not, I’ll recommend you to first read this Log4j Tutorial, to get started.

That said, now I’ll start with MDC or Mapped Diagnostic Context. Don’t get scared with this name! MDC is not that tough. It’s a simple yet useful concept. Before I explain what is MDC, lets assume that we are going to develop a simple web application with one servlet MyServlet that serves requests from multiple clients. And, this servlet uses log4j framework for logging. A file appender has been defined for this servlet, so all the log messages will be logged into a text file.

With the above said configuration, all the log messages from MyServletwill go into a single log file. And when this servlet is serving more than one clients at the same time, the log statements will be mixed and there’s  no way to differentiate which log statement is belongs to which client’s processing. This’ll make it difficult to trace and debug if any processing error occured in MyServlet life cycle.

How to differentiate log statements with respective to each clients?

To avoid the log statements mix-in, we could add a user name (or some other data which will be unique to each client) to our log statements. To do this, we have to make sure that we pass this user name data explicitley to each and every log statements, which is a tedious and repetitive work. But, no need to worry! Log4j has an excellent way to overcome this. It’s called asMDC or Mapped Diagnostic Context.

So, What is Log4j MDC (Mapped Diagnostic Context)

To put it simple, the MDC is a map which stores the context data of the particular thread where the context is running. To explain it, come back to our simple application – every client request will be served by different thread of the MyServlet. So, if you use log4j for logging, then each thread can have it’s own MDC which is  global to the entire thread. Any code which is part of that thread can easily access the values that are present in thread’s MDC.

So, how do we make MDC to differentiate logging statements from multiple clients? Simple : Before starting any business process in your code, get the user name (for our Servlet, we can get it from request object) and put that into MDC. Now the user name will be available to the further processing. In your log4j.properties while defining the conversionPattern, add a pattern %X{key} to retrievce the values that are present in the MDC. The key will be userName in our example. It’s like getting a value from a Session object.

 

This is a simple example where we have a one servlet and one filter. The filter intercepts each and every request and put the user name in the MDC.

 

Filter:

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.log4j.MDC;

/**
 * An example authentication filter which is used to intercept all the requests
 * for fetching the user name from it and put the user name to the Log4j Mapped
 * Diagnostic Context (MDC), so that the user name could be used for
 * differentiating log messages.
 *
 * @author veerasundar.com/blog
 *
 */
public class AuthenticationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        try {
            /*
            * This code puts the value "userName" to the Mapped Diagnostic
            * context. Since MDc is a static class, we can directly access it
            * with out creating a new object from it. Here, instead of hard
            * coding the user name, the value can be retrieved from a HTTP
            * Request object.
            */
            MDC.put("userName", "veera");

            chain.doFilter(request, response);

        } finally {
            MDC.remove("userName");
        }

    }

}

Servlet

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

/**
 * A good-for-nothing servlet which is just writing few messages to the logger
 * object. Since we've configured in the log4j.properties file to include the
 * userName, taken from MDC, every message will be appended with the user name
 * that is set from the AuthenticationFilter
 *
 */
public class Log4jMdcDemo extends HttpServlet {

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

    public Log4jMdcDemo() {
    }

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doService(request, response);
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doService(request, response);
    }

    protected void doService(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        logger.info("This is  demo for the Log4j MDC concept");
        logger.info("From Veerasundar.com/blog");
        logger.debug("Just some sample messages");
    }
}

Configration

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"   xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <servlet>
        <description/>
        <display-name>Log4jMdcDemo</display-name>
        <servlet-name>Log4jMdcDemo</servlet-name>
        <servlet-class>com.veerasundar.code.log4jmdc.Log4jMdcDemo</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Log4jMdcDemo</servlet-name>
        <url-pattern>/Log4jMdcDemo</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>AuthFilter</filter-name>
        <filter-class>com.veerasundar.code.log4jmdc.AuthenticationFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Log4j.properties file which uses the data present in MDC

# sample log4j.properties file, explaining log4j MDC concept
# author: veerasundar.com/blog

log4j.appender.consoleAppender = org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout = org.apache.log4j.PatternLayout

#note the %X{userName} - this is how you fetch data from Mapped Diagnostic Context (MDC)
log4j.appender.consoleAppender.layout.ConversionPattern = %-4r [%t] %5p %c %x - %m - %X{userName}%n

log4j.rootLogger = DEBUG, consoleAppender

I hope that code explains the concept of MDC. Do let me know if you have any questions/suggestions in the comments.

Output from the log file:

0    [http-8084-2]  INFO Log4jMDCDemo  - This is  demo for the Log4j MDC concept - veera
0    [http-8084-2]  INFO Log4jMDCDemo  - From Veerasundar.com/blog - veera
0    [http-8084-2] DEBUG Log4jMDCDemo  - Just some sample messages - veera

FROM HERE

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s