diff --git a/api/common-api/src/main/java/kr/ac/kookmin/stream/api/common/GlobalExceptionHandler.java b/api/common-api/src/main/java/kr/ac/kookmin/stream/api/common/GlobalExceptionHandler.java index 3d364ca3..276fb937 100644 --- a/api/common-api/src/main/java/kr/ac/kookmin/stream/api/common/GlobalExceptionHandler.java +++ b/api/common-api/src/main/java/kr/ac/kookmin/stream/api/common/GlobalExceptionHandler.java @@ -23,8 +23,14 @@ public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ResponseEntity> handleBusiness(BusinessException e) { - log.warn("[requestId={}, userId={}] ({}) {}", - MDC.get(REQUEST_ID), MDC.get(USER_ID), e.getErrorCode().name(), e.getMessage()); + // 인증 실패(401)는 인터넷에 노출된 서버에서 스캐너·봇으로 인해 상시 발생하는 정상 트래픽이므로 DEBUG로 낮춘다 + if (e.getErrorCode().status() == ErrorStatus.UNAUTHORIZED) { + log.debug("[requestId={}, userId={}] ({}) {}", + MDC.get(REQUEST_ID), MDC.get(USER_ID), e.getErrorCode().name(), e.getMessage()); + } else { + log.warn("[requestId={}, userId={}] ({}) {}", + MDC.get(REQUEST_ID), MDC.get(USER_ID), e.getErrorCode().name(), e.getMessage()); + } return ResponseEntity.status(e.getErrorCode().status()).body(ApiResponse.error(e)); } diff --git a/gateway/logging/src/main/java/kr/ac/kookmin/stream/logging/LoggingExclusions.java b/gateway/logging/src/main/java/kr/ac/kookmin/stream/logging/LoggingExclusions.java index 115a5dba..1425fb1b 100644 --- a/gateway/logging/src/main/java/kr/ac/kookmin/stream/logging/LoggingExclusions.java +++ b/gateway/logging/src/main/java/kr/ac/kookmin/stream/logging/LoggingExclusions.java @@ -1,20 +1,26 @@ package kr.ac.kookmin.stream.logging; import jakarta.servlet.http.HttpServletRequest; +import java.util.Set; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * 로깅 필터가 건너뛸 요청 판별. 헬스체크는 빈번히 폴링되므로 MDC·액세스 로그 대상에서 제외한다. * 경로는 액추에이터 기본값이자 security 모듈 {@code PublicEndpoints.HEALTH_CHECK}와 같은 값이다. + * 또한 브라우저·크롤러·스캐너가 상시 찔러보는 노이즈 경로도 제외한다. */ @NoArgsConstructor(access = AccessLevel.PRIVATE) final class LoggingExclusions { private static final String HEALTH_CHECK_PATH = "/actuator/health"; + // 실존 엔드포인트가 아니지만 브라우저·크롤러·봇 스캐너가 상시 요청해 401 노이즈만 남기는 경로 + private static final Set NOISE_PATHS = Set.of("/", "/robots.txt", "/favicon.ico"); + static boolean isExcluded(HttpServletRequest request) { String path = request.getRequestURI(); - return path.equals(HEALTH_CHECK_PATH) || path.startsWith(HEALTH_CHECK_PATH + "/"); + return path.equals(HEALTH_CHECK_PATH) || path.startsWith(HEALTH_CHECK_PATH + "/") + || NOISE_PATHS.contains(path); } }