개발을 진행하면서 몇 가지 설계상의 고민과 개선점이 생겨났습니다.
예외 로그 레벨: 클라이언트 오류에 error 로그가 맞을까?
상황 : 특정 리소스를 조회하는 API에서 해당 리소스가 존재하지 않으면 예외를 던지면서 함께 log.error를 기록했습니다.
예를들어 숙소를 조회할 때 해당 숙소가 존재하지 않는다면 NotFoundException을 발생시키고, 이 예외는 BaseException을 상속하고 log.error를 기록하도록 구현했습니다. 하지만 이러한 예외는 서버의 문제가 아닌 사용자의 잘못된 요청으로부터 발생하는 예외였습니다. 하지만 log.error가 발생하면 마치 서버 오류처럼 로그가 발생해서 오해를 불러일으킵니다. 운영중에도 이러한 로그를 모니터링하다 보니, 클라이언트의 잘못된 입력으로 인한 예외까지 error로 표시되면서 심각한 오류라고 판단할 오해의 소지가 있습니다.
그러기에 로그의 기준을 다시 생각해야 했습니다. "서버 측 문제가 아닌, 사용자 요청 오류라면 로그 레벨을 낮춰야 하지 않을까?"
그렇기에 로그의 레벨을 다시 한번 생각해보며 서버 내부 문제(5xx)와 사용자 잘못된 요청(4xx)을 구분해 로그를 남기도록 수정하고자 하였습니다.
해결: 새로운 예외 타입인 BadRequestException을 정의하여 이러한 경우에 사용했습니다. BadRequestException 은 RuntimeException을 상속하고, 별도로 경고 수준으로만 로그를 기록하도록 변경했습니다.
그리고 GlobalExceptionHandler에서는 BadRequestException 발생 시 log.warn로 경고 로그를 남깁니다(발견할 수 없는 요청 또한 되도록 발생하지 않도록 지양해야하기 때문에 warn레벨로 로그를 기록하였습니다). 반면 BaseException (주로 서버 내부 오류 용도)은 그대로 log.error("BaseException: ", e)로 에러 로그를 남깁니다GitHub.
결과: 이제 잘못된 요청으로 인한 예외는 로그에 warn으로 표시되어, 진짜 서버 측 문제와 구분됩니다. 사용자가 존재하지 않는 방 ID로 요청하면 RoomNotFoundException을 던지지만, 이는 BadRequestException을 상속받았기 때문에 warn 로그만 남습니다. 이로써 운영 시 error 로그가 줄어들고, 정말 확인이 필요한 치명적 오류만 error로 나타나게 되었습니다. 예를들어 Algolia를 활용해 키워드를 삽입할때 algolia와 연결에 실패하는것은 서버의 문제이기 때문에 해당 예외는 BaseException을 상속 받도록 구현하여 log.error를 기록하도록 하였습니다.
log.warn으로 기록하여 서버의 심각한 오류는 아니지만 자주 발생한다면 지양할 수 있는 방향을 고민해볼 수 있도록 하였고,
서버의 심각한 오류는 log.error로 기록하여 즉각적으로 문제를 모니터링할 수 있도록 수정하였습니다.
로그의 레벨에 따라 모니터링 하고 문제의 원인을 즉각적으로 파악할 수 있도록 로그의 원인과 레벨을 올바르게 설정하는 것이 중요하다라는 것을 깨달았습니다.
댓글