최근 한 프로젝트에서 HTTP 요청/응답을 로깅을 구현하게 되어 해당 글을 쓰게 되었습니다!
✔️ 프로젝트에 로깅을 도입하게 된 계기
운영서버에서 개발자가 발견하지 못한 엣지케이스 같은게 있을 수도 있는데, 이러한 경우에 대한 대비가 전혀되고 있지 않았던 상황이라는 생각이 들었어요. 따라서 로깅 시스템을 구축하여, 실제 운영상황 및 QA시에 버그가 발생했을때, 추적을 용이하게 하기 위해서 도입을 결정하게 되었습니다!
이전에 다른 프로젝트 했을때도, 사실 로깅을 신경을 많이 쓰지 못했기에 에러가 나는 상황에서 불편함이 종종 있었어요. 예를 들어, 클라에서 에러난다고 하면 원인을 찾고 빠르게 대응하기가 어려웠다거나... 그래서 지금처럼 실제 운영되고 있는 서비스에서는 더더욱 있으면 좋겠다 싶었습니다!
그냥 요약하자면 빠르게 에러를 트래킹하여 좀 더 탄탄하고 안정적인 서버의 기반을 만들고 싶었습니다. ㅎㅎ
✔️ 로깅을 어느 시점에 할 것인지?!
공부를 하다보니 크게 로깅을 AOP, Filter, Interceptor 세 가지로 많이 구현하셨더라구요.
저는 그 중에서 Filter단에서 로깅을 하고자 했습니다!
- 이유: 스프링 AOP를 통해 구현할 수도 있지만, 이렇게 되면 http는 스프링 외부 영역에서 시작하기 때문에, filter를 거쳐 들어오는 과정에서 오류가 발생한다면 HTTP 요청 로그를 남기지 못하게 된다.
✔️ 요구사항
제가 이 정한 요구사항은 아래와 같았습니다.
🔆 요구사항)
- 모든 API 요청에 대한 요청과 응답을 기록하자
- 테스트 중 동작 확인 - 애플리케이션이 정상적으로 동작하는지 실시간 로그를 통해 확인하는 경우
- 에러 발생 시, 에러와 관련된 상세 로그를 남길 것
- 에러가 발생했을 때 - 에러 로그를 찾아서 원인을 분석하는 경우
✔️ Profile 별 로깅전략
해당 프로젝트는 실제 운영되고 있는 서비스이며, 다음과 같은 배포 파이프라인을 가져가고 있어요.
Local -> Staging -> Prod
Local은 개발자가 로컬 PC에서 자유롭게 개발을 하는 환경
Staging은 운영 환경과 거의 동일한 환경을 만들어 놓고, 운영 환경으로 이전하기 전에, 개발한 기능을 테스트 하는 환경. 저희 팀에서는 QA 용도로 많이 쓰이고 있습니다.
Prod는 실제 서비스가 배포되는 환경
위와 같은 환경으로 구분지을 수 있다보니, 로깅전략도 환경마다 다른 로깅 전략을 가져가고자 했습니다.
아래사진은 최종 ver 전까지 여러 방안을 고민한 흔적입니다..ㅎㅎ
☑️ Local 환경
- 무조건 콘솔에 로그찍히게
- 이유: 빠르게 문제 파악하고, 디버깅 및 테스트를 하는 게 우선시 되어야 한다고 생각
- 로그 레벨: INFO (처음에 DEBUG 모드로 했다가 너무 불필요한 데이터까지 보여져서,, 오히려 개발 생산성이 떨어진다고 생각하여 INFO 레벨로 조정했어요.)
☑️ Staging 환경
- 로그 저장 위치: 파일(RollingFileAppender)에 저장
- 파일 경로: /home/ubuntu/dev-logs/terning.log
- 로그 패턴:
- 날짜 시각 [스레드명] 로그레벨 로거명 [traceId] - 메시지
- ex) 2025-03-30 14:21:03 [main] INFO com.example.SomeClass [NoTraceID] - Hello log
- 참고로 traceId는 추적에 용이하도록 사용하고자 했으며, 하나의 요청/응답은 고유한 traceId를 가지게 됩니다.
- 로그 레벨: INFO
☑️ Prod 환경
- 로그 저장 위치: 파일(RollingFileAppender)에 저장
- 파일 경로: /home/ubuntu/prod-logs/terning.log
- 로그 패턴:
- 날짜 시각 [스레드명] 로그레벨 로거명 [traceId] - 메시지
- ex) 2025-03-30 14:21:03 [main] INFO com.example.SomeClass [NoTraceID] - Hello log
- 로그 레벨: INFO
Staging과 Prod는 실제로도 서로가 동일한 환경이기 때문에, 로깅 전략도 같게 가져갔고, 다만 파일 경로명만 조금 수정했습니다.
Staging과 Prod 환경에서는 파일에 저장하여 로그를 볼 수 있도록 했고, RollingFileAppender를 사용했는데 아래와 같이 동작하도록 설정했습니다!
🌀 Staging, Prod 환경에서의 롤링 동작 방식
- 로그 기록 파일
- 애플리케이션 실행 중에는 모든 로그가 terning.log 파일에 기록됩니다.
- 경로: /home/ubuntu/dev-logs/terning.log
- 롤링 조건
- 매일 자정이 지나면, 기존의 terning.log 파일은 날짜가 붙은 파일로 변경되고,
- ex) 4월 1일 -> 4월 2일이 되면 terning-2025-04-01.log 생성
도커 마운트 설정 (⚡️트러블 슈팅)
로깅 설정해주고 staging 서버에 접속했는데 눈을 씻고 봐도 로깅 파일이 없었어요,, 제 생각대로라면 yml 파일에 설정해준 것 처럼 /home/ubuntu/dev-logs 에 terning.log 파일이 생겨야 하는데..!
logging:
config: classpath:logback-dev.xml
location: /home/ubuntu/dev-logs
당연히 ec2 내에 생길 줄 알았지만,… 컨테이너 내부에 로그 파일이 적재되고 있었습니다..
컨테이너에 저장되는 데이터는 컨테이너가 삭제되면 사라지기 때문에, 영구적으로 볼 수 있도록 도커 bind mount 설정을 해주었습니다.
deploy.sh 파일의 아래 부분을 수정해주었다. (컨테이너와 호스트를 연결)
docker run -d --name ${server_name}-${green_port} -v /home/ubuntu:/home/ubuntu/dev-logs -p ${green_port}:8080 -e TZ=Asia/Seoul ${user_name}/${server_name}
컨테이너 삭제해도 log는 그대로 남아있는 거 확인까지 완료!
'백엔드 > SpringBoot' 카테고리의 다른 글
[페이징] offset pagination vs cursor pagination (0) | 2025.02.17 |
---|---|
스프링 AOP와 트랜잭션 (0) | 2025.02.12 |
멀티스레드 환경에서의 트랜잭션 동작 - feat. 테스트 코드 (0) | 2025.02.10 |
② 실전! 스프링 부트와 JPA 활용1 (1) | 2023.11.14 |
① 실전! 스프링 부트와 JPA 활용1 (2) | 2023.11.06 |