From fa25891cc9e8fcf010511c2da4b5485160831ed4 Mon Sep 17 00:00:00 2001 From: hod Date: Thu, 12 Mar 2026 12:47:15 +0900 Subject: [PATCH] Implement MDC logging for request tracking and enhance error handling with member ID context and Change Android Notification Priority --- .../com/moa/common/auth/AuthMemberResolver.kt | 2 + .../auth/OnboardingAuthMemberResolver.kt | 2 + .../exception/GlobalExceptionHandler.kt | 13 ++++++- .../com/moa/common/filter/MdcLoggingFilter.kt | 38 +++++++++++++++++++ src/main/kotlin/com/moa/service/FcmService.kt | 5 +++ src/main/resources/application-local.yml | 1 - src/main/resources/logback-spring.xml | 22 +++++++++++ 7 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/com/moa/common/filter/MdcLoggingFilter.kt create mode 100644 src/main/resources/logback-spring.xml diff --git a/src/main/kotlin/com/moa/common/auth/AuthMemberResolver.kt b/src/main/kotlin/com/moa/common/auth/AuthMemberResolver.kt index a51828b..f8662d7 100644 --- a/src/main/kotlin/com/moa/common/auth/AuthMemberResolver.kt +++ b/src/main/kotlin/com/moa/common/auth/AuthMemberResolver.kt @@ -6,6 +6,7 @@ import com.moa.common.exception.UnauthorizedException import com.moa.repository.* import io.jsonwebtoken.ExpiredJwtException import jakarta.servlet.http.HttpServletRequest +import org.slf4j.MDC import org.springframework.core.MethodParameter import org.springframework.stereotype.Component import org.springframework.web.bind.support.WebDataBinderFactory @@ -37,6 +38,7 @@ class AuthMemberResolver( binderFactory: WebDataBinderFactory?, ): AuthMemberInfo { val memberId = resolveMemberId() + MDC.put("memberId", memberId.toString()) validateOnboardingCompleted(memberId) return AuthMemberInfo(id = memberId) } diff --git a/src/main/kotlin/com/moa/common/auth/OnboardingAuthMemberResolver.kt b/src/main/kotlin/com/moa/common/auth/OnboardingAuthMemberResolver.kt index 6a8db67..b47dc11 100644 --- a/src/main/kotlin/com/moa/common/auth/OnboardingAuthMemberResolver.kt +++ b/src/main/kotlin/com/moa/common/auth/OnboardingAuthMemberResolver.kt @@ -4,6 +4,7 @@ import com.moa.common.exception.ErrorCode import com.moa.common.exception.UnauthorizedException import io.jsonwebtoken.ExpiredJwtException import jakarta.servlet.http.HttpServletRequest +import org.slf4j.MDC import org.springframework.core.MethodParameter import org.springframework.stereotype.Component import org.springframework.web.bind.support.WebDataBinderFactory @@ -37,6 +38,7 @@ class OnboardingAuthMemberResolver( val memberId = jwtTokenProvider.getUserIdFromToken(token) ?: throw UnauthorizedException() + MDC.put("memberId", memberId.toString()) return AuthMemberInfo(id = memberId) } catch (ex: ExpiredJwtException) { throw UnauthorizedException(ErrorCode.EXPIRED_TOKEN) diff --git a/src/main/kotlin/com/moa/common/exception/GlobalExceptionHandler.kt b/src/main/kotlin/com/moa/common/exception/GlobalExceptionHandler.kt index e357102..9106797 100644 --- a/src/main/kotlin/com/moa/common/exception/GlobalExceptionHandler.kt +++ b/src/main/kotlin/com/moa/common/exception/GlobalExceptionHandler.kt @@ -2,6 +2,7 @@ package com.moa.common.exception import com.moa.common.response.ApiResponse import com.moa.common.response.FieldError +import jakarta.servlet.http.HttpServletRequest import org.slf4j.LoggerFactory import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus @@ -26,14 +27,22 @@ class GlobalExceptionHandler : ResponseEntityExceptionHandler() { } @ExceptionHandler(UnauthorizedException::class) - fun handleUnauthorizedException(ex: UnauthorizedException): ResponseEntity> { + fun handleUnauthorizedException( + ex: UnauthorizedException, + request: HttpServletRequest, + ): ResponseEntity> { + log.warn("Unauthorized: {} {} - {}", request.method, request.requestURI, ex.errorCode) return ResponseEntity .status(HttpStatus.UNAUTHORIZED) .body(ApiResponse.error(ex.errorCode)) } @ExceptionHandler(ForbiddenException::class) - fun handleForbiddenException(ex: ForbiddenException): ResponseEntity> { + fun handleForbiddenException( + ex: ForbiddenException, + request: HttpServletRequest, + ): ResponseEntity> { + log.warn("Forbidden: {} {} - {}", request.method, request.requestURI, ex.errorCode) return ResponseEntity .status(HttpStatus.FORBIDDEN) .body(ApiResponse.error(ex.errorCode)) diff --git a/src/main/kotlin/com/moa/common/filter/MdcLoggingFilter.kt b/src/main/kotlin/com/moa/common/filter/MdcLoggingFilter.kt new file mode 100644 index 0000000..560c75c --- /dev/null +++ b/src/main/kotlin/com/moa/common/filter/MdcLoggingFilter.kt @@ -0,0 +1,38 @@ +package com.moa.common.filter + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.slf4j.LoggerFactory +import org.slf4j.MDC +import org.springframework.core.Ordered +import org.springframework.core.annotation.Order +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import java.util.UUID + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +class MdcLoggingFilter : OncePerRequestFilter() { + + private val log = LoggerFactory.getLogger(javaClass) + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain, + ) { + MDC.put("requestId", UUID.randomUUID().toString().take(8)) + + val start = System.currentTimeMillis() + log.info("→ {} {}", request.method, request.requestURI) + + try { + filterChain.doFilter(request, response) + } finally { + val duration = System.currentTimeMillis() - start + log.info("← {} {} {} {}ms", request.method, request.requestURI, response.status, duration) + MDC.clear() + } + } +} diff --git a/src/main/kotlin/com/moa/service/FcmService.kt b/src/main/kotlin/com/moa/service/FcmService.kt index 94f5ea9..21afe5b 100644 --- a/src/main/kotlin/com/moa/service/FcmService.kt +++ b/src/main/kotlin/com/moa/service/FcmService.kt @@ -41,6 +41,11 @@ class FcmService( .setBody(data["body"]) .build() ) + .setAndroidConfig( + AndroidConfig.builder() + .setPriority(AndroidConfig.Priority.HIGH) + .build() + ) .putAllData(data) .build() diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 3cfb78a..9711217 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -5,7 +5,6 @@ spring: properties: hibernate: format_sql: true - show_sql: true defer-datasource-initialization: true h2: console: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..204bbc2 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,22 @@ + + + + %d{HH:mm:ss.SSS} %-5level [%X{requestId:-?}] [member:%X{memberId:-?}] %logger{30} - %msg%n + + + + + + + + + + + + + + + + + +