Slf4jMdcUtil.java
package se.jobtechdev.personaldatagateway.api.logging;
import org.slf4j.Logger;
import org.slf4j.MDC;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Iterator;
public final class Slf4jMdcUtil {
public static final String EVENT_ACTION = "event.action";
public static final String AUDIT_LOG = "audit.log";
public static final String TEMP_LOG = "temp.log";
public static final String TEMP_CUSTOM_PDG_API_CLIENTID = "temp.custom.pdg-api.clientid";
public static final String AUDIT_CUSTOM_PDG_API_CLIENTID = "audit.custom.pdg-api.clientid";
public static final String HTTP_REQUEST_METHOD = "http.request.method";
public static final String HTTP_RESPONSE_MIME_TYPE = "http.response.mime_type";
public static final String HTTP_RESPONSE_STATUS_CODE = "http.response.status_code";
public static final String URL_PATH = "url.path";
public static final String URL_QUERY = "url.query";
public static final String URL_FULL = "url.full";
public static final String URL_PORT = "url.port";
public static final String URL_SCHEME = "url.scheme";
public static final String OBJECT_ID = "object.id";
public static final String EVENT_UPDATE_PARAMETERS = "event.update_parameters";
public static final String EVENT_CREATE_PARAMETERS = "event.create_parameters";
public static final String EVENT_SEARCH_HITS = "event.search.hits";
public static final String EVENT_SEARCH_PARAMETERS = "event.search.parameters";
public static final String EVENT_OUTCOME = "event.outcome";
public static final String EVENT_ACTION_READ = "read";
public static final String EVENT_ACTION_CREATE = "create";
public static final String EVENT_ACTION_UPDATE = "update";
public static final String EVENT_OUTCOME_SUCCESS = "success";
public static final String EVENT_OUTCOME_FAILURE = "failure";
public static final String OBJECT_BODY = "object.body";
private Slf4jMdcUtil() {}
public static boolean activateAuditField() {
final var tempLog = MDC.get(TEMP_LOG);
if (null != tempLog) {
MDC.put(AUDIT_LOG, tempLog);
final var clientId = MDC.get(TEMP_CUSTOM_PDG_API_CLIENTID);
if (null != clientId) MDC.put(AUDIT_CUSTOM_PDG_API_CLIENTID, clientId);
return true;
}
return false;
}
public static void addHttpFields(
ContentCachingRequestWrapper request, ContentCachingResponseWrapper response) {
MDC.put(HTTP_REQUEST_METHOD, request.getMethod());
MDC.put(HTTP_RESPONSE_MIME_TYPE, response.getContentType());
MDC.put(HTTP_RESPONSE_STATUS_CODE, Integer.toString(response.getStatus()));
}
public static void addUrlFields(ContentCachingRequestWrapper request) {
MDC.put(URL_PATH, UrlFieldExtractor.extractUrlPath(request));
MDC.put(URL_QUERY, UrlFieldExtractor.extractUrlQuery(request));
MDC.put(URL_FULL, UrlFieldExtractor.extractUrlFull(request));
MDC.put(URL_PORT, UrlFieldExtractor.extractUrlPort(request));
MDC.put(URL_SCHEME, UrlFieldExtractor.extractUrlScheme(request));
}
public static void addEventFields(
ContentCachingRequestWrapper request, ContentCachingResponseWrapper response) {
final var paramStringBuilder = new StringBuilder();
final var paramNames = request.getParameterNames();
for (Iterator<String> it = paramNames.asIterator(); it.hasNext(); ) {
var paramName = it.next();
var paramValues = String.join(",", request.getParameterValues(paramName));
paramStringBuilder.append(String.format("%s=%s", paramName, paramValues));
if (it.hasNext()) {
paramStringBuilder.append(";");
}
}
switch (request.getMethod()) {
case "GET":
{
MDC.put(EVENT_ACTION, EVENT_ACTION_READ);
final var xPaginationTotal = response.getHeader("X-Pagination-Total");
if (xPaginationTotal != null && !xPaginationTotal.isEmpty()) {
MDC.put(EVENT_SEARCH_HITS, xPaginationTotal);
}
if (!paramStringBuilder.isEmpty()) {
MDC.put(EVENT_SEARCH_PARAMETERS, paramStringBuilder.toString());
}
break;
}
case "POST":
{
MDC.put(EVENT_ACTION, EVENT_ACTION_CREATE);
if (!paramStringBuilder.isEmpty()) {
MDC.put(EVENT_CREATE_PARAMETERS, paramStringBuilder.toString());
}
break;
}
case "PATCH":
{
MDC.put(EVENT_ACTION, EVENT_ACTION_UPDATE);
if (!paramStringBuilder.isEmpty()) {
MDC.put(EVENT_UPDATE_PARAMETERS, paramStringBuilder.toString());
}
MDC.put(OBJECT_ID, request.getRequestURI());
break;
}
default:
{
/* NOP */
}
}
}
public static void log(ContentCachingResponseWrapper response, Logger logger) {
final var message = ApplicationFieldExtractor.extractMessage(response);
if (response.getStatus() < 400) {
MDC.put(EVENT_OUTCOME, EVENT_OUTCOME_SUCCESS);
logger.info(message);
} else {
MDC.put(EVENT_OUTCOME, EVENT_OUTCOME_FAILURE);
logger.error(message);
}
}
public static void addObjectFields(ContentCachingRequestWrapper request, Logger logger) {
switch (request.getMethod()) {
case "POST":
{
Slf4jMdcUtil.addObjectBodyField(request, logger);
break;
}
case "PATCH":
{
MDC.put(OBJECT_ID, request.getRequestURI());
Slf4jMdcUtil.addObjectBodyField(request, logger);
break;
}
default:
{
/* NOP */
}
}
}
public static void addObjectBodyField(ContentCachingRequestWrapper request, Logger logger) {
final var encoded = request.getContentAsString();
if (!encoded.isEmpty()) {
try {
final var decoded = URLDecoder.decode(encoded, request.getCharacterEncoding());
if (!decoded.isEmpty()) {
MDC.put(OBJECT_BODY, decoded);
}
} catch (UnsupportedEncodingException exception) {
logger.warn(
"Failed to decode request content, writing content as-is to 'object.body' field...");
MDC.put(OBJECT_BODY, encoded);
}
}
}
public static void clearMdc() {
MDC.clear();
}
}