티스토리 뷰
Spring에서 Controller의 메서드에 예외가 발생되면 @ControllerAdvice가 선언된 클래스가 동작하여 발생된 예외타입에 맞는 메서드가 호출된다. 그리고 그 메서드 내부에는 개발자마다 다르겠지만 e.printStackTrace()를 호출하는 코드가 작성되있는 경우가 있다. 혹은 특정 로직을 try/catch로 감싸서 처리할 경우 catch 블록에도 e.printStackTrace()를 호출하는 코드가 작성되있는 경우가 있다
그러나 e.printStackTrace()는 특히 운영환경에서는 지양해야될 요소이며 이유는 다음과 같다.
- JAVA의 Reflection을 이용하여 예외를 추적하는 것이라 많은 오버헤드가 발생할 수 있다.
- 메서드 스택정보를 취합하기 때문에 서버 부하의 원인이기도 하다.
- 내부적으로 System.err을 사용하는데 비용이 비싼편이다.
여기서 한가지 생각이 든 점은 e.printStackTrace()를 사용하면 메서드 스택트레이스에 대한 정보가 나오며 스택트레이스 맨 위에 출력되는 곳이 실제 예외가 발생된 곳인데 그 부분만 로깅해보면 어떨까 라는 생각나서 바로 구현을 해보았다.
기존코드
/hello로 요청이 들어올 경우 현재날짜를 출력하고 예외를 발생시키고 발생된 예외를 로깅해보자 log.error() 메서드의 두번째 인자에 Throwable 인스턴스를 던져주면 내부적으로 e.printStackTrace()의 내용이 로깅된다.
애플리케이션에서 출력된 로그를 AWS Cloud Watch에 전송한다. Spring에서 발생된 로그를 AWS Cloud Watch에 전송하는 방법은 여기에서 볼 수 있다.
이렇게 e.printStackTrace()의 결과를 모두 출력하는 것 보단 예외가 발생된 부분만 출력해주면 불필요한 로깅이 되지않고 필요한 부분만 할 수 있어서 성능에 이점이 생길 수 있다.
Throwable 클래스에 getStackTrace() 라는 StackTraceElement 배열타입의 메서드를 반환하는데 이 메서드 반환값의 인덱스 0번째 요소가 실제 예외가 발생된 지점이다.
변경코드
예외 메세지가 비어있을 경우를 고려하여 다음과 같이 설계하였다.
예외가 발생된 지점만 출력이 되었다.
Cloud Watch에 로그를 전송해보니 예외가 발생된 부분만 로깅되었다. 개인적인 생각이지만 위에서 e.printStackTrace()로 로깅했을 때보다 System.err를 사용하지 않으면서 예외가 발생된 부분만 로깅하니까 더 깔끔하고 나은 것 같다.
참고
JAVA에서는 메서드가 호출되면 메서드의 콜스택을 추적하기 위해 스택 트레이스라는 곳에 메서드의 콜스택 정보들이 적재되며 예외가 발생되면 마지막 호출이 된 메서드 콜스택 정보를 포함해서 System.err를 이용하여 출력해준다.