- 현대 분산 시스템에서 기존 로그 방식은 진실을 전달하지 못하는 구조적 한계를 지님
- 로그는 여전히 2005년식 단일 서버 환경을 전제로 설계되어, 다중 서비스·데이터베이스·캐시를 거치는 요청의 컨텍스트를 잃어버림
- 단순 문자열 검색은 구조·관계·상관성을 이해하지 못해, 문제 원인을 찾기 어렵게 만듦
- 해결책은 각 요청마다 모든 맥락을 담은 단일 ‘Wide Event(또는 Canonical Log Line)’ 을 남기는 것
- 이를 통해 로그가 단순 텍스트가 아닌 분석 가능한 데이터 자산으로 전환됨
로깅의 근본적 문제
- 기존 로그는 모놀리식 서버 시대를 전제로 만들어져, 현대의 분산 서비스 구조를 반영하지 못함
- 한 요청이 여러 서비스·DB·캐시·큐를 거치지만, 로그는 여전히 단일 서버 기준으로 기록됨
- 예시 로그에서는 한 요청당 13줄이 생성되어, 10,000명 동시 사용자 시 초당 130,000줄이 쏟아지지만 대부분 무의미한 정보임
- 문제 발생 시 필요한 것은 맥락(context) 이지만, 현재 로그에는 그것이 결여되어 있음
문자열 검색의 한계
- 사용자가 “결제가 안 된다”고 보고했을 때, 이메일이나 user_id로 로그를 검색해도 일관된 구조가 없어 유효한 결과를 얻기 어려움
- 동일한 사용자 ID가 user-123, user_id=user-123, {"userId":"user-123"} 등 수십 가지 형태로 기록됨
- 서비스 간 로그 포맷이 달라 연관 이벤트 추적이 불가능
- 핵심 문제는 로그가 쓰기(write) 중심으로 설계되어 있고, 조회(query) 에는 최적화되어 있지 않다는 점
핵심 개념 정의
-
Structured Logging: 문자열 대신 키-값(JSON) 형태로 기록하는 방식
-
Cardinality(카디널리티) : 필드의 고유값 개수, 예를 들어 user_id는 매우 높음
-
Dimensionality(차원 수) : 로그 이벤트 내 필드 개수, 많을수록 분석 가능성이 높음
-
Wide Event / Canonical Log Line: 요청당 하나의 맥락이 풍부한 단일 로그 이벤트
- 대부분의 로깅 시스템은 고카디널리티 데이터를 비용 문제로 제한하지만, 실제로는 그것이 디버깅에 가장 유용함
OpenTelemetry의 한계
- OpenTelemetry(OTel)는 프로토콜과 SDK 세트로, 데이터 수집·전송 표준만 제공
- 그러나 OTel은 다음을 수행하지 않음
- 무엇을 로깅할지 결정하지 않음
- 비즈니스 맥락(예: 구독 등급, 장바구니 금액 등)을 자동으로 추가하지 않음
- 개발자의 로깅 사고방식을 바꾸지 않음
- 동일한 라이브러리를 사용해도, 맥락을 의도적으로 추가한 계측과 단순 계측의 디버깅 경험은 극명히 다름
- OTel은 단순한 전달 수단(plumbing) 이며, 무엇을 흘려보낼지는 개발자가 결정해야 함
Wide Event / Canonical Log Line 방식
- 기존의 “코드가 무엇을 하는가” 중심 로깅에서 벗어나, “요청에 무슨 일이 일어났는가”를 기록해야 함
- 각 요청마다 서비스 단위로 하나의 광범위한 이벤트를 생성
- 요청·사용자·결제·오류·환경 등 50개 이상의 필드를 포함 가능
- 예시 JSON에는 user_id, subscription_tier, service_version, error_code 등 모든 디버깅 맥락이 포함
- 이를 통해 단일 검색으로 “프리미엄 사용자의 결제 실패 원인” 등 즉시 분석 가능
Wide Event의 쿼리 활용
- Wide Event는 단순 텍스트 검색이 아닌 구조화된 데이터 쿼리로 다룸
- 고카디널리티·고차원 데이터 기반으로 실시간 분석 수준의 디버깅 가능
- 예: “지난 1시간 동안 프리미엄 사용자의 결제 실패율을 오류 코드별로 집계” 같은 쿼리를 즉시 실행 가능
구현 패턴
- 요청 수명주기 전체에서 이벤트를 구성하고, 마지막에 한 번만 출력
- 미들웨어에서 request_id, timestamp, method, path 등 기본 필드 초기화
- 핸들러에서 사용자·장바구니·결제·오류 정보를 점진적으로 추가
- 최종적으로 logger.info(event)로 단일 JSON 이벤트를 기록
샘플링으로 비용 제어
- 요청당 50필드 이상을 기록하면 비용이 급증하므로 샘플링 필요
- 단순 무작위 샘플링은 오류를 놓칠 위험이 있음
-
Tail Sampling 전략 제시
- 오류(500 등)는 항상 저장
- 느린 요청(p99 이상)은 항상 저장
- VIP 사용자·특정 플래그 세션은 항상 저장
- 나머지는 1~5%만 무작위 샘플링
- 이를 통해 비용 절감과 핵심 이벤트 보존을 동시에 달성
흔한 오해 정리
-
Structured Logging ≠ Wide Event: JSON 포맷만으로는 충분하지 않음, 맥락이 핵심
-
OpenTelemetry 사용 ≠ 완전한 관측성 확보: 수집만 표준화할 뿐, 무엇을 기록할지는 개발자 몫
-
Tracing과 동일하지 않음: 트레이싱은 서비스 간 흐름, Wide Event는 서비스 내부 맥락 제공
-
로그는 디버깅용, 메트릭은 대시보드용이라는 구분은 불필요함 — Wide Event는 두 용도 모두 충족
-
고카디널리티 데이터는 비싸다는 인식은 구식임, ClickHouse·BigQuery 등 현대 DB는 이를 효율적으로 처리
Wide Event 도입의 효과
- 디버깅이 탐사(archaeology) 에서 분석(analytics) 으로 전환
- “사용자 결제 실패”를 찾기 위해 50개 서비스 로그를 grep 하던 방식에서,
“프리미엄 사용자 결제 실패율을 오류 코드별로 조회”하는 단일 쿼리 기반 분석으로 변화
- 결과적으로 로그가 거짓을 말하던 도구에서, 진실을 말하는 데이터 자산으로 전환됨