Play 2.2.x Java MDC Filter

231 views
Skip to first unread message

Diego Mole

unread,
Sep 30, 2016, 5:27:06 PM9/30/16
to Play Framework
Hi! I'm trying without success to create a "filter" or "interceptor" or something that make me able to catch every request, read the cookie and set MDC key-value for logging purposes. And when the request has finish, clear those keys, in order not to leave zombie MDC values on that thread.

I found a few examples in the web, but none of them works in this play version (2.2.x for Java).

But this filter only do something when the request arrives, but not when the request is leaving... 

JavaFilter.java
import play.api.mvc.*;
import scala.Function1;
import scala.concurrent.Future;
import scala.runtime.AbstractFunction1;


public abstract class JavaFilter implements Filter {


   
@Override
   
public Future<SimpleResult> apply(Function1<RequestHeader, Future<SimpleResult>> nextFilter, RequestHeader requestHeader) {
       
return nextFilter
               
.apply(requestHeader)
               
.map(new AbstractFunction1<SimpleResult, SimpleResult>() {
                   
@Override
                   
public SimpleResult apply(SimpleResult currentResult) {
                       
return Apply(currentResult, requestHeader);
                   
}
               
},
                        play
.api.libs.concurrent.Execution.defaultContext());
   
}


   
@Override
   
public EssentialAction apply(EssentialAction next) {
       
return Filter$class.apply(this, next);
   
}


   
public abstract SimpleResult Apply(SimpleResult currentResult, RequestHeader requestHeader);
}


LoggerMdcFilter.java
import org.jboss.logging.MDC;
import play.api.mvc.RequestHeader;
import play.api.mvc.SimpleResult;


public class LoggerMdcFilter extends JavaFilter {


   
@Override
   
public SimpleResult Apply(SimpleResult currentResult, RequestHeader requestHeader) {
        MDC
.put("email", requestHeader.session().get("email").get());
       
return currentResult;
       
//TODO: Clean MDC when request ends
   
}


}


Can anyone share a working example of setting and clear an MDC on play 2.2.x for Java? 
Or at least tell me if the given example can be improve in order to clean the MDC when the request leaves the server?

Thanks in advance!
Diego

Greg Methvin

unread,
Sep 30, 2016, 6:15:11 PM9/30/16
to play-framework
Hi Diego,

The Java filter implementation you have doesn't really do the same thing as its Scala equivalent. Typically a filter would take the "next" action to call so it can wrap the entire call. Here's the implementation from 2.5.x: https://github.com/playframework/playframework/blob/2.5.x/framework/src/play/src/main/java/play/mvc/Filter.java.

Also, you're going to run into problems with MDC because it uses a thread local. To get around that you could explicitly pass an SLF4J Marker, or try one of the workarounds here: https://yanns.github.io/blog/2014/05/04/slf4j-mapped-diagnostic-context-mdc-with-play-framework/

Greg

--
You received this message because you are subscribed to the Google Groups "Play Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/189056da-f0f3-40a8-8202-5f65c7ad337e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Senior Software Engineer

Diego Mole

unread,
Oct 4, 2016, 3:22:30 AM10/4/16
to Play Framework
Hi Greg! Thanks for your answer. 
I couldn't make the filter example work in play 2.2.x ... I think there are components that are only present in play 2.5.x

Anyway, I found and example in the web that fulfill my needs:
@Override
   
public Action onRequest(Request request, Method actionMethod) {
       
return new Action.Simple() {
           
@Override
           
public F.Promise<SimpleResult> call(Http.Context ctx) throws Throwable {


                MDC
.put("username", ctx.session().get("email"));
                F
.Promise<SimpleResult> result = delegate.call(ctx);


                MDC
.remove("username");
               
return result;
           
}
       
};
   
}

This code works well... but it doesn't prevent the "Thread-Local" problem that you told me. And the example you gave me is in Scala... do you have another working example in Java for Play 2.2x?

Thanks in advance!
Diego
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

Greg Methvin

unread,
Oct 4, 2016, 11:27:50 AM10/4/16
to play-framework
Hi Diego,

You should be able to use the first solution provided in the blog post (a custom Akka dispatcher) in a Java app as well. Play's default execution context is also used to execute Java actions and in the F.Promise implementation, so I think that should cover all your needs. The Dispatcher API in Akka is the same for Java and Scala.

Greg

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/1f898ebf-2125-4186-a020-1b7421b6c37d%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Greg Methvin

unread,
Oct 4, 2016, 1:59:52 PM10/4/16
to play-framework
I'd also suggest you consider simply explicitly passing the logger around and use a library like https://github.com/godaddy/godaddy-logger

So instead of calling the logger statically you could pass logger.with(request) that already has a request identifier. That does mean you have to explicitly pass something around, but it's less likely to cause problems if you happen to call some code that doesn't use the default execution context.

Greg
Reply all
Reply to author
Forward
0 new messages