36P by GN⁺ 18일전 | ★ favorite | 댓글 1개
  • 현대 분산 시스템에서 기존 로그 방식은 진실을 전달하지 못하는 구조적 한계를 지님
  • 로그는 여전히 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 하던 방식에서,
    “프리미엄 사용자 결제 실패율을 오류 코드별로 조회”하는 단일 쿼리 기반 분석으로 변화
  • 결과적으로 로그가 거짓을 말하던 도구에서, 진실을 말하는 데이터 자산으로 전환됨
Hacker News 의견들
  • 글이 읽기 어렵고 AI가 도운 듯한 느낌이 있었음. 그래도 메시지는 가치 있었고, 좀 더 간결했으면 좋았을 것 같음
    최근 생각한 점은 다음과 같음.

    • 스택 전반에 인증이 있으므로 모든 로그 라인에 user id를 포함하기 시작했음. 덕분에 사용자가 겪은 경험을 전체적으로 보기 쉬워졌음
    • 에러를 요청 로그와 별도 라인으로 기록하는 건 번거로움. trace로 필터링할 수는 있지만 “5xx 요청과 관련된 에러만 보여줘” 같은 쿼리를 만들기 어려움
    • 이런 컨텍스트를 추가하는 것만으로는 부족하고, 동료들에게도 새 필드가 생겼음을 교육해야 함. 몰라서 스스로 고생하는 경우를 많이 봤음
    • 더 나은 tracing 도구에 투자하면 단순 로그로는 불가능한 수준의 디버깅이 가능해짐. user id를 trace로 쓰는 개념을 확장한 셈임
    • 코드베이스에 request ID 개념이 있다면, 그걸로도 사용자의 행동을 더 구체적으로 추적할 수 있음
    • TID를 서비스 전반에 강제 적용하면, 어떤 팀이든 TID 하나로 전체 트랜잭션을 추적할 수 있음
    • 이런 “AI 냄새 난다”는 식의 댓글은 곧 비판받는 문화가 될 것 같음
  • 이 주제라면 Charity Majors를 언급하지 않을 수 없음. 그녀는 “wide events”와 “observability” 개념을 10년 넘게 전파했고, Honeycomb.io를 그 철학 위에 세웠음.
    요즘은 다양한 도구로 이 방식을 구현할 수 있음. structured logstraces를 활용해 wide event를 캡처하고, 시계열·히스토그램 등 시각화가 풍부한 툴을 쓰는 게 중요함

    • 다만 “observability”라는 용어 자체를 그녀가 만든 건 아님. 이미 여러 분야에서 수십 년 전부터 쓰이던 개념임. 그녀는 영향력 있는 실무자이지만, 용어의 창시자는 아님
    • 그녀의 블로그와 Honeycomb의 배경 이야기는 업계 종사자라면 꼭 읽어볼 만함. 이 접근법의 가치를 처음 인식한 팀이었음
    • 글이 그녀의 스타일과 너무 닮아서, 마지막에 Honeycomb 광고가 나올 줄 알았는데 아니어서 놀랐음
    • .NET 생태계에서는 Nick Blumhardt가 “structured logging”을 오래전부터 다뤘고, SeqSerilog가 이를 지원했음
    • 그녀의 콘텐츠는 좋지만, “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 같은 형태가 좋음

    • 사실 이 도메인과 글은 저자의 observability SaaS 홍보용임. Cloudflare 계정이 필요하지만 무료라서 장기적인 마케팅 전략 같음. 그래도 유익했고, 나도 CF 계정이 있어 써볼 생각임
    • 이 글은 Simon Willison의 “Give people something to link to”와 비슷한 맥락임
    • 이건 블로그 글이라기보다 디지털 마케팅용 리드 생성 페이지에 가까움. 서비스 홍보가 명확함
  • “로그는 모놀리식 시대의 유물”이라는 주장에 대해, 나는 로컬 로그는 여전히 유효하다고 생각함.
    로컬 프로세스의 대화를 기록하는 게 본래 역할이며, 다른 서버의 상황을 파악하려면 트랜잭션 트레이싱이 필요함.
    적절한 위치의 로그만 봐도 근본 원인에 도달할 수 있음

    • 다만 로그는 단순한 원인 분석용이 아니라, 누가 영향을 받았는지, 성능과 입력 간의 상관관계, 보안 취약점의 영향 등 비즈니스 인사이트를 주기도 함.
      풍부한 컨텍스트가 있는 로그는 분석 엔진과 결합해 제품 개선에도 활용 가능함
    • “요청이 15개 서비스와 3개 DB를 거친다”는 말에, 그런 복잡성을 지양해야 한다는 반응도 있었음
    • 나는 APN/Kibana만으로도 로그 분석에 충분하다고 느낌
  • “코드가 하는 일 대신, 요청에 무슨 일이 일어났는지를 로그하라”는 말에는 동의하지만, 저자가 경험이 부족해 보였음.
    나는 이를 “bug parts logging” 이라 부르며, 처리 경로·횟수·시간 같은 전조 신호를 포함해야 한다고 생각함.
    로깅은 메트릭이나 감사(audit)와 다름. 로깅이 실패해도 처리는 계속되어야 하지만, 감사 실패는 치명적임.
    SCADA 시스템의 “historian” 개념처럼, 관찰(observables)평가(evaluatives) 를 구분해야 함.
    예를 들어 연료 센서의 세밀한 이벤트는 진단에는 유용하지만, “목적지까지 갈 수 있는가”라는 질문에는 불필요함.
    결국 중요한 건 무엇을 관찰하고, 무엇을 평가할 것인가를 명확히 하는 것임

    • 나는 “관측성의 통합 이론”을 지지함. 로그·메트릭·감사는 모두 비트 스트림일 뿐이며, 손실 없이 변환 가능함.
      저장·변환·조회 방식은 달라도, 소비 지점과 메커니즘은 동일하게 설계할 수 있음.
      이렇게 하면 시스템 설계가 단순해지고, 장기 보관된 로그를 나중에 재처리할 수도 있음