From 5e823e53352630c5f2e60e1be77e009e8b829234 Mon Sep 17 00:00:00 2001 From: kmw2378 Date: Tue, 9 Jul 2024 10:40:34 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[refactor]LoggingFilter=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20(#322)=20=EB=A1=9C=EA=B9=85=EC=9D=84=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=EC=85=89=ED=84=B0=20=EB=8B=A8=EA=B3=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20LoggingFi?= =?UTF-8?q?lter=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: kmw2378 --- .../backend/logging/filter/LoggingFilter.java | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 src/main/java/org/kakaoshare/backend/logging/filter/LoggingFilter.java diff --git a/src/main/java/org/kakaoshare/backend/logging/filter/LoggingFilter.java b/src/main/java/org/kakaoshare/backend/logging/filter/LoggingFilter.java deleted file mode 100644 index 51469302..00000000 --- a/src/main/java/org/kakaoshare/backend/logging/filter/LoggingFilter.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.kakaoshare.backend.logging.filter; - -import com.querydsl.core.util.StringUtils; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.kakaoshare.backend.logging.util.ApiQueryCounter; -import org.springframework.stereotype.Component; -import org.springframework.util.StopWatch; -import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.util.ContentCachingRequestWrapper; -import org.springframework.web.util.ContentCachingResponseWrapper; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.springframework.http.HttpHeaders.AUTHORIZATION; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; - -@Component -@Slf4j -@RequiredArgsConstructor -public class LoggingFilter extends OncePerRequestFilter { - private static final String REQUEST_LOG_NO_BODY_FORMAT = "REQUEST :: METHOD: {}, URL: {}, HAS_AUTHORIZATION: {}"; - private static final String REQUEST_LOG_FORMAT = REQUEST_LOG_NO_BODY_FORMAT + ", BODY: {}"; - private static final String RESPONSE_LOG_NO_BODY_FORMAT = "RESPONSE :: STATUS_CODE: {}, METHOD: {}, URL: {}, QUERY_COUNT: {}, TIME_TAKEN: {}ms"; - private static final String QUERY_COUNT_WARNING_LOG_FORMAT = "쿼리가 {}번 이상 실행되었습니다."; - - private static final int QUERY_COUNT_WARNING_STANDARD = 10; - private static final String START_OF_PARAMS = "?"; - private static final String PARAM_DELIMITER = "&"; - private static final String KEY_VALUE_DELIMITER = "="; - - private static final String METRIC_URL_PREFIX = "/actuator"; - private static final String FAVICON_URL = "/favicon.ico"; - - private final StopWatch apiTimer; - private final ApiQueryCounter apiQueryCounter; - - @Override - protected void doFilterInternal(final HttpServletRequest request, - final HttpServletResponse response, - final FilterChain filterChain) throws ServletException, IOException { - final ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request); - final ContentCachingResponseWrapper cachingResponse = new ContentCachingResponseWrapper(response); - final String requestURI = cachingRequest.getRequestURI(); - if (requestURI.contains(METRIC_URL_PREFIX) || requestURI.contains(FAVICON_URL)) { - return; - } - - apiTimer.start(); - filterChain.doFilter(cachingRequest, cachingResponse); - apiTimer.stop(); - - logRequestAndResponse(cachingRequest, cachingResponse); - cachingResponse.copyBodyToResponse(); - } - - private void logRequestAndResponse(final ContentCachingRequestWrapper request, - final ContentCachingResponseWrapper response) { - logRequest(request); - logResponse(request, response); - warnByQueryCount(); - } - - private void logRequest(final ContentCachingRequestWrapper request) { - final String requestBody = new String(request.getContentAsByteArray()); - final String requestURIWithParams = getRequestURIWithParams(request); - - if (requestBody.isBlank()) { - log.info(REQUEST_LOG_NO_BODY_FORMAT, request.getMethod(), requestURIWithParams, StringUtils.isNullOrEmpty(request.getHeader(AUTHORIZATION))); - return; - } - - log.info(REQUEST_LOG_FORMAT, request.getMethod(), requestURIWithParams, StringUtils.isNullOrEmpty(request.getHeader(AUTHORIZATION)), requestBody); - } - - private void logResponse(final ContentCachingRequestWrapper request, - final ContentCachingResponseWrapper response) { - final int queryCount = apiQueryCounter.getCount(); - final String requestURIWithParams = getRequestURIWithParams(request); - log.info(RESPONSE_LOG_NO_BODY_FORMAT, response.getStatus(), request.getMethod(), - requestURIWithParams, queryCount, apiTimer.getLastTaskTimeMillis()); - } - - private String getRequestURIWithParams(final ContentCachingRequestWrapper request) { - final String requestURI = request.getRequestURI(); - final Map params = request.getParameterMap(); - - if (params.isEmpty()) { - return requestURI; - } - - final String parsedParams = parseParams(params); - return requestURI + parsedParams; - } - - private String parseParams(final Map params) { - final String everyParamStrings = params.entrySet().stream() - .map(this::toParamString) - .collect(Collectors.joining(PARAM_DELIMITER)); - - return START_OF_PARAMS + everyParamStrings; - } - - private String toParamString(final Map.Entry entry) { - final String key = entry.getKey(); - final StringBuilder builder = new StringBuilder(); - - return Arrays.stream(entry.getValue()) - .map(value -> builder.append(key).append(KEY_VALUE_DELIMITER).append(value)) - .collect(Collectors.joining(PARAM_DELIMITER)); - } - - private Optional getJsonResponseBody(final ContentCachingResponseWrapper response) { - if (Objects.equals(response.getContentType(), APPLICATION_JSON_VALUE)) { - return Optional.of(new String(response.getContentAsByteArray())); - } - - return Optional.empty(); - } - - private void warnByQueryCount() { - final int queryCount = apiQueryCounter.getCount(); - if (queryCount >= QUERY_COUNT_WARNING_STANDARD) { - log.warn(QUERY_COUNT_WARNING_LOG_FORMAT, QUERY_COUNT_WARNING_STANDARD); - } - } -} From 82ba5f2c36c80b323c3d52db7f6082bf4b41363a Mon Sep 17 00:00:00 2001 From: kmw2378 Date: Tue, 9 Jul 2024 10:40:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[feat]LoggingInterceptor=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#322)=20=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20?= =?UTF-8?q?=EC=88=98=EC=A4=80=EC=97=90=EC=84=9C=20=EB=A1=9C=EA=B9=85?= =?UTF-8?q?=EC=9D=84=20=EC=88=98=ED=96=89=ED=95=98=EB=8A=94=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=EC=85=89=ED=84=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: kmw2378 --- .../interceptor/LoggingInterceptor.java | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java diff --git a/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java b/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java new file mode 100644 index 00000000..78cae4c3 --- /dev/null +++ b/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java @@ -0,0 +1,139 @@ +package org.kakaoshare.backend.logging.interceptor; + + +import com.querydsl.core.util.StringUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.kakaoshare.backend.logging.util.ApiQueryCounter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StopWatch; +import org.springframework.web.context.request.WebRequestInterceptor; +import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +@Component +@Slf4j +public class LoggingInterceptor extends WebRequestHandlerInterceptorAdapter { + private static final String REQUEST_LOG_NO_BODY_FORMAT = "REQUEST :: METHOD: {}, URL: {}, HAS_AUTHORIZATION: {}"; + private static final String REQUEST_LOG_FORMAT = REQUEST_LOG_NO_BODY_FORMAT + ", BODY: {}"; + private static final String RESPONSE_LOG_NO_BODY_FORMAT = "RESPONSE :: STATUS_CODE: {}, METHOD: {}, URL: {}, QUERY_COUNT: {}, TIME_TAKEN: {}ms"; + private static final String QUERY_COUNT_WARNING_LOG_FORMAT = "쿼리가 {}번 이상 실행되었습니다."; + + private static final int QUERY_COUNT_WARNING_STANDARD = 10; + private static final String START_OF_PARAMS = "?"; + private static final String PARAM_DELIMITER = "&"; + private static final String KEY_VALUE_DELIMITER = "="; + + private final StopWatch apiTimer; + private final ApiQueryCounter apiQueryCounter; + + @Autowired + public LoggingInterceptor(final WebRequestInterceptor requestInterceptor, + final StopWatch apiTimer, + final ApiQueryCounter apiQueryCounter) { + super(requestInterceptor); + this.apiTimer = apiTimer; + this.apiQueryCounter = apiQueryCounter; + } + + @Override + public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { + apiTimer.start(); + return true; + } + + @Override + public void afterCompletion(final HttpServletRequest request, + final HttpServletResponse response, + final Object handler, + final Exception ex) throws Exception { + final ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request); + final ContentCachingResponseWrapper cachingResponse = new ContentCachingResponseWrapper(response); + apiTimer.stop(); + logRequestAndResponse(cachingRequest, cachingResponse); + cachingResponse.copyBodyToResponse(); + } + + private void logRequestAndResponse(final ContentCachingRequestWrapper request, + final ContentCachingResponseWrapper response) { + logRequest(request); + logResponse(request, response); + warnByQueryCount(); + } + + private void logRequest(final ContentCachingRequestWrapper request) { + final String requestBody = new String(request.getContentAsByteArray()); + final String requestURIWithParams = getRequestURIWithParams(request); + + if (requestBody.isBlank()) { + log.info(REQUEST_LOG_NO_BODY_FORMAT, request.getMethod(), requestURIWithParams, StringUtils.isNullOrEmpty(request.getHeader(AUTHORIZATION))); + return; + } + + log.info(REQUEST_LOG_FORMAT, request.getMethod(), requestURIWithParams, StringUtils.isNullOrEmpty(request.getHeader(AUTHORIZATION)), requestBody); + } + + private void logResponse(final ContentCachingRequestWrapper request, + final ContentCachingResponseWrapper response) { + final int queryCount = apiQueryCounter.getCount(); + final String requestURIWithParams = getRequestURIWithParams(request); + log.info(RESPONSE_LOG_NO_BODY_FORMAT, response.getStatus(), request.getMethod(), + requestURIWithParams, queryCount, apiTimer.getLastTaskTimeMillis()); + } + + private String getRequestURIWithParams(final ContentCachingRequestWrapper request) { + final String requestURI = request.getRequestURI(); + final Map params = request.getParameterMap(); + + if (params.isEmpty()) { + return requestURI; + } + + final String parsedParams = parseParams(params); + return requestURI + parsedParams; + } + + private String parseParams(final Map params) { + final String everyParamStrings = params.entrySet().stream() + .map(this::toParamString) + .collect(Collectors.joining(PARAM_DELIMITER)); + + return START_OF_PARAMS + everyParamStrings; + } + + private String toParamString(final Map.Entry entry) { + final String key = entry.getKey(); + final StringBuilder builder = new StringBuilder(); + + return Arrays.stream(entry.getValue()) + .map(value -> builder.append(key).append(KEY_VALUE_DELIMITER).append(value)) + .collect(Collectors.joining(PARAM_DELIMITER)); + } + + private Optional getJsonResponseBody(final ContentCachingResponseWrapper response) { + if (Objects.equals(response.getContentType(), APPLICATION_JSON_VALUE)) { + return Optional.of(new String(response.getContentAsByteArray())); + } + + return Optional.empty(); + } + + private void warnByQueryCount() { + final int queryCount = apiQueryCounter.getCount(); + if (queryCount >= QUERY_COUNT_WARNING_STANDARD) { + log.warn(QUERY_COUNT_WARNING_LOG_FORMAT, QUERY_COUNT_WARNING_STANDARD); + } + } +} From 4eca5295a6dead3f893fa3786d5312a31f3b5774 Mon Sep 17 00:00:00 2001 From: kmw2378 Date: Tue, 9 Jul 2024 10:41:25 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[feat]LoggingConfig=EC=97=90=20Interceptor?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#322)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: kmw2378 --- .../backend/logging/config/LoggingConfig.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java b/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java index 53867e6f..12a92a69 100644 --- a/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java +++ b/src/main/java/org/kakaoshare/backend/logging/config/LoggingConfig.java @@ -1,15 +1,32 @@ package org.kakaoshare.backend.logging.config; +import lombok.RequiredArgsConstructor; +import org.kakaoshare.backend.logging.interceptor.LoggingInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StopWatch; import org.springframework.web.context.annotation.RequestScope; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -public class LoggingConfig { +@RequiredArgsConstructor +public class LoggingConfig implements WebMvcConfigurer { + private static final String METRIC_URL_PREFIX = "/actuator"; + private static final String FAVICON_URL = "/favicon.ico"; + + private final LoggingInterceptor loggingInterceptor; + @Bean @RequestScope public StopWatch stopWatch() { return new StopWatch(); } + + @Override + public void addInterceptors(final InterceptorRegistry registry) { + registry.addInterceptor(loggingInterceptor) + .addPathPatterns("/**") + .excludePathPatterns(METRIC_URL_PREFIX, FAVICON_URL); + } } From 7d4e414608f33dde5d378c49fb42a8544713254b Mon Sep 17 00:00:00 2001 From: kmw2378 Date: Tue, 9 Jul 2024 10:49:43 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[refactor]LoggingInterceptor=20=ED=83=80?= =?UTF-8?q?=EC=9D=B4=EB=A8=B8=20=EC=A2=85=EB=A3=8C=20=EC=8B=9C=EC=A0=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#322)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: kmw2378 --- .../backend/logging/interceptor/LoggingInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java b/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java index 78cae4c3..530c6c8a 100644 --- a/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java +++ b/src/main/java/org/kakaoshare/backend/logging/interceptor/LoggingInterceptor.java @@ -59,9 +59,9 @@ public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception { + apiTimer.stop(); final ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request); final ContentCachingResponseWrapper cachingResponse = new ContentCachingResponseWrapper(response); - apiTimer.stop(); logRequestAndResponse(cachingRequest, cachingResponse); cachingResponse.copyBodyToResponse(); }