2023.08.15 - [IT/고찰] - Comment에 대한 고찰
Commit Message와 Comment에 이은 고찰 시리즈 3탄, Log
Log
필요성
로그는 프로세스의 흐름, 장애 파악 등 정보 수집에서 중요한 역할을 담당합니다. 오류가 발생하면 가장 먼저 어디서 잘못되었는지 로그 검색을 통해 정보를 알아냅니다.
하지만 잘못된 로그들은 오히려 개발자들에게 혼란을 가중시킵니다. 또한, 의미없는 로그 데이터들로 인해 중요한 정보를 보기까지 많은 어려움을 겪게 됩니다. 실제로 프로젝트를 진행하면서 정말 중요한 흐름만 기록을 했다면, 에러가 난 부분을 명확하게 표현했다면 간단하게 고쳐질 문제들이 대부분이었습니다. 그러나 그 원인을 찾기까지 정말 수많은 시간을 소모하였습니다.
Commit Message, Comment는 개발자들의 협업을 위한 것이었다면, Log는 좋은 프로그램을 만들어나가기 위해서 중요하게 작성되어야 합니다.
로그 남기기
로그는 많이 접하지만 그만큼 무시되는 경우도 종종 있습니다. 에러가 분명히 발생했는데, 어디서 어떻게 발생한 것인지 찾아 헤매는 시간만큼 개발은 더뎌지게 됩니다.
로그가 사용되는 곳
로그는 구조나 의미가 변경될 때 남겨주는 것이 좋습니다. 기능에 따라서 로그를 남기게 되면 코드의 흐름에 따라 로그를 파악할 수 있습니다.
class UserController {
public void updateUser(User userInfo){
log.debug("UserController.updateUser, content = userInfo: {}", userInfo);
...
}
}
class UserService {
public void updateUserInfo(User userInfo)(
//하나의 메소드에도 진행되는 기능에 따라 로그 정보를 넣어준다.
log.debug("UserService.createUser, content = userInfo: {}", userInfo);
...
log.debug("UserService.updateUser, content = userInfo: {}", userInfo);
...
}
}
//UserController.updateUser, content = userInfo: userInfo
//UserService.createUser, content = userInfo: userInfo
//UserService.updateUser, content = userInfo: userInfo
- controller의 updateUser 실행 시, 중복되는 내용의 로그가 발생할 수 있지만 어떤 기능을 수행하고 있는 지 명확하게 확인 할 수 있습니다.
Try-Catch
오류가 발생할 수 있는 상황에 Try-Catch로 감싸는 경우가 종종 있습니다.
try{
method();
} catch (IllegalArgumentException e){
/* 에러 로그 작성 (필수) */
log.error("method 실행 중 IllegalArgument 예외 발생: {}", e.getMessage());
/* 예외 처리 */
...
} catch (Exception e){
...
}
Try-Catch는 예외가 발생 할 수 있는 상황을 컨트롤 할 수 있다는 장점을 가지고 있습니다. 그러나 디버깅 시, 가장 많은 문제를 안겨준 것도 Try-Catch였습니다.
1. 앞에서 발생한 예외 로그를 남기지 않고 throw를 하게 되면 어디서 발생했는 지 찾기 어려워지는 문제가 발생합니다.
2. 예외 또는 에러를 해결하기 위한 try-catch가 아닌 프로그램 실행만이 목적인 try-catch는 본래의 목적을 잃고 실행되어서는 안되는 코드를 실행시킵니다.
예외처리 사용에 대한 의견은 저마다 갈리지만, 사용 할 때에는 반드시 몇 가지 주의가 필요합니다.
* 주의해야 할 점
1. catch 시 반드시 로그를 작성해야 합니다. 정확한 로그 기록을 남기기가 어렵다면, try-catch를 사용하지 않는 것도 방법입니다.
2. 상위 메소드로 예외를 던지지 않아야 합니다. catch를 지나가게 될 수록 불필요한 log가 쌓이게 됩니다.
try{
method();
} catch(Exception e){ //1. 받은 에러를 상위 클래스로 넘기는 경우
log.error("메소드 실행 중 에러 발생: {} ", e.getMessage());
throw e;
} catch(Exception e){ //2. 예외를 매핑해서 던지는 경우
throw new MyException("MyException Error: ", e);
}
만약, 상위 메소드로 Exception을 보내야 한다면 MyException과 같이 추가적인 정보를 담긴 내용으로 매핑하여 던지는 것이 좋습니다.
3. e.printStackTrace() 출력을 주의해야 합니다.
- e.printStackTrace()는 에러의 발생 근원을 모두 찾아 출력하기 때문에 많은 오버헤드가 발생할 수 있습니다.
- System.err의 리소스 비용은 비싼 편입니다.
e.printStackTrace() 대체방법
- e.getMessage(), e.toString(): 에러의 마지막 메시지를 남기게 됩니다. 추적하기에는 적절하지 않습니다.
- e.getStackTrace()[0]: 현재 에러 위치를 확인 할 수 있습니다.
로그에 담겨야 할 정보들
로그 남기는 것이 중요하지만, 로그에 필요한 정보가 담겨있지 않다면 추적에 어려움을 겪을 수 있습니다.
좋은 로그는 '필요한 정보'가 있고, '의미'를 명확하게 가지고 있으며, 편리하게 '데이터'를 얻을 수 있습니다.
1. 날짜/시간
- 시간의 흐름에 따라 로그를 확인 할 수 있습니다.
- SpringBoot 실행 시, 시간과 같이 각 정보들을 찍어주고 있습니다.
2. 단서
- 빨리 찾기위한 단서를 남겨야 합니다. 가입자에 대한 정보를 보고 싶다면 가입ID와 같은 정보를 로그에 남기면, 가입ID를 기준으로 로그를 검색할 수 있습니다.
- 메타 데이터를 활용하여 문맥 식별자, 고유 식별자 등을 작성 할 수 있습니다. 해당 데이터들 역시 에러를 찾기위한 단서로 사용됩니다.
2-1. 문맥 식별자(Context identifier)
- 하나의 행동으로 일어난 여러 개의 로그가 같은 문맥 식별자를 갖도록 합니다.
- 인과관계 및 선후관계를 파악하는 데 도움이 됩니다.
ex) 신규 가입을 진행한다고 하였을 때, transaction_id: 123456을 가지고 있는 데이터를 검색하면 (가입 가능 여부 확인 -> 접수 -> 신규가입 테이블 저장 -> 신규가입 완료 푸쉬) 등과 같이 흐름을 확인 할 수 있습니다.
2-2. 고유 식별자(Unique identifier)
- 특정 로그를 확인하고 싶을 때 사용합니다.
ex) getFunctionalNumber1() 함수만 가질 수 있는 고유 식별자를 부여하여 검색하면 해당 함수의 내용을 쉽게 확인할 수 있습니다.
3. 데이터
- 현재 함수, 호출자 정보, 전달 받은 키 변수, 실행중인 주체 등 현재 실행중인 주체에 대한 정보를 남기는 것이 좋습니다.
public void createUser(String userId){
log.debug("userService.createUser, [CREATE USER SERVICE], userId={}, userInfo={} ", userId, userInfoDTO.toLog());
}
- 누가 무엇을 했는지에 대한 정보를 남깁니다.
log.error("데이터를 찾지 못했습니다.");
log.error("UserId: {}에 대한 UserInfo 정보를 찾지 못하였습니다.", userId);
4. 형식
- toString()과 같은 함수는 너무 많은 정보를 출력합니다. 클래스에 담긴 정보가 많다면 로그 용량이 커질 수 있습니다.
- 개향은 지양하고, 일관성 있는 단어를 사용하는 것이 좋습니다.
- 5단계의 로그 레벨에 따라 세분화하여 작성하면 On/Off로 필요한 정보만 제어할 수 있습니다.
로그에 관한 정보는 정말 다양합니다. 5단계에 따라 넣어야 하는 정보들, 전략적으로 로그를 남기는 법 등 많은 내용들이 있지만 로그에 담아야 하는 '메시지'에 집중하여 로그를 최대한 활용할 수 있었으면 좋겠습니다. :)
참고자료
'IT > 고찰' 카테고리의 다른 글
Git과 SVN 고찰 (0) | 2024.06.19 |
---|---|
Comment에 대한 고찰 (0) | 2023.08.15 |
Commit Message에 대한 고찰 (0) | 2023.08.02 |