Kotlin | Software Development
Asynchronous streams and callbacks in Kotlin and Java Matthisk Heimensen 08 May, 2020
public class AuditLogAdvice implements MethodInterceptor { private static final Log log = LogFactory.getLog("AuditLogger"); /** * @see org.aopalliance.intercept.MethodInterceptor#invoke( * org.aopalliance.intercept.MethodInvocation) */ public Object invoke(MethodInvocation invocation) throws Throwable { StringBuilder logLine = new StringBuilder(); logLine.append("Calling: "); Method m = invocation.getMethod(); logLine.append(m.getDeclaringClass().getName()) .append(".") .append(m.getName()) .append("("); Object[] params = invocation.getArguments(); if (params != null && params.length > 0) { int counter = 0; for (Object param : params) { logLine.append(param); counter++; if (counter < params.length) { logLine.append(", "); } } } logLine.append(")"); log.debug(logLine); Object returnValue = invocation.proceed(); StringBuilder logReturnLine = new StringBuilder(); logReturnLine.append("Returned: ").append(returnValue); log.debug(logReturnLine); return returnValue; } }This advice can now be applied to method calls using a pointcut in your Spring applicationContext.xml file. This snippet for example matches all calls to the DAO classes of your application:
.*DAO.*That's it, we're done... But wait, didn't the maintenance guy also want the IPAddress and userName logged. How can we do this? Luckily for us, we are using Log4J to do the logging. Log4J contains an Object called NDC (Nested Diagnostic Context), which allows us to register some information specific to a thread. Now we need some location where we have access to the HttpServletRequest. We don't want to put the IPAddress of the user on the NDC stack in each Struts Action / Spring-MVC Controller / Tapestry Page, but instead we want to do this in exactly one location. One of the ideal locations is in a javax.servlet.Filter, as each and every request from the clients' browser passes through the Filter (if correctly mapped in your web.xml). So let's define the Filter:
public class AuditLogFilter implements Filter { private static final Logger logger = Logger.getLogger("AuditLogger"); /** * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) */ public void init(FilterConfig arg0) throws ServletException { } /** * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, * javax.servlet.ServletResponse, javax.servlet.FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String ndc = request.getRemoteAddr(); ndc += "/" + request.getParameter("userId"); NDC.push(ndc); chain.doFilter(request, response); NDC.pop(); } /** * @see javax.servlet.Filter#destroy() */ public void destroy() { NDC.remove(); } }Now we are really done. Using these classes, we are able to declaratively log what the maintenance guy wants (IPAddress, userName, actions and results), in a consistent format. And it is easy to adapt at a later stage!