새소식

Welcome to the tech blog of Junior Backend Developer Seulbin Kang!

Backend

(BE 개발자 3개월차) 현업에서 고민한 로그 관리

  • -

👀 들어가기 전에

처음 회사를 입사하고 일 처리를 하나하나 해나갈 당시 우리 서비스에는 중앙 로그 수집이나 모니터링 체계가 전혀 없었다.

서버에서 문제가 발생하면 EC2에 직접 접속해 도커 컨테이너 로그를 하나하나 확인해야 했고, 그마저도 어떤 로그가 에러인지 어떤 로그가 단순 정보인지 구분하기 쉽지 않았다.

 

실제로 EC2 장애를 한 번 겪고 나서야(이전 포스팅 참고!) 이 문제가 얼마나 치명적인지를 직접적으로 체감하게 되었다.

장애 상황임에도 불구하고 지금 무슨 일이 벌어졌는지를 한눈에 파악할 수 없는 상태였기 때문이다.

이 경험을 계기로 고민했다.

 

 

결국 로그 정책 수립부터 수집 / 시각화 환경 구성까지 전부 직접 제안하고 구축하게 되었고 이 글은 그 과정에서의 고민과 선택들을 정리한 기록이다.


👀 본론

로그란 무엇인가

Loki 대시보드

 

우선 로그에 대해서 근본적인 것부터 이야기를 먼저 하겠다.

로그는 시스템에서 발생한 성공, 실패, 실행과정, 에러 예외를 시간순으로 기록한 흔적이다.

운영 중인 서비스에서 로그는 곧 시스템의 상태를 가장 정확하게 설명해주는 단서가 되는 것이다.


스프링 로그만으로 충분하지 않을까?

 

처음 제안 당시 들었던 질문이다. 하나하나 이유를 들어 말씀드리며 설득했던 기억이 새록새록하다.

스프링 부트는 기본적으로 예외 발생 시 스택 트레이스를 포함한 로그를 찍어준다.

하지만 실제 운영 환경에서는 한계가 명확하다.

 

예를 들어, 아래와 같은 로그만 남는 상황을 생각해보자.

org.springframework.web.server.ResponseStatusException: 500 INTERNAL_SERVER_ERROR
  • 어떤 요청에서 발생한 문제인지
  • 어떤 비즈니스 흐름에서 실패했는지
  • 사용자 입력값이나 상태는 무엇이었는지

이 정보들은 스프링 기본 로그만으로는 알 수 없다.

 

또한 개인정보를 다루는 서비스나 금융 도메인에서는 법적/정책적으로 반드시 남겨야 하는 로그도 존재한다.

이런 요구사항은 프레임워크가 자동으로 해결해주지 않는다.

결국 어떤 정보를 어떤 기준으로 남길것인가는 직접 정의해야하는 문제인 것이다.


어떤 로그가 잘 남긴 로그일까?

좋은 로그

로그 정책을 수립해가며 고민한 부분이다.

내가 정한 기준은 단순했다.

  1. 트러블 슈팅에 도움이 되는가
  2. 사후 분석 시 원인 추적이 가능한가
  3. 법적/운영적으로 반드시 필요한 정보인가

이 기준에 맞춰 로그를 남기기 위해 예외 처리 전략부터 정리했다.

  • Unchecked Exception 기반의 커스텀 예외를 적극 사용
  • @ControllerAdvice에서 공통 매핑
  • 메서드 시그니처에 불필요한 throws 제거

Checked Exception은 개발자가 즉시 합리적으로 처리할 수 있는 대안이 있을 때라는 명확한 기준이 있는 경우에만 사용하도록 했다.


로그 레벨 기준 정립

로그가 많다고 좋은 것이 아니다. 의미 없이 쌓인 로그는 오히려 장애 대응을 방해한다고 판단했다.

그리하여 로그 레벨별로 아래와 같은 기준을 만들었다.

  • DEBUG
    로컬/개발 환경에서 디버깅 목적
    로직 흐름, 내부 상태 확인용
  • INFO
    Request 유입
    Response 반환
    주요 비즈니스 이벤트 발생
  • WARN
    즉각적인 장애는 아니지만 향후 문제로 이어질 수 있는 상황
    ex) 외부 API 지연, 예상 못한 입력값 등등
  • ERROR
    요청 실패
    트랜잭션 롤백
    반드시 추적이 필요한 장애 상황

많이 쓰이는 로그 레벨을 참고해서 공부하여 만들어 놓았다.


로그 포맷 표준화

실제 로그들

모든 로그는 아래 정보를 공통으로 포함하도록 했다.

  • 시간
  • 로그레벨
  • 스레드명
  • 패키지.클래스
  • 메시지

이 포맷 덕분에 멀티스레드 환경에서도 흐름 추적이 쉬워졌고 로그 필터링과 검색이 훨씬 수월해졌다.


Logback 설정과 수집 구조

이 기준을 바탕으로 Logback 설정 파일을 직접 작성했다.

고민했던 점은 크게 세가지 였다.

  1. 로그를 어떤 단위로 롤링할 것인가
  2. 로그 레벨별로 어떻게 분리할 것인가
  3. 운영 환경별로 어떻게 관리할 것인가

로그를 어떤 단위로 롤링할 것이며 얼마나 보관할 것인가

운영환경에서 로그는 반드시 쌓인다.

그렇기에 로그를 어디까지 보관하고 언제 삭제할 것인가를 정하지 않으면 로그 파일이 곧 장애의 원인이 될 수 있다.

 

그렇기에 시간과 파일 크기 기준으로 로그를 롤링하는 방식을 선택했다.

  • 장애가 발생했을때, 발생 날짜를 기준으로 로그 범위를 빠르게 좁힐 수 있어야 한다고 판단했다.
    그렇기에 하루치 로그가 하나의 단위로 관리되도록 날짜 기준 롤링을 기본으로 설정했다.
  • 다만 특정 시간대 트래픽 급증이나 예상치 못한 로그 폭주가 발생했을 때, 단일 로그 파일이 과도하게 커지는 상황은 반드시 방지해야 한다.
    이를 위해 파일 단위 최대 크기를 두었고 로그파일은 롤링 시점에 Gzip으로 자동 압축되도록 하였다.

 


로그 어떻게 분리할 것인가

로그는 필요한 순간에 바로 찾을 수 있어야 의미가 있다.

그래서 로그레벨 별로 아래와 같은 두가지 기준으로 나누었다.

  • 일반적인 INFO레벨 이상의 로그 : 요청흐름, 정상 처리, 잠재적 문제 상황까지 포함해 전체적인 서비스 흐름 파악
  • ERROR 로그 : 장애 상황이 발생 여부 확인, 불필요한 다른 로그 없이 에러만 파악, 장애 원인 분석에 집중

이후 운영 환경별로도 로그를 분리했다.

운영 로그와 개발 로그는 절대 섞이면 안된다.

운영 서버에서 에러났는데 개발 로그에서 원인을 찾는 경우를 사전에 방지해야헀다.

이를 위해 로그 파일명에 Spring Active Profile을 직접 반영했다.

이렇게 개발 환경로그와 운영 환경로그가 물리적으로 완전히 분리되었고 혼선 없이 로그를 관리할 수 있었다.


로그 보관 정책

일반적인 INFO 로그의 경우 날짜 기준 보관 기간을 30일로 설정했다.

다만 트래픽 폭증과 로그 폭주로 용량이 커질 때를 고려해 전체 로그 용량의 상한을 5GB로 잡았고 30일이 안되었더라도 오래된 로그부터 삭제하도록 구성했다.

 

ERROR 전용 로그의 경우도 최대한 30일로 잡았지만 장애 로그만 사용을 하기에 전체 용량은 1GB로 잡았다.


👀 마치며

이번 경험을 통해 로그의 소중함을 다시금 느끼게 되었다.

로그를 어떻게 남기느냐에 따라 장애 대응 속도도, 서비스 신뢰도도 완전히 달라진다.

 

실제로 로그 체계 구축 이후 프론트엔드 개발 매니저님으로 부터 장애 관련 문의가 들어왔을 때

로그만으로 원인을 바로 파악해 빠르게 대응할 수 있었고 로그의 가치를 다시금 실감할 수 있었다.

꽤나,,,뿌듯했다 😎

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.