Friday, September 10, 2010

Log4j MDC

Yesterday I happen to see few articles related to Log4j MDC.
I thought of preparing demo for that..
I thought of logging the User in my logs.. Since my application is Spring based.. So the best way is to fetch the user from the Spring Security Context and store that in log4j MDC and use that in log4j.xml.

Log4j MDC:

The MDC class is based on a map. It provides mapped diagnostic contexts. A Mapped Diagnostic Context, or MDC in short, is an instrument for distinguishing interleaved log output from different sources. Log output is typically interleaved when a server handles multiple clients near-simultaneously.

The MDC is managed on a per thread basis. A child thread automatically inherits a copy of the mapped diagnostic context of its parent.

With MDC we can add logging context variables as we pass through the security/filter layer and log as we always would in the business logic.

For example you want to log user name :
1. Create a generic MDCHelper class which will help to set and clear your MDC context.
2. Create a filter and inject your MDCHelper.
3. include your context in Log4j conversion pattern.

Sample:
This class will help to set the Authenticated user in to MDC context, if unauthenticated user then the default value will be set.
public final class MDCUserNameLogHelper {
private static final String USER_NAME = "userName";
private static final String DEFAULT_USER = "system";
public MDCUserNameLogHelper () { }
public static void initLogContext(final String userName) {
if (userName != null) {
MDC.put(USER_NAME, userName); // Stores the userName
} else {
defaultInit(); //if unauthenticated the userName will be null so log default //user.
}
}
public static void clearLogContext() {
MDC.remove(USER_NAME); // clears the user name..
}
static void defaultInit() {
if (MDC.get(USER_NAME) == null) {
MDC.put(USER_NAME, DEFAULT_USER); // Setting the default value if the user is not authenticated.
}
}
}

My filter to fetch the authenticated user :

public class UserNameMDCFilter implements Filter {
private final static Logger logger =
Logger.getLogger(UserNameMDCFilter.class.getName());

public void init(FilterConfig filterConfig) {
}

public void destroy() {
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
try
{
final String userName = getUserName();
// Set the MDC context
MDCUserNameLogHelper.initLogContext(userName);

// pass on the request
chain.doFilter(request, response);
}
finally
{
// no matter what happens, clean up
// the variables associated with this Thread/Request
MDCUserNameLogHelper.clearLogContext();
}
}

private String getUserName() {
SecurityContext sc = SecurityContextHolder.getContext();
Authentication a = sc.getAuthentication();
// Here unauthenticated content. So the default user will be logged
if (a == null) {
return null;
}
if (!AnonymousAuthenticationToken.class.isAssignableFrom(a.getClass())) {
Object aPrincipal = a.getPrincipal();
if (aPrincipal instanceof UserDetails) {
UserDetails ud = (UserDetails) aPrincipal;
if (ud == null) {
return null;
}
final String username = ud.getUsername();
log.debug("aPrincipal instanceof UserDetails, username: {}", username);
return username;
} else {
if (aPrincipal instanceof String) {
String ud = (String) aPrincipal;
log.debug("aPrincipal instanceof String, username: {}", ud);
return ud;
}
}
}
}



The above two java classes will be responsible for fetching the Authenticated and Unauthenticated UserName and set and clear that in log4j MDC context.

Changes in Log4j:
Add user MDC context variable on to Conversion pattern.


You can see the userName is appended in your logs :)
there are so many examples in the net. hope it helps you.
Meet you again with some other article.

Reference :
http://ivor.bosloper.nl/2006/11/07/log4j-mdc/

Thursday, September 2, 2010

AOP (Aspect Oriented Programming)

I am using AOP for quite some time.
Yesterday found a nice book and read more about Spring 2.5 and AOP.

Really interesting one.
I will prepare a doc and will update in a while..