Igor Kromin |   Consultant. Coder. Blogger. Tinkerer. Gamer.

I've been updating logging in the web services I look after so that the WS-A MessageID can be logged for each request and for all of the messages written to the log during that request. On the most part putting this value it into the MDC and configuring the appender to write it out to the log is all that's required. However, when you start using thread pools e.g. with ThreadPoolExecutor the MDC goes haywire and that's because and I quote..."The MDC is managed on a per thread basis."

When using a thread pool the processing starts to happen outside of the main web service thread so the MDC that is being used is completely unrelated to the original request. This means you can start logging data for requests that have completed running already.

This is how I went about making sure the correct MDC is maintained...
 Java
package net.igorkromin;
import org.apache.log4j.MDC;
import java.util.Hashtable;
import java.util.concurrent.Callable;
public abstract class MdcCallable<T> implements Callable<T> {
private Hashtable contextCopy;
public MdcCallable() {
contextCopy = (Hashtable) MDC.getContext().clone();
}
public T call() throws Exception {
MDC.clear();
for (Object key : contextCopy.keySet()) {
MDC.put((String) key, contextCopy.get(key));
}
return process();
}
public abstract T process() throws Exception;
}


I created a small wrapper class that implements Callable. Since the Callable is going to be created within the context of the original web service thread the MDC that is available there is the correct one. So in the constructor I create a clone of that MDC context.

I also implement the call() method - this is called in the context of whatever Thread happens to be available from the pool so the MDC is cleared first to remove any previous requests' data, then I copy values from the cloned MDC context into the current MDC and call the abstract process() method.

To use this all I have to do is extend MdcCallable and implement the process() method instead of the usual approach of implementing Callable and the call() method.



It may not be very performant making clones of the MDC context every time so we ended up optimising this to look for specific key/values from the MDC but the approach is much the same as described above.

For this to work you will need the 1.2.17 version of log4j.

-i

A quick disclaimer...

Although I put in a great effort into researching all the topics I cover, mistakes can happen. Use of any information from my blog posts should be at own risk and I do not hold any liability towards any information misuse or damages caused by following any of my posts.

All content and opinions expressed on this Blog are my own and do not represent the opinions of my employer (Oracle). Use of any information contained in this blog post/article is subject to this disclaimer.
Hi! You can search my blog here ⤵
NOTE: (2022) This Blog is no longer maintained and I will not be answering any emails or comments.

I am now focusing on Atari Gamer.