All Downloads are FREE. Search and download functionalities are using the official Maven repository.

log.munzi.interceptor.LoggingInterceptor Maven / Gradle / Ivy

The newest version!
package log.munzi.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import log.munzi.config.ApiLogProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Interceptor 단계에서 HttpServletRequest, HttpServletResponse 등을 가로채
 * API의 Request, Response log를 찍어준다.
 *
 * log type : REQ, RES
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class LoggingInterceptor implements HandlerInterceptor {

    private final ObjectMapper objectMapper;

    private final ApiLogProperties apiLog;

    /**
     * Request API log를 찍는 부분.
     * 설정파일의 secret 여부, 길이 제한 등을 체크해 설정대로 로그를 남긴다.
     * 

* Interceptor가 Request 중간에서 가로채서 작업하는 부분이기 때문에, * preHandle 호출 시 필요한 HttpServletRequest, HttpServletResponse, handler를 인자로 받아 사용하고 preHandle 호출에 그대로 사용한다. * * @param request HttpServletRequest * @param response HttpServletResponse * @param handler HttpServletResponse * @return HandlerInterceptor.super.preHandle * @throws Exception request.getReader Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long startTime = System.currentTimeMillis(); String requestMethodUri = request.getMethod() + " " + request.getRequestURI(); String requestAccept = request.getHeader("accept"); // HttpServletRequest에 값 저장 request.setAttribute("startTime", startTime); request.setAttribute("requestMethodUri", requestMethodUri); request.setAttribute("requestAccept", requestAccept); if (apiLog.isUse() && apiLog.getRequest() != null) { // inactive api '*' check boolean inactiveYn = this.checkEndAsterisk(apiLog.getRequest().getInactiveApi(), requestMethodUri); if ((!request.getClass().getName().contains("SecurityContextHolderAwareRequestWrapper") || apiLog.isIgnoreSecurityLog()) && !inactiveYn && !apiLog.getRequest().getInactiveApi().contains(requestMethodUri)) { StringBuilder headersBuilder = new StringBuilder(); Enumeration headerNames = request.getHeaderNames(); String headerName; while (headerNames.hasMoreElements()) { headerName = headerNames.nextElement(); headersBuilder.append("\""); headersBuilder.append(headerName); headersBuilder.append("\":\""); headersBuilder.append(request.getHeader(headerName).replaceAll("\"", "'")); headersBuilder.append("\", "); } int headersLength = headersBuilder.length(); if (headersLength >= 2) headersBuilder.delete(headersLength - 2, headersLength); StringBuilder paramsBuilder = new StringBuilder(); Enumeration paramNames = request.getParameterNames(); String paramName; while (paramNames.hasMoreElements()) { paramName = paramNames.nextElement(); paramsBuilder.append("\""); paramsBuilder.append(paramName); paramsBuilder.append("\":\""); paramsBuilder.append(request.getParameter(paramName)); paramsBuilder.append("\", "); } int paramLength = paramsBuilder.length(); if (paramLength >= 2) paramsBuilder.delete(paramLength - 2, paramLength); String body; String contentType = request.getHeader("Content-Type"); if (contentType == null || request.getHeader("Content-Length") == null) { body = "{}"; } else { int contentLength = Integer.parseInt(request.getHeader("Content-Length")); if (contentType.contains("multipart/form-data")) { body = "[multipart/form-data]"; } else if (this.checkEndAsterisk(apiLog.getRequest().getSecretApi(), requestMethodUri) || apiLog.getRequest().getSecretApi().contains(requestMethodUri)) { body = "[secret! " + byteCalculation(contentLength) + "]"; } else { if (apiLog.getRequest().getMaxBodySize().isEmpty()) apiLog.getRequest().setMaxBodySize("1KB"); if (contentLength > textSizeToByteSize(apiLog.getRequest().getMaxBodySize())) { body = "[" + byteCalculation(contentLength) + "]"; } else { body = request.getReader().lines().collect(Collectors.joining(System.lineSeparator())) .replaceAll("\\s", "") .replaceAll("\\b", ""); } } } String headers = "{" + headersBuilder + "}"; String params = "{" + paramsBuilder + "}"; if (apiLog.isJsonPretty()) { headers = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(headers, Object.class)); params = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(params, Object.class)); if (body.startsWith("{") && body.endsWith("}")) { body = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(body, Object.class)); } } if (this.checkEndAsterisk(apiLog.getDebugApi(), requestMethodUri) || apiLog.getDebugApi().contains(requestMethodUri)) { log.debug("REQ > [{}],\nheaders={},\nparams={},\nbody={}", requestMethodUri, headers, params, body); } else { log.info("REQ > [{}],\nheaders={},\nparams={},\nbody={}", requestMethodUri, headers, params, body); } } } return HandlerInterceptor.super.preHandle(request, response, handler); } /** * Response API log를 찍는 부분 * 설정파일의 secret 여부, 길이 제한 등을 체크해 설정대로 로그를 남긴다. *

* Interceptor가 Response 중간에서 가로채서 작업하는 부분이기 때문에, * postHandle 호출 시 필요한 HttpServletRequest, HttpServletResponse, handler, ModelAndView를 인자로 받아 사용하고 postHandle 호출에 그대로 사용한다. * * @param request HttpServletRequest * @param response HttpServletResponse * @param handler handler * @param modelAndView ModelAndView * @throws Exception HandlerInterceptor.super.postHandle Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { String requestMethodUri = (String) request.getAttribute("requestMethodUri"); long startTime = (long) request.getAttribute("startTime"); String requestAccept = (String) request.getAttribute("requestAccept"); if (!Objects.equals(requestAccept, MediaType.TEXT_EVENT_STREAM_VALUE) && apiLog.isUse() && apiLog.getResponse() != null) { // inactive api '*' check boolean inactiveYn = this.checkEndAsterisk(apiLog.getResponse().getInactiveApi(), requestMethodUri); if ((!request.getClass().getName().contains("SecurityContextHolderAwareRequestWrapper") || apiLog.isIgnoreSecurityLog()) && !inactiveYn && !apiLog.getResponse().getInactiveApi().contains(requestMethodUri)) { StringBuilder headersBuilder = new StringBuilder(); Enumeration headerNames = request.getHeaderNames(); String headerName; while (headerNames.hasMoreElements()) { headerName = headerNames.nextElement(); headersBuilder.append("\""); headersBuilder.append(headerName); headersBuilder.append("\":\""); headersBuilder.append(request.getHeader(headerName).replaceAll("\"", "'")); headersBuilder.append("\", "); } int headersLength = headersBuilder.length(); if (headersLength >= 2) headersBuilder.delete(headersLength - 2, headersLength); String payload = ""; final ContentCachingResponseWrapper wrappingResponse = (ContentCachingResponseWrapper) response; String contentType = wrappingResponse.getContentType(); if (contentType != null) { if (contentType.contains("application/json") && wrappingResponse.getContentAsByteArray().length != 0) { payload = objectMapper.readTree(wrappingResponse.getContentAsByteArray()).toString(); } else if (contentType.contains("text/plain")) { payload = new String(wrappingResponse.getContentAsByteArray()); } else if (contentType.contains("multipart/form-data")) { payload = "[multipart/form-data]"; } int payloadSize = payload.getBytes(StandardCharsets.UTF_8).length; String payloadTextSize = byteCalculation(payloadSize); if (this.checkEndAsterisk(apiLog.getResponse().getSecretApi(), requestMethodUri) || apiLog.getResponse().getSecretApi().contains(requestMethodUri)) { payload = "[secret! " + payloadTextSize + "]"; } else { if (apiLog.getResponse().getMaxBodySize().isEmpty()) apiLog.getResponse().setMaxBodySize("1KB"); if (payloadSize > textSizeToByteSize(apiLog.getResponse().getMaxBodySize())) { payload = "[" + payloadTextSize + "]"; } } } String headers = "{" + headersBuilder + "}"; if (apiLog.isJsonPretty() && contentType != null && contentType.contains("application/json")) { headers = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(headers, Object.class)); if (payload.startsWith("{") && payload.endsWith("}")) { payload = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectMapper.readValue(payload, Object.class)); } } long responseTimeMs = System.currentTimeMillis() - startTime; if (this.checkEndAsterisk(apiLog.getDebugApi(), requestMethodUri) || apiLog.getDebugApi().contains(requestMethodUri)) { log.debug("RES > {} [{}] {}ms,\nheaders={},\npayload={}", response.getStatus(), requestMethodUri, responseTimeMs, headers, payload); } else { log.info("RES > {} [{}] {}ms,\nheaders={},\npayload={}", response.getStatus(), requestMethodUri, responseTimeMs, headers, payload); } } } HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } /** * bytes 단위의 숫자를 KB, MB 단위의 문자열로 변환 * ex) 2048 -> 2 KB * * @param bytes 문자열로 변환할 byte단위 크기 * @return KB, MB 단위로 변환된 문자열 */ private String byteCalculation(int bytes) { String[] sArray = {"bytes", "KB", "MB", "GB", "TB", "PB"}; if (bytes == 0) return "0 bytes"; int idx = (int) Math.floor(Math.log(bytes) / Math.log(1024)); DecimalFormat df = new DecimalFormat("#,###.##"); double ret = ((bytes / Math.pow(1024, Math.floor(idx)))); return df.format(ret) + " " + sArray[idx]; } /** * KB, MB 등의 단위로 표현된 문자열을 byte 로 변환 * * @param size 문자열로 표기된 크기 * @return byte 단위로 변환된 값 */ private double textSizeToByteSize(String size) { String[] sArray = {"BYTES", "KB", "MB", "GB", "TB", "PB"}; size = size.toUpperCase(); for (int i = 0; i < sArray.length; i++) { if (size.contains(sArray[i])) { String sizeNumber = size.replaceAll(" ", "").replaceAll(sArray[i], ""); return Double.parseDouble(sizeNumber) * Math.pow(1024, i); } } return 0; } private boolean checkEndAsterisk(List apiList, String requestMethodUri) { boolean asterisk = false; if (apiList != null && apiList.size() > 0) { for (String api : apiList) { if (api.contains("*")) { String[] split = api.split("\\*"); if (!split[0].isEmpty() && requestMethodUri.startsWith(split[0])) { asterisk = true; } } } } return asterisk; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy