# 로깅은 엉망이다

> Clean Markdown view of GeekNews topic #25239. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=25239](https://news.hada.io/topic?id=25239)
- GeekNews Markdown: [https://news.hada.io/topic/25239.md](https://news.hada.io/topic/25239.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-12-22T09:42:26+09:00
- Updated: 2025-12-22T09:42:26+09:00
- Original source: [loggingsucks.com](https://loggingsucks.com/)
- Points: 37
- Comments: 1

## Summary

현대 분산 시스템에서의 로깅은 여전히 **단일 서버 시대의 관성**에 머물러 있습니다. 서비스·DB·캐시를 거치는 요청의 컨텍트스가 분절되어, 문자열 검색만으로는 원인 추적이 거의 불가능합니다. 이를 해결하기 위해 제안하는 **Wide Event(또는 Canonical Log Line)** 방식은 요청 단위로 모든 컨텍스트를 담은 단일 이벤트를 기록해, 로그를 단순 텍스트가 아닌 **분석 가능한 데이터 자산**으로 전환합니다. 결과적으로 디버깅은 ‘grep 탐사’가 아니라, 쿼리 기반의 실시간 분석으로 진화하게 됩니다.

## Topic Body

- 현대 분산 시스템에서 **기존 로그 방식은 진실을 전달하지 못하는 구조적 한계**를 지님  
- 로그는 여전히 **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은 다음을 수행하지 않음  
  1. 무엇을 로깅할지 결정하지 않음  
  2. 비즈니스 맥락(예: 구독 등급, 장바구니 금액 등)을 자동으로 추가하지 않음  
  3. 개발자의 **로깅 사고방식**을 바꾸지 않음  
- 동일한 라이브러리를 사용해도, **맥락을 의도적으로 추가한 계측**과 단순 계측의 디버깅 경험은 극명히 다름  
- 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** 전략 제시  
  1. 오류(500 등)는 항상 저장  
  2. 느린 요청(p99 이상)은 항상 저장  
  3. VIP 사용자·특정 플래그 세션은 항상 저장  
  4. 나머지는 1~5%만 무작위 샘플링  
- 이를 통해 **비용 절감과 핵심 이벤트 보존**을 동시에 달성  
  
### 흔한 오해 정리  
- **Structured Logging ≠ Wide Event**: JSON 포맷만으로는 충분하지 않음, **맥락이 핵심**  
- **OpenTelemetry 사용 ≠ 완전한 관측성 확보**: 수집만 표준화할 뿐, **무엇을 기록할지는 개발자 몫**  
- **Tracing과 동일하지 않음**: 트레이싱은 서비스 간 흐름, Wide Event는 **서비스 내부 맥락** 제공  
- **로그는 디버깅용, 메트릭은 대시보드용**이라는 구분은 불필요함 — Wide Event는 두 용도 모두 충족  
- **고카디널리티 데이터는 비싸다**는 인식은 구식임, **ClickHouse·BigQuery 등 현대 DB는 이를 효율적으로 처리**  
  
### Wide Event 도입의 효과  
- 디버깅이 **탐사(archaeology)** 에서 **분석(analytics)** 으로 전환  
- “사용자 결제 실패”를 찾기 위해 50개 서비스 로그를 grep 하던 방식에서,  
  “프리미엄 사용자 결제 실패율을 오류 코드별로 조회”하는 **단일 쿼리 기반 분석**으로 변화  
- 결과적으로 로그가 **거짓을 말하던 도구에서, 진실을 말하는 데이터 자산**으로 전환됨

## Comments



### Comment 48100

- Author: neo
- Created: 2025-12-22T09:42:27+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=46346796) 
- 글이 읽기 어렵고 **AI가 도운 듯한 느낌**이 있었음. 그래도 메시지는 가치 있었고, 좀 더 간결했으면 좋았을 것 같음  
  최근 생각한 점은 다음과 같음.  
  - 스택 전반에 인증이 있으므로 모든 로그 라인에 **user id**를 포함하기 시작했음. 덕분에 사용자가 겪은 경험을 전체적으로 보기 쉬워졌음  
  - 에러를 요청 로그와 별도 라인으로 기록하는 건 번거로움. trace로 필터링할 수는 있지만 “5xx 요청과 관련된 에러만 보여줘” 같은 쿼리를 만들기 어려움  
  - 이런 컨텍스트를 추가하는 것만으로는 부족하고, 동료들에게도 새 필드가 생겼음을 **교육**해야 함. 몰라서 스스로 고생하는 경우를 많이 봤음
  - 더 나은 **tracing 도구**에 투자하면 단순 로그로는 불가능한 수준의 디버깅이 가능해짐. user id를 trace로 쓰는 개념을 확장한 셈임
  - 코드베이스에 **request ID** 개념이 있다면, 그걸로도 사용자의 행동을 더 구체적으로 추적할 수 있음
  - **TID**를 서비스 전반에 강제 적용하면, 어떤 팀이든 TID 하나로 전체 트랜잭션을 추적할 수 있음
  - 이런 “AI 냄새 난다”는 식의 댓글은 곧 **비판받는 문화**가 될 것 같음

- 이 주제라면 **Charity Majors**를 언급하지 않을 수 없음. 그녀는 “wide events”와 “observability” 개념을 10년 넘게 전파했고, [Honeycomb.io](https://www.honeycomb.io/)를 그 철학 위에 세웠음.  
  요즘은 다양한 도구로 이 방식을 구현할 수 있음. **structured logs**나 **traces**를 활용해 wide event를 캡처하고, 시계열·히스토그램 등 시각화가 풍부한 툴을 쓰는 게 중요함
  - 다만 “observability”라는 용어 자체를 그녀가 만든 건 아님. 이미 여러 분야에서 수십 년 전부터 쓰이던 개념임. 그녀는 영향력 있는 실무자이지만, 용어의 창시자는 아님  
  - 그녀의 블로그와 Honeycomb의 배경 이야기는 업계 종사자라면 꼭 읽어볼 만함. 이 접근법의 가치를 처음 인식한 팀이었음  
  - 글이 그녀의 스타일과 너무 닮아서, 마지막에 Honeycomb 광고가 나올 줄 알았는데 아니어서 놀랐음  
  - .NET 생태계에서는 **Nick Blumhardt**가 “structured logging”을 오래전부터 다뤘고, **Seq**와 **Serilog**가 이를 지원했음  
  - 그녀의 콘텐츠는 좋지만, “observability”를 브랜드화한 사람은 없음. 존중은 하되 과장된 주장은 피해야 함

- 글의 주장에 일부 동의하지만, **단일 wide event**만 남기는 방식에는 함정이 있음. 요청 중간에 예외나 타임아웃이 발생하면 아무것도 남지 않음.  
  언어의 기본 로깅 프레임워크나 의존성 로그도 놓치게 됨.  
  그래서 이건 기존 로그 위에 덧씌우는 **추가 레이어**로 쓰는 게 좋음. request/session 단위 ID를 두고 ClickHouse 같은 곳에서 집계하면 됨
  - 중간 계층의 가시성이 문제라면, 이벤트가 충분히 **wide하지 않은 것**임. log.error(data)나 wide_event.attach(error, data) 모두 본질은 같음  
  - “connection X:Y accepted at Z ns”와 “closed at Z ns” 같은 로그는 느린 시스템 디버깅에 매우 유용함  
  - 나는 PHP 프레임워크에서 **LoggerInterface**를 만들어 해결했음. 예외를 전역 핸들러로 잡아 DB에 wide 형태로 저장함. 약간의 보일러플레이트가 있지만 너무 잘 작동해서 이제는 없으면 불편함

- 프레젠테이션과 **인터랙티브 예시**가 훌륭했음. 하지만 결국 “로그에 구조화된 태그를 추가하라”는 이야기로 요약됨.  
  wide log는 복잡성과 **가독성 저하**에 비해 얻는 이득이 크지 않다고 느낌.  
  단순히 `grep "uid=user-123" application.log`로 충분한데, 굳이 사용자 배송 방법까지 붙일 필요가 있을까 싶음.  
  (참고로 Android Brave 브라우저에서는 체크박스가 작동하지 않았음)
  - JSON 로그라면 여전히 `grep '"uid": "user-123"'`로 검색 가능함. `--context` 옵션으로 주변 라인도 볼 수 있음

- 반도체 제조 환경에서 **수천 개의 메시지 버스 참가자**가 있는 시스템을 다뤘음. 시간당 300~400MB 로그가 나왔지만, **grep과 CLI 도구만으로도 충분히 관리**했음.  
  로그는 단순히 이벤트의 시계열일 뿐, 세부 분석은 Oracle 쿼리로 처리했음. 로그는 사건의 인과관계를 파악하는 데 쓰는 도구임
  - 로그는 **타임라인 파악용**이지, 요청·응답의 모든 데이터를 담는 용도가 아님. 너무 많은 정보를 넣으면 오히려 이해가 어려워짐.  
    로그는 “언제, 무엇이 일어났는가”를 말하고, “왜”는 코드와 데이터, 이벤트의 조합에서 찾는 것임  
    개인적으로 **ELK 스택** 같은 인터페이스는 직관적 탐색에 불편함. 로그는 직감적으로 따라가며 읽는 게 중요함  
  - 시간당 400MB 로그는 사실 많지 않음. 그래서 단순 grep으로도 충분히 처리 가능함

- 글 마지막의 “모든 에러·예외·느린 요청을 로그하라”는 조언은 **위험한 발상**임.  
  예를 들어 의존성이 느려지면 로그량이 100배로 폭증할 수 있음.  
  장애 상황에서는 서비스가 더 적은 일을 해야 복구가 쉬운데, 로그 폭증은 오히려 **연쇄 장애**를 유발함
  - Cloudflare에서는 **적응형 샘플링**을 씀. 로그 배치를 필드별로 버킷화하고, 각 버킷에서 입력 로그 수의 제곱근 혹은 로그값만 남김.  
    로그량이 많을수록 샘플링 비율이 자동으로 조정되어 시스템이 과부하되지 않음  
  - 이런 **매직 임계값**은 위험함. P(99) 같은 수치는 동적으로 갱신되어야 함. OTEL provider에서 주기적으로 실제 값을 가져오면 안전함  
  - 프로덕션 서비스는 로그 수집이 **수요에 맞게 확장**되도록 설계해야 함. 로컬 디스크 버퍼링만으로도 큰 도움이 됨  
  - 고트래픽 서비스라면 trace_id mod 100 == 0 같은 방식으로 **건강한 요청만 샘플링**하면 됨  
  - 로그가 병목이 된다면 시스템 설계가 잘못된 것임. 효율적인 로깅은 **초당 수억 건**도 처리 가능함

- 현대 소프트웨어에서는 단일 로그로 “무슨 일이 일어났는가”를 완전히 설명하기 어려움.  
  그래서 **수직적 상관관계(Vertical correlation)** 와 **수평적 상관관계(Horizontal correlation)** 가 필요함.  
  스택 내 상하 계층 간에는 동일한 correlation 값을 공유해야 하고, 시스템 간 통신 시에는 피어 간 correlation이 필요함.  
  이런 값을 API나 프로토콜에 추가하는 건 어렵지만, **트랜잭션 ID**를 미리 설계해두면 전체 추적이 가능함

- 단일 글을 위해 **도메인을 따로 등록**하는 건 지속성이 낮다고 생각함.  
  매년 갱신비를 내야 하므로, 개인 블로그나 **서브도메인**을 쓰는 게 낫다고 봄.  
  예를 들어 [logging-sucks.boristane.com](https://logging-sucks.boristane.com/) 같은 형태가 좋음
  - 사실 이 도메인과 글은 저자의 **observability SaaS 홍보용**임. Cloudflare 계정이 필요하지만 무료라서 장기적인 마케팅 전략 같음. 그래도 유익했고, 나도 CF 계정이 있어 써볼 생각임  
  - 이 글은 [Simon Willison의 “Give people something to link to”](https://simonwillison.net/2024/Jul/13/give-people-something-to-link-to/)와 비슷한 맥락임  
  - 이건 블로그 글이라기보다 **디지털 마케팅용 리드 생성 페이지**에 가까움. 서비스 홍보가 명확함

- “로그는 모놀리식 시대의 유물”이라는 주장에 대해, 나는 **로컬 로그는 여전히 유효**하다고 생각함.  
  로컬 프로세스의 대화를 기록하는 게 본래 역할이며, 다른 서버의 상황을 파악하려면 **트랜잭션 트레이싱**이 필요함.  
  적절한 위치의 로그만 봐도 근본 원인에 도달할 수 있음
  - 다만 로그는 단순한 원인 분석용이 아니라, **누가 영향을 받았는지**, **성능과 입력 간의 상관관계**, **보안 취약점의 영향** 등 비즈니스 인사이트를 주기도 함.  
    풍부한 컨텍스트가 있는 로그는 분석 엔진과 결합해 제품 개선에도 활용 가능함  
  - “요청이 15개 서비스와 3개 DB를 거친다”는 말에, 그런 복잡성을 지양해야 한다는 반응도 있었음  
  - 나는 **APN/Kibana**만으로도 로그 분석에 충분하다고 느낌

- “코드가 하는 일 대신, 요청에 무슨 일이 일어났는지를 로그하라”는 말에는 동의하지만, 저자가 경험이 부족해 보였음.  
  나는 이를 **“bug parts logging”** 이라 부르며, 처리 경로·횟수·시간 같은 **전조 신호**를 포함해야 한다고 생각함.  
  로깅은 메트릭이나 감사(audit)와 다름. 로깅이 실패해도 처리는 계속되어야 하지만, 감사 실패는 치명적임.  
  SCADA 시스템의 “historian” 개념처럼, **관찰(observables)** 과 **평가(evaluatives)** 를 구분해야 함.  
  예를 들어 연료 센서의 세밀한 이벤트는 진단에는 유용하지만, “목적지까지 갈 수 있는가”라는 질문에는 불필요함.  
  결국 중요한 건 **무엇을 관찰하고, 무엇을 평가할 것인가**를 명확히 하는 것임
  - 나는 “**관측성의 통합 이론**”을 지지함. 로그·메트릭·감사는 모두 비트 스트림일 뿐이며, 손실 없이 변환 가능함.  
    저장·변환·조회 방식은 달라도, **소비 지점과 메커니즘**은 동일하게 설계할 수 있음.  
    이렇게 하면 시스템 설계가 단순해지고, 장기 보관된 로그를 나중에 재처리할 수도 있음
