포스트

메트릭, 로그, 트레이스 - 무엇을 어디에 남기고 카디널리티는 어디서 터지는가

관측성의 세 기둥을 “메트릭, 로그, 트레이스”로 외우는 것은 쉽고, 어느 것에 무엇을 남길지 정하는 것은 어렵다. 세 가지는 취향이 아니라 비용 구조가 다른 저장소다. 그 차이를 알면 “이 값을 메트릭 라벨로 넣을까 로그 필드로 넣을까”에 답할 수 있다.

셋을 가르는 축은 카디널리티다

 답하는 질문비용이 커지는 축적합한 값
메트릭“얼마나 자주, 얼마나 오래”고유 시계열 수유한하고 작은 집합
로그“그때 정확히 무슨 일이”이벤트 수 × 크기임의의 값
트레이스“이 요청이 어디서 시간을 썼나”스팬 수 (샘플링으로 조절)요청 단위 식별자

메트릭의 비용은 고유 라벨 조합의 수에 비례한다. Prometheus는 라벨 조합 하나를 시계열 하나로 만들고, 각 시계열은 값이 변하지 않아도 메모리와 인덱스를 차지한다.

1
2
http_requests_total{method="GET", path="/api/orders", status="200"}   → 시계열 1개
http_requests_total{method="GET", path="/api/orders/12345"}           → 주문마다 1개

두 번째를 넣으면 주문 수만큼 시계열이 생긴다. 이것이 카디널리티 폭발이고, 관측 시스템이 죽는 가장 흔한 원인이다. 사용자 ID, 주문 번호, 이메일, 전체 URL 경로, 요청 본문은 메트릭 라벨에 넣지 않는다. 경로는 /api/orders/{id} 형태로 템플릿화해야 하고, Spring의 http_server_requests가 uri 라벨에 패턴을 쓰는 이유가 이것이다.

거꾸로, 임의의 값을 남겨야 한다면 그 자리는 로그나 트레이스 속성이다. 로그는 이벤트 하나에 필드를 몇 개 붙이든 시계열을 만들지 않는다.

셋의 연결이 실제 값을 만든다

따로 있으면 세 번 검색해야 하고, 연결되어 있으면 한 번에 좁혀진다.

  • 메트릭 → 트레이스: 에러율이 튄 시점의 예시 트레이스로 바로 간다. Prometheus의 exemplar가 이 연결이다.
  • 트레이스 → 로그: 스팬의 trace_id를 로그에도 남기면 그 요청의 로그만 모을 수 있다. MDC에 trace_id를 넣는 작업이 이것이다.
  • 로그 → 메트릭: 로그를 세어 메트릭을 만드는 것은 가능하지만 비싸다. 자주 세는 값이면 처음부터 메트릭으로 남긴다.

구조화 로그(JSON)가 전제다. 문자열을 정규식으로 파싱하는 파이프라인은 포맷이 바뀌는 날 조용히 멈춘다.

무엇을 남기지 않을 것인가

  • 개인정보와 비밀값. 로그와 트레이스 속성은 보존 기간이 길고 여러 시스템을 거친다. 전체 계좌번호, 토큰, 비밀번호는 남기지 않는다.
  • 성공 경로의 DEBUG. 부하가 오르면 로그가 디스크와 CPU를 먹는다. 동기 로깅이면 요청 지연에 직접 들어간다.
  • 같은 사실의 세 번 기록. 요청 하나에 메트릭, 로그, 트레이스가 모두 “성공했다”만 말하면 비용만 세 배다. 로그는 분기와 판단을 남기는 자리다.

이 설명이 깨지는 곳

  • 카디널리티 한계는 시스템마다 다르다. Prometheus는 라벨 조합에 민감하고, 로그 기반 시스템(Loki)은 라벨은 적게 내용은 자유롭게 두는 구조다. “높은 카디널리티를 지원한다”는 제품도 비용이 사라지는 것이 아니라 청구서로 옮겨간다.
  • 트레이스는 샘플링된다. 1%만 남긴다면 “이 요청”이 없을 가능성이 99%다. 드문 오류를 트레이스로 조사하려면 테일 샘플링이 필요하다.
  • 메트릭은 분포를 잃는다. 백분위를 사후에 재집계할 수 없다는 문제는 별도로 다룬다(백분위 통계).
  • 기술 지표만으로는 업무 실패를 못 본다. RED가 전부 정상인데 결제만 실패하는 상황이 있고, 그것은 업무 지표를 따로 세워야 보인다.

무엇을 재면 확인되는가

  1. prometheus_tsdb_head_series로 시계열 수를 보고, 라벨 하나를 추가하기 전후를 비교한다. 추정이 아니라 실제 증가분이 나온다.
  2. 동기 로깅으로 요청당 N줄을 남기며 부하를 걸고 p99를 본다. 로깅이 지연에 들어가는 지점이 있다.
  3. 샘플링 비율을 바꿔 가며 “특정 오류의 트레이스를 찾을 확률”을 센다.

2번은 spring-ops-lab의 S3 보조 엔드포인트(/api/slow-log)가 다룰 자리다. S1에서 배운 것도 같은 계열이다. server.tomcat.mbeanregistry.enabled가 꺼져 있으면 tomcat_threads_* 시계열이 아예 없고, 빈 그래프는 “문제 없음”처럼 보인다. 없는 지표는 0으로 보이지 않고 정상으로 보인다.

실무와의 접점

Datadog로 병목을 추적한 경험에서 지표가 가리킨 곳과 원인이 있던 곳이 달랐다. 메트릭은 “어디가 이상한가”까지 좁히고, 그 다음은 heap dump처럼 다른 도구가 필요했다. 세 기둥을 다 세워도 답이 나오지 않는 층이 있고, 그 층에는 dump와 프로파일러가 있다.

정리

  • 셋을 가르는 것은 카디널리티다. 메트릭은 고유 시계열 수에, 로그는 이벤트 수에 비용이 붙는다.
  • 사용자 ID, 주문 번호, 전체 경로는 메트릭 라벨이 아니라 로그 필드나 트레이스 속성이다.
  • 값은 연결에서 나온다. exemplar, MDC의 trace_id, 구조화 로그가 그 연결이다.
  • 로그는 분기와 판단을 남기는 자리다. 성공을 세 번 기록하면 비용만 세 배다.
  • 없는 지표는 0이 아니라 정상으로 보인다. 지표의 존재 자체를 점검 항목에 넣어야 한다.

참고

장애 대응과 관측
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

댓글

아직 댓글이 없습니다