로그(Log) 확인해봤어?? 로그 제대로 알자

우리가 개발을 하면서 하루에도 수십번 말하고 듣는 말이 있습니다. “로그 확인해봤어??”

 

로그 (Log)

여기서 로그란 무엇일까요??

 

로그(Log)란 시스템이 동작할 때 시스템의 상태 및 동작 정보를 시간 경과에 따라 기록하는 것을 의미합니다.

예를 들어 우리가 회원가입을 한다고 가정해보았을 때, Spring 코드로는 아래와 같은 로직이 있겠죠??

@PostMapping
public ResponseEntity<?> join(@RequestBody MemberDto member){

}

위와 같은 로직에서 member 정보가 잘 넘어왔는지 확인하기 위하여 우리는 안에 담긴 내용들을 확인하는 작업들을 하는데 System.out.println() 을 사용하죠. 하지만 이 방법은 잘못된 방법입니다.

 

 

잘못된 로그 찍는 방법
  • System.out
  • System.err
  • e.printTraceStack()

 

위 세 가지의 방법들은 무슨 문제가 있을까요??

 

  1. System.out은 log보다 더 많은 리소스를 요구합니다.
  2. log Level을 관리할 수 없다는 문제점이 있습니다.
  3. 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() 같은 두 방법이 간편하고 익숙하더라도 스프링에는 라이브러리가 잘 되어 있으니 로그 라이브러리 활용을 해보자.