From 079ec160d028724afd8bf0331e77ded244ce5915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=9C=EA=B4=80=ED=9D=AC?= Date: Sat, 1 Mar 2025 23:24:11 +0900 Subject: [PATCH] =?UTF-8?q?feat(Logging):=20=EB=A1=9C=EA=B9=85=EC=97=90=20?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20Logback=20MDC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/stempo/config/SecurityConfig.java | 6 ++ .../java/com/stempo/filter/MDCFilter.java | 40 +++++++++ .../main/java/com/stempo/util/ApiLogger.java | 87 +++++++++++++++---- 3 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 stempo-auth/src/main/java/com/stempo/filter/MDCFilter.java diff --git a/stempo-auth/src/main/java/com/stempo/config/SecurityConfig.java b/stempo-auth/src/main/java/com/stempo/config/SecurityConfig.java index 43466ee4..d6566edd 100644 --- a/stempo-auth/src/main/java/com/stempo/config/SecurityConfig.java +++ b/stempo-auth/src/main/java/com/stempo/config/SecurityConfig.java @@ -3,6 +3,7 @@ import com.stempo.application.JwtTokenService; import com.stempo.filter.CustomBasicAuthenticationFilter; import com.stempo.filter.JwtAuthenticationFilter; +import com.stempo.filter.MDCFilter; import com.stempo.util.ApiLogger; import com.stempo.util.HttpReqResUtils; import com.stempo.util.IpWhitelistValidator; @@ -37,6 +38,7 @@ public class SecurityConfig { .AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer; private final JwtTokenService tokenService; private final IpWhitelistValidator ipWhitelistValidator; + private final MDCFilter mdcFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @@ -48,6 +50,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests( authorizeHttpRequestsCustomizer ) + .addFilterBefore( + mdcFilter, + UsernamePasswordAuthenticationFilter.class + ) .addFilterBefore( new CustomBasicAuthenticationFilter(authenticationManager, ipWhitelistValidator), UsernamePasswordAuthenticationFilter.class diff --git a/stempo-auth/src/main/java/com/stempo/filter/MDCFilter.java b/stempo-auth/src/main/java/com/stempo/filter/MDCFilter.java new file mode 100644 index 00000000..7d95fdbd --- /dev/null +++ b/stempo-auth/src/main/java/com/stempo/filter/MDCFilter.java @@ -0,0 +1,40 @@ +package com.stempo.filter; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.UUID; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +@Component +public class MDCFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest httpRequest = (HttpServletRequest) request; + try { + String transactionId = httpRequest.getHeader("X-Transaction-Id"); + if (transactionId == null || transactionId.isEmpty()) { + transactionId = UUID.randomUUID().toString(); + } + MDC.put("transactionId", transactionId); + + String requestId = httpRequest.getHeader("X-Request-Id"); + if (requestId == null || requestId.isEmpty()) { + requestId = UUID.randomUUID().toString(); + } + MDC.put("requestId", requestId); + + chain.doFilter(request, response); + } finally { + MDC.clear(); + } + } +} diff --git a/stempo-common/src/main/java/com/stempo/util/ApiLogger.java b/stempo-common/src/main/java/com/stempo/util/ApiLogger.java index 6ddabe09..6737295b 100644 --- a/stempo-common/src/main/java/com/stempo/util/ApiLogger.java +++ b/stempo-common/src/main/java/com/stempo/util/ApiLogger.java @@ -3,6 +3,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; @@ -14,44 +15,96 @@ public class ApiLogger { private ApiLogger() { } - public static void logRequest(HttpServletRequest request, HttpServletResponse response, String clientIpAddress, - String message) { + public static void logRequest(HttpServletRequest request, HttpServletResponse response, + String clientIpAddress, String message) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String id = - (authentication == null || authentication.getName() == null) ? "anonymous" : authentication.getName(); + String userId = (authentication == null || authentication.getName() == null) + ? "anonymous" : authentication.getName(); String requestUrl = request.getRequestURI(); String queryString = request.getQueryString(); - String fullUrl = queryString == null ? requestUrl : requestUrl + "?" + queryString; - + if (queryString != null) { + requestUrl += "?" + queryString; + } String httpMethod = request.getMethod(); int httpStatus = response.getStatus(); - log.info("[{}:{}] {} {} {} {}", clientIpAddress, id, fullUrl, httpMethod, httpStatus, message); + String userAgent = request.getHeader("User-Agent"); + String serviceName = "stempo-core"; + String env = System.getProperty("spring.profiles.active", "default"); + + MDC.put("clientIp", clientIpAddress); + MDC.put("userId", userId); + MDC.put("requestUrl", requestUrl); + MDC.put("httpMethod", httpMethod); + MDC.put("httpStatus", String.valueOf(httpStatus)); + if (userAgent != null) { + MDC.put("userAgent", userAgent); + } + MDC.put("serviceName", serviceName); + MDC.put("env", env); + + log.info(message); + + MDC.remove("clientIp"); + MDC.remove("userId"); + MDC.remove("requestUrl"); + MDC.remove("httpMethod"); + MDC.remove("httpStatus"); + MDC.remove("userAgent"); + MDC.remove("serviceName"); + MDC.remove("env"); } public static void logRequestDuration(HttpServletRequest request, HttpServletResponse response, Exception ex) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String id = - (authentication == null || authentication.getName() == null) ? "anonymous" : authentication.getName(); - String clientIpAddress = HttpReqResUtils.getClientIpAddressIfServletRequestExist(); + String userId = (authentication == null || authentication.getName() == null) + ? "anonymous" : authentication.getName(); + String clientIpAddress = HttpReqResUtils.getClientIpAddressIfServletRequestExist(); String requestUrl = request.getRequestURI(); String queryString = request.getQueryString(); - String fullUrl = queryString == null ? requestUrl : requestUrl + "?" + queryString; - + if (queryString != null) { + requestUrl += "?" + queryString; + } String httpMethod = request.getMethod(); int httpStatus = response.getStatus(); long startTime = (Long) request.getAttribute("startTime"); - long endTime = TimeUtils.currentTimeMillis(); - long duration = endTime - startTime; + long duration = System.currentTimeMillis() - startTime; + + String userAgent = request.getHeader("User-Agent"); + String serviceName = "stempo-core"; + String env = System.getProperty("spring.profiles.active", "default"); + + MDC.put("clientIp", clientIpAddress); + MDC.put("userId", userId); + MDC.put("requestUrl", requestUrl); + MDC.put("httpMethod", httpMethod); + MDC.put("httpStatus", String.valueOf(httpStatus)); + MDC.put("durationMs", String.valueOf(duration)); + if (userAgent != null) { + MDC.put("userAgent", userAgent); + } + MDC.put("serviceName", serviceName); + MDC.put("env", env); if (ex == null) { - log.info("[{}:{}] {} {} {} {}ms", clientIpAddress, id, fullUrl, httpMethod, httpStatus, duration); + log.info("Request completed successfully"); } else { - log.error("[{}:{}] {} {} {} {}ms, Exception: {}", clientIpAddress, id, fullUrl, httpMethod, httpStatus, - duration, ex.getMessage()); + MDC.put("exception", ex.getMessage()); + log.error("Request completed with error"); + MDC.remove("exception"); } + + MDC.remove("clientIp"); + MDC.remove("userId"); + MDC.remove("requestUrl"); + MDC.remove("httpMethod"); + MDC.remove("httpStatus"); + MDC.remove("durationMs"); + MDC.remove("userAgent"); + MDC.remove("serviceName"); + MDC.remove("env"); } }