package io.oasp.module.logging.common.impl;
import java.io.IOException;
import java.util.UUID;
import javax.inject.Inject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.oasp.module.logging.common.api.DiagnosticContextFacade;
/**
* Request logging filter that adds the request log message to the SLF4j mapped diagnostic context (MDC) before the
* request is processed, removing it again after the request is processed.
*
*/
public class DiagnosticContextFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(DiagnosticContextFilter.class);
/**
* The name of the {@link FilterConfig#getInitParameter(String) init parameter} for
* {@link #setCorrelationIdHttpHeaderName(String)}.
*/
private static final String CORRELATION_ID_HEADER_NAME_PARAM = "correlationIdHeaderName";
/** The default value for {@link #setCorrelationIdHttpHeaderName(String)}. */
public static final String CORRELATION_ID_HEADER_NAME_DEFAULT = "X-Correlation-Id";
/** @see #setCorrelationIdHttpHeaderName(String) */
private String correlationIdHttpHeaderName;
private DiagnosticContextFacade diagnosticContextFacade;
/**
* The constructor.
*/
public DiagnosticContextFilter() {
super();
this.correlationIdHttpHeaderName = CORRELATION_ID_HEADER_NAME_DEFAULT;
}
/**
* @param correlationIdHttpHeaderName is the name of the {@link HttpServletRequest#getHeader(String) HTTP header} for
* the {@link io.oasp.module.logging.common.api.LoggingConstants#CORRELATION_ID correlation ID}.
*/
public void setCorrelationIdHttpHeaderName(String correlationIdHttpHeaderName) {
this.correlationIdHttpHeaderName = correlationIdHttpHeaderName;
}
@Override
public void destroy() {
}
private static String normalizeValue(String value) {
if (value != null) {
String result = value.trim();
if (!result.isEmpty()) {
return result;
}
}
return null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
setCorrelationId(request);
try {
chain.doFilter(request, response);
} finally {
this.diagnosticContextFacade.removeCorrelationId();
}
}
private void setCorrelationId(ServletRequest request) {
String correlationId = null;
if (request instanceof HttpServletRequest && this.correlationIdHttpHeaderName != null) {
correlationId = normalizeValue(((HttpServletRequest) request).getHeader(this.correlationIdHttpHeaderName));
if (correlationId == null) {
LOG.debug("No correlation ID found for HTTP header {}.", this.correlationIdHttpHeaderName);
} else {
this.diagnosticContextFacade.setCorrelationId(correlationId);
LOG.debug("Using correlation ID {} from HTTP header {}.", correlationId, this.correlationIdHttpHeaderName);
return;
}
}
if (correlationId == null) {
// potential fallback if initialized before this filter...
correlationId = normalizeValue(this.diagnosticContextFacade.getCorrelationId());
if (correlationId != null) {
LOG.debug("Correlation ID was already set to {} before DiagnosticContextFilter has been invoked.",
correlationId);
} else {
// no correlation ID present, create a unique ID
correlationId = UUID.randomUUID().toString();
this.diagnosticContextFacade.setCorrelationId(correlationId);
LOG.debug("Created unique correlation ID {}.", correlationId);
}
}
}
/**
* @param diagnosticContextFacade the diagnosticContextFacade to set
*/
@Inject
public void setDiagnosticContextFacade(DiagnosticContextFacade diagnosticContextFacade) {
this.diagnosticContextFacade = diagnosticContextFacade;
}
@Override
public void init(FilterConfig config) throws ServletException {
String headerName = config.getInitParameter(CORRELATION_ID_HEADER_NAME_PARAM);
if (headerName == null) {
LOG.debug("Parameter {} not configured via filter config.", CORRELATION_ID_HEADER_NAME_PARAM);
} else {
this.correlationIdHttpHeaderName = headerName;
}
LOG.info("Correlation ID header initialized to: {}", this.correlationIdHttpHeaderName);
if (this.diagnosticContextFacade == null) {
try {
// ATTENTION: We do not import these classes as we keep spring as an optional dependency.
// If spring is not available in your classpath (e.g. some real JEE context) then this will produce a
// ClassNotFoundException and use the fallback in the catch statement.
ServletContext servletContext = config.getServletContext();
org.springframework.web.context.WebApplicationContext springContext;
springContext =
org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.diagnosticContextFacade = springContext.getBean(DiagnosticContextFacade.class);
} catch (Throwable e) {
LOG.warn("DiagnosticContextFacade not defined in spring. Falling back to default", e);
this.diagnosticContextFacade = new DiagnosticContextFacadeImpl();
}
}
}
}