Last active
January 12, 2016 08:53
-
-
Save pismy/509289dc5822df8bf272 to your computer and use it in GitHub Desktop.
SLF4J Tool: A servlet filter that adds a generated unique request id to the logging context (MDC)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.IOException; | |
import javax.servlet.Filter; | |
import javax.servlet.FilterChain; | |
import javax.servlet.FilterConfig; | |
import javax.servlet.ServletException; | |
import javax.servlet.ServletRequest; | |
import javax.servlet.ServletResponse; | |
import javax.servlet.http.HttpServletRequest; | |
import org.slf4j.MDC; | |
/** | |
* A {@link Filter servlet filter} that adds a generated unique request id to the logging context ({@link MDC}) | |
* <p> | |
* Requires SLF4J as the logging facade API. | |
* <p> | |
* With a log management system such as ELK, this will help you track a complete callflow, filtering logs from a unique request. Quite valuable when | |
* your system processes thousands of requests per second and produces terabytes of logs each day... | |
* <p> | |
* Note that in a micro-services architecture, upon calling other services you can transfer this generated {@code requestId} in a request header | |
* {@code X-Track-RequestId}, thus implementing an end-to-end callflow tracking. | |
* <p> | |
* The request attribute, MDC attribute and request header can be overridden with Java properties: | |
* | |
* <table border=1> | |
* <tr> | |
* <th>attribute</th> | |
* <th>default value</th> | |
* <th>Filter config</th> | |
* <th>Java property</th> | |
* </tr> | |
* <tr> | |
* <td>request header</td> | |
* <td>{@code X-Track-RequestId}</td> | |
* <td>{@code header.requestId}</td> | |
* <td>{@code slf4j.tools.header.requestId}</td> | |
* </tr> | |
* <tr> | |
* <td>request attribute</td> | |
* <td>{@code track.requestId}</td> | |
* <td>{@code attribute.requestId}</td> | |
* <td>{@code slf4j.tools.attribute.requestId}</td> | |
* </tr> | |
* <tr> | |
* <td>MDC attribute</td> | |
* <td>{@code requestId}</td> | |
* <td>{@code mdc.requestId}</td> | |
* <td>{@code slf4j.tools.mdc.requestId}</td> | |
* </tr> | |
* </table> | |
* | |
* y {@code slf4j.tools.mdc.sessionId} | |
* | |
* @author pismy | |
*/ | |
public class RequestIdFilter implements Filter { | |
private String headerName; | |
private String attributeName; | |
private String mdcName; | |
public void init(FilterConfig filterConfig) throws ServletException { | |
headerName = getConfig("header.requestId", "X-Track-RequestId", filterConfig); | |
attributeName = getConfig("attribute.requestId", "track.requestId", filterConfig); | |
mdcName = getConfig("mdc.requestId", "requestId", filterConfig); | |
} | |
private String getConfig(String param, String defaultValue, FilterConfig filterConfig) { | |
String valueFromConfig = filterConfig.getInitParameter(param); | |
return valueFromConfig != null ? valueFromConfig : System.getProperty("slf4j.tools."+param, defaultValue); | |
} | |
/** | |
* <ul> | |
* <li>checks whether the current request has an attached request id, | |
* <li>if not, tries to get one from request headers (implements end-to-end callflow traceability), | |
* <li>if not, generates one | |
* <li>attaches it to the request (as an attribute) and to the {@link MDC} context. | |
* </ul> | |
*/ | |
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { | |
// checks whether the current request has an attached request id | |
String reqId = (String) request.getAttribute(attributeName); | |
if (reqId == null) { | |
// retrieve id from request headers | |
if (request instanceof HttpServletRequest) { | |
reqId = ((HttpServletRequest) request).getHeader(headerName); | |
} | |
if (reqId == null) { | |
// no requestId (either from attributes or headers): generate one | |
reqId = Long.toHexString(System.nanoTime()); | |
} | |
// attach to request | |
request.setAttribute(attributeName, reqId); | |
} | |
// attach to MDC context | |
MDC.put(mdcName, reqId); | |
try { | |
chain.doFilter(request, response); | |
} finally { | |
// remove from MDC context | |
MDC.remove(mdcName); | |
} | |
} | |
public void destroy() { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment