Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: ExceptionRestHandler 로깅을 ErrorType에 따른 레벨로 수정 #268

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 32 additions & 23 deletions src/main/java/com/dnd/runus/global/exception/type/ErrorType.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lombok.experimental.Accessors;
import org.springframework.http.HttpStatus;

import static com.dnd.runus.global.exception.type.ErrorType.Level.*;
import static lombok.AccessLevel.PRIVATE;
import static org.springframework.http.HttpStatus.*;

Expand All @@ -13,43 +14,51 @@
@RequiredArgsConstructor(access = PRIVATE)
public enum ErrorType {
// WebErrorType
UNHANDLED_EXCEPTION(INTERNAL_SERVER_ERROR, "WEB_001", "직접적으로 처리되지 않은 예외, 문의해주세요"),
FAILED_VALIDATION(BAD_REQUEST, "WEB_002", "Request 요청에서 올바르지 않은 값이 있습니다"),
FAILED_PARSING(BAD_REQUEST, "WEB_003", "Request JSON body를 파싱하지 못했습니다"),
UNSUPPORTED_API(BAD_REQUEST, "WEB_004", "지원하지 않는 API입니다"),
COOKIE_NOT_FOND(BAD_REQUEST, "WEB_005", "요청에 쿠키가 필요합니다"),
INVALID_BASE64(BAD_REQUEST, "WEB_006", "잘못된 Base64 문자열입니다"),
UNHANDLED_EXCEPTION(INTERNAL_SERVER_ERROR, ERROR, "WEB_001", "직접적으로 처리되지 않은 예외, 문의해주세요"),
FAILED_VALIDATION(BAD_REQUEST, WARN, "WEB_002", "Request 요청에서 올바르지 않은 값이 있습니다"),
FAILED_PARSING(BAD_REQUEST, WARN, "WEB_003", "Request JSON body를 파싱하지 못했습니다"),
UNSUPPORTED_API(BAD_REQUEST, DEBUG, "WEB_004", "지원하지 않는 API입니다"),
COOKIE_NOT_FOND(BAD_REQUEST, DEBUG, "WEB_005", "요청에 쿠키가 필요합니다"),
INVALID_BASE64(BAD_REQUEST, DEBUG, "WEB_006", "잘못된 Base64 문자열입니다"),

// AuthErrorType
FAILED_AUTHENTICATION(UNAUTHORIZED, "AUTH_001", "인증에 실패하였습니다"),
INVALID_ACCESS_TOKEN(UNAUTHORIZED, "AUTH_002", "유효하지 않은 토큰입니다"),
EXPIRED_ACCESS_TOKEN(UNAUTHORIZED, "AUTH_003", "만료된 토큰입니다"),
MALFORMED_ACCESS_TOKEN(UNAUTHORIZED, "AUTH_004", "잘못된 형식의 토큰입니다"),
TAMPERED_ACCESS_TOKEN(UNAUTHORIZED, "AUTH_005", "변조된 토큰입니다"),
UNSUPPORTED_JWT_TOKEN(UNAUTHORIZED, "AUTH_006", "지원하지 않는 JWT 토큰입니다"),
UNSUPPORTED_SOCIAL_TYPE(UNAUTHORIZED, "AUTH_007", "지원하지 않는 소셜 타입입니다."),
INVALID_CREDENTIALS(UNAUTHORIZED, "AUTH_008", "해당 사용자의 정보가 없거나 일치하지 않아 처리할 수 없습니다."),
FAILED_AUTHENTICATION(UNAUTHORIZED, INFO, "AUTH_001", "인증에 실패하였습니다"),
INVALID_ACCESS_TOKEN(UNAUTHORIZED, DEBUG, "AUTH_002", "유효하지 않은 토큰입니다"),
EXPIRED_ACCESS_TOKEN(UNAUTHORIZED, DEBUG, "AUTH_003", "만료된 토큰입니다"),
MALFORMED_ACCESS_TOKEN(UNAUTHORIZED, DEBUG, "AUTH_004", "잘못된 형식의 토큰입니다"),
TAMPERED_ACCESS_TOKEN(UNAUTHORIZED, DEBUG, "AUTH_005", "변조된 토큰입니다"),
UNSUPPORTED_JWT_TOKEN(UNAUTHORIZED, DEBUG, "AUTH_006", "지원하지 않는 JWT 토큰입니다"),
UNSUPPORTED_SOCIAL_TYPE(UNAUTHORIZED, DEBUG, "AUTH_007", "지원하지 않는 소셜 타입입니다"),
INVALID_CREDENTIALS(UNAUTHORIZED, DEBUG, "AUTH_008", "해당 사용자의 정보가 없거나 일치하지 않아 처리할 수 없습니다"),

// OauthErrorType
USER_NOT_FOUND(NOT_FOUND, "OAUTH_001", "존재하지 않은 사용자 입니다."),
USER_NOT_FOUND(NOT_FOUND, DEBUG, "OAUTH_001", "존재하지 않은 사용자 입니다"),

// DatabaseErrorType
ENTITY_NOT_FOUND(NOT_FOUND, "DB_001", "해당 엔티티를 찾을 수 없습니다"),
VIOLATION_OCCURRED(NOT_ACCEPTABLE, "DB_002", "저장할 수 없는 값입니다"),
ENTITY_NOT_FOUND(NOT_FOUND, DEBUG, "DB_001", "해당 엔티티를 찾을 수 없습니다"),
VIOLATION_OCCURRED(NOT_ACCEPTABLE, ERROR, "DB_002", "저장할 수 없는 값입니다"),

// TimeErrorType
START_AFTER_END(BAD_REQUEST, "TIME_001", "시작 시간이 종료 시간보다 빨라야 합니다"),
START_AFTER_END(BAD_REQUEST, DEBUG, "TIME_001", "시작 시간이 종료 시간보다 빨라야 합니다"),

// RunningErrorType
ROUTE_MUST_HAVE_AT_LEAST_TWO_COORDINATES(BAD_REQUEST, "RUNNING_001", "경로는 최소 2개의 좌표를 가져야 합니다"),
CHALLENGE_MODE_WITH_PERSONAL_GOAL(BAD_REQUEST, "RUNNING_002", "챌린지 모드에서는 개인 목표를 설정할 수 없습니다."),
GOAL_MODE_WITH_CHALLENGE_ID(BAD_REQUEST, "RUNNING_003", "개인 목표 모드에서는 챌린지 ID를 설정할 수 없습니다."),
GOAL_TIME_AND_DISTANCE_BOTH_EXIST(BAD_REQUEST, "RUNNING_004", "개인 목표 시간과 거리 중 하나만 설정해야 합니다."),
ROUTE_MUST_HAVE_AT_LEAST_TWO_COORDINATES(BAD_REQUEST, DEBUG, "RUNNING_001", "경로는 최소 2개의 좌표를 가져야 합니다"),
CHALLENGE_MODE_WITH_PERSONAL_GOAL(BAD_REQUEST, DEBUG, "RUNNING_002", "챌린지 모드에서는 개인 목표를 설정할 수 없습니다"),
GOAL_MODE_WITH_CHALLENGE_ID(BAD_REQUEST, DEBUG, "RUNNING_003", "개인 목표 모드에서는 챌린지 ID를 설정할 수 없습니다"),
GOAL_TIME_AND_DISTANCE_BOTH_EXIST(BAD_REQUEST, DEBUG, "RUNNING_004", "개인 목표 시간과 거리 중 하나만 설정해야 합니다"),

// WeatherErrorType
WEATHER_API_ERROR(SERVICE_UNAVAILABLE, "WEATHER_001", "날씨 API 호출 중 오류가 발생했습니다"),
WEATHER_API_ERROR(SERVICE_UNAVAILABLE, WARN, "WEATHER_001", "날씨 API 호출 중 오류가 발생했습니다"),
;
private final HttpStatus httpStatus;
private final Level level;
private final String code;
private final String message;

public enum Level {
INFO,
DEBUG,
WARN,
ERROR
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@
public class ExceptionRestHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<ApiErrorDto> handleBaseException(BaseException e) {
log.warn(e.getMessage());
return toResponseEntity(e.getType(), e.getMessage());
}

@ExceptionHandler(AuthException.class)
public ResponseEntity<ApiErrorDto> handleAuthException(AuthException e) {
log.warn(e.getMessage());
return toResponseEntity(e.getType(), e.getMessage());
}

Expand Down Expand Up @@ -64,6 +62,11 @@ public ResponseEntity<ApiErrorDto> handleInsufficientAuthenticationException(
return toResponseEntity(ErrorType.FAILED_AUTHENTICATION, e.getMessage());
}

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ApiErrorDto> handleIllegalArgumentException(IllegalArgumentException e) {
return toResponseEntity(ErrorType.FAILED_VALIDATION, e.getMessage());
}

////////////////// 요청 파라미터 예외 / 타입 불일치, Enum 매개변수 관련 예외
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<ApiErrorDto> handleMethodArgumentTypeMismatchException(
Expand Down Expand Up @@ -103,7 +106,6 @@ public ResponseEntity<ApiErrorDto> handleMethodArgumentTypeMismatchException(
////////////////// 직렬화 / 역직렬화 예외
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiErrorDto> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
log.warn(ex.getBindingResult().getAllErrors().toString());
return toResponseEntity(
ErrorType.FAILED_VALIDATION,
ex.getBindingResult().getAllErrors().toString());
Expand All @@ -117,21 +119,29 @@ public ResponseEntity<ApiErrorDto> handleHttpMessageNotReadableException(HttpMes
////////////////// Database 관련 예외
@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<ApiErrorDto> handleDataIntegrityViolationException(DataIntegrityViolationException ex) {
log.warn(ex.getMessage(), ex);
return toResponseEntity(ErrorType.VIOLATION_OCCURRED, ex);
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ApiErrorDto> handleConstraintViolationException(ConstraintViolationException ex) {
log.warn(ex.getMessage(), ex);
return toResponseEntity(ErrorType.VIOLATION_OCCURRED, ex);
}

private static ResponseEntity<ApiErrorDto> toResponseEntity(@NotNull ErrorType type, Exception exception) {
return toResponseEntity(type, exception.getMessage());
return toResponseEntity(type, exception.getClass().getName() + ": " + exception.getMessage());
}

private static ResponseEntity<ApiErrorDto> toResponseEntity(@NotNull ErrorType type, String message) {
loggingExceptionByErrorType(type, message);
return ResponseEntity.status(type.httpStatus().value()).body(ApiErrorDto.of(type, message));
}

private static void loggingExceptionByErrorType(ErrorType type, String message) {
switch (type.level()) {
case INFO -> log.info(message);
case DEBUG -> log.debug(message);
case WARN -> log.warn(message);
default -> log.error(message);
}
}
}