Log4J MDC Integration

Purpose

Server-side applications are typically multi-threaded and therefore logging has to deal with these parallel execution paths. In the case of ULC, logging output should be clearly related to the originating session.

This contribution demonstrates how to include session identifying information in Log4J's logging output using the MDC (Mapped Diagnostic Context) feature. It also shows how one can execute code just before or after ULC processes a request.

Related To

How to use

Include the Log4J library and the MDCSessionRegistry class with your (server-side) application code.

Register the MDCSessionRegistry as shown below. This needs to be done prior to creating the first session. For this purpose, just create a static initializer in your main class which contains these two statements:

ICurrentSessionRegistry registry = new MDCSessionRegistry();
ULCSession.setCurrentSessionRegistry(registry);

You can now add logging statements to your code in the Log4J fashion. In order to distinguish between different sessions, a pattern has to be specified in the Log4J configuration which includes the session id and client IP-address. e.g.

<appender name="FileAppender" class="org.apache.log4j.FileAppender">
    <param name="File" value="SomeLogFile.txt"></param>
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d %3X{MDC_SESSION_ID} / %X{MDC_SESSION_IP} %p %l - %m%n"/>
    </layout>
</appender>

How it is implemented

The implementation uses Log4J's Mapped Diagnostic Context. The MDC binds logging information to a thread. Your task is to associate the thread with a ULC session. This is accomplished by means of the MDCSessionRegistry class.

The class MDCSessionRegistry creates the session-thread binding for Log4J. The method setCurrentSession() is called before a request is processed by ULC on the server. This is the right spot to create Log4J MDC entries with key "MDC_SESSION_ID" and session id as value and with key "MDC_SESSION_IP" and client IP-address as value. At the end of request processing, the method setCurrentSession() is called again with a null parameter. This time the Log4J MDC entries are removed.

public class MDCSessionRegistry implements ICurrentSessionRegistry
{
  private static final ThreadLocal CURRENT_SESSION = new ThreadLocal();

public ULCSession getCurrentSession() { return (ULCSession) CURRENT_SESSION.get(); }

public void setCurrentSession(ULCSession ulcSession) { CURRENT_SESSION.set(ulcSession); handleMDC(ulcSession); }

private void handleMDC(ULCSession ulcSession) { if(ulcSession != null) { MDC.put("MDC_SESSION_ID", getSessionInfo().getSessionID()); MDC.put("MDC_SESSION_IP", getSessionInfo().getSessionIP()); } else { MDC.remove("MDC_SESSION_ID"); MDC.remove("MDC_SESSION_IP"); } }

… }

The classes SessionID and SessionIP hold the session- and client-specific information that you would like to add to a log statement. This contribution simply holds the IP-address of the client which is fetched lazily using the ClientContext. In addition, an incrementing number is assigned in order to distinguish different sessions associated with the same IP address.