우리가 개발을 하면서 하루에도 수십번 말하고 듣는 말이 있습니다. “로그 확인해봤어??”
로그 (Log)
여기서 로그란 무엇일까요??
로그(Log)란 시스템이 동작할 때 시스템의 상태 및 동작 정보를 시간 경과에 따라 기록하는 것을 의미합니다.
예를 들어 우리가 회원가입을 한다고 가정해보았을 때, Spring 코드로는 아래와 같은 로직이 있겠죠??
@PostMapping
public ResponseEntity<?> join(@RequestBody MemberDto member){
}
위와 같은 로직에서 member 정보가 잘 넘어왔는지 확인하기 위하여 우리는 안에 담긴 내용들을 확인하는 작업들을 하는데 System.out.println() 을 사용하죠. 하지만 이 방법은 잘못된 방법입니다.
잘못된 로그 찍는 방법
- System.out
- System.err
- e.printTraceStack()
위 세 가지의 방법들은 무슨 문제가 있을까요??
- System.out은 log보다 더 많은 리소스를 요구합니다.
- log Level을 관리할 수 없다는 문제점이 있습니다.
- e.printTraceStack()은 기본적으로 System.err 를 사용하기 때문에 1번에 대한 문제점이 있습니다.
로그 레벨 (Log Level)
로그는 로그 레벨에 따라 출력될지 넘어갈지 결정을 하는데요 로그 레벨에 따른 심각도 수준은 아래와 같습니다.
Trace > Debug > Info > Warn > Error > Fatal
LEVEL | 설명 |
Trace | Debug 레벨보다 더 자세함. Dev 환경에서 버그를 활용하기 위해 사용한다. |
Debug | Info 레벨보다 더 자세한 정보가 필요할 때 사용. 개발환경에서 사용한다. |
Info | 명확한 의도가 있는 에러. 상태 변경이나 요구사항에 따른 시스템 변경에 사용한다. |
Warn | 에러가 될 수 있는 잠재적 가능성이 있는 경우 사용. |
Error | 의도하지 않은 에러가 발생할 경우. 서버가 종료되지 않는 정도 |
Fatal | 매우 심각한 에러. 프로그램이 종료되는 경우 |
예제 1
회원가입 시, DB에 동일한 email을 가진 회원이 있을 때,
DuplicationException을 던진다면 이 이벤트의 로그는 어떤 레벨을 적용할까?
>> Info
>> 개발자가 의도한 에러이기 때문이다.
예제 2
프로젝트 설정에서 Log Level을 Info로 설정해놓는다면 아래 결과는??
log.debug("log Check!!!!!");
💡 API를 개발할 때는 로그 레벨을 DEBUG로 설정해놓고 배포시에는 INFO나 Warn 레벨로 설정해라.
잘못된 로그 출력 방법
로그 레벨을 INFO로 두었다고 가정하고 아래 두 가지의 로그 출력 방법에 대해 비교해보겠습니다.
무슨 차이가 있을까요??
1. log.debug("log test : " + "1234"); // 문자열 연산
2. log.debug("log test : {}", 1234); // 파라미터 바인딩
👀 출력 결과
두 로그 모두 출력되지는 않습니다. 로그 레벨을 INFO로 설정해놓았기 때문에 더 높은 레벨인 DEBUG는 출력되지 않는 것이죠. 하지만 차이가 있습니다. 2번 로그는 로그가 출력되었을 때 괄호안에 대입을 하는 반면 1번 로그는 출력되지 않더라도 문자열 생성같은 연산이 발생합니다. 만약 1000번의 API 요청이 수행되었다고 가정했을 때 1000번의 문자열 생성 연산은 API에 치명적입니다.
👍 파라미터 바인딩
- slf4j의 {}의 문자열 바인딩 기능을 통해 해당 위치에 파라미터를 넣어 로그를 확인한다.
- 파라미터는 한 개 혹은 두 개 만을 사용하자 (3개 이상 사용 시, Object[] 을 생성한다)
log.debug("log test : {}", 1234);
//best
log.debug("User Info : {}", user); // user에 toString 구현
//worst
log.debug("User id : {}, password : {}", user.getId(), user.getPassword());
예외 처리에서의 Log 처리하기
잘못된 방법
try{
}catch(Exception e){
e.printTraceStack();
}
위에서도 언급했듯 try-catch 구문에서 e.printstackTrace() 를 이용하여 예외처리를 합니다.
e.printStackTrace()는 로그 레벨의 구분 없이 호출되며, 내부적으로 java.lang.System.err를 이용하여 로그를 남깁니다. 그래서 아래와 같은 심각한 문제점들이 발생합니다.
- 로그레벨의 구분이 없다.
- 커널 CPU 점유율이 높기 때문에 성능 이슈
- 원하는 메시지를 표현할 수 없다.
좋은 방법
try{
...
}catch(Exception e){
log.debug("Message : {}, id : {}", e.getMessage(), id);
}
- logger에서 연산이 발생하지 않는다.
- 로그 레벨에 따라 출력 여부가 결정되며 로그 레벨보다 높으면 toString() 실행 X
- 파라미터를 2개 이하로 설정 ( Object[] 가 만들어지지 않음.)
요약
👌 System.out.println() 과 Exception.printTraceStack() 같은 두 방법이 간편하고 익숙하더라도 스프링에는 라이브러리가 잘 되어 있으니 로그 라이브러리 활용을 해보자.
'BackEnd' 카테고리의 다른 글
세션 인증 방식과 토큰 인증 방식 (1) | 2023.07.13 |
---|---|
Interceptor 를 통해 회원 API 리팩토링하기 (1) | 2023.07.03 |
[Spring] Interceptor 와 Filter의 개념 및 차이점 (0) | 2023.06.20 |
Message Queue (3) | 2023.04.27 |
티켓 예약 서비스의 대규모 트래픽 처리에 대한 고민 (1) | 2023.04.22 |