Skip to content

Instantly share code, notes, and snippets.

@pismy
Last active January 12, 2016 08:53
Show Gist options
  • Save pismy/509289dc5822df8bf272 to your computer and use it in GitHub Desktop.
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)
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