← 모든 글

logs, metrics, traces — 셋 다 안 될 때 무엇부터 깔 것인가

관찰 가능성 3종을 동시에 못 깔 때, 순서와 이유를 실경험으로 정리한 회고

셋 다 한 번에 못 한다. 리소스가 무한한 팀은 없고, ‘일단 다 넣자’는 계획은 보통 셋 다 어중간하게 끝난다.

막상 해보면 선택 순서가 장애 대응 속도를 갈라놓는다. 잘못 고르면 불이 났을 때 연기는 보이는데 어디서 났는지를 모른다.

logs 먼저 — 이건 협상 불가

첫 번째 장애는 항상 로그가 없을 때 터진다. 경험칙이 아니라 진짜로 그렇다.

  • 2026년 초 우리 팀 API 서버가 간헐적으로 5xx를 뱉었다. 에러율이 약 2%였는데, metrics는 붙어 있었고 로그는 stdout에 찍고 있었다. 문제는 stdout을 아무도 수집하지 않았다는 것. 숫자로는 “2%가 죽었다”만 보였고 왜 죽었는지는 컨테이너 재시작과 함께 사라졌다. 결국 같은 증상이 세 번 더 반복되고 나서야 로그 수집을 붙였고, 네 번째에야 원인을 잡았다. DB 커넥션 풀 고갈이었다.
  • 로그는 ‘무슨 일이 일어났나’의 원본이다. metrics가 신호를 주고 traces가 맥락을 주지만, 실제 에러 메시지와 스택트레이스는 로그에만 있다. 로그 없이는 나머지 두 개가 가리키는 곳에 도착해도 빈 방이다.
  • structured logging을 처음부터 강제하는 게 낫다. printf 스타일로 시작하면 나중에 필드 파싱 regex를 짜는 데 시간을 쓴다. 우리는 {"level": "error", "trace_id": "...", "msg": "..."} 를 첫날부터 강제했고, 나중에 traces를 붙일 때 trace_id 필드가 있어서 연결이 반나절 만에 됐다.
  • 보존 기간을 처음부터 정해놓지 않으면 비용이 터진다. 기본 30일로 뒀다가 S3 비용이 예상의 4배가 나왔다. 서비스별로 error는 90일, info는 7일로 계층화하니 안정됐다.

metrics 두 번째 — 경보의 신경망

로그가 ‘무엇’을 알려준다면 metrics는 ‘언제부터’와 ‘얼마나’를 말해준다. 로그만 있으면 장애를 발견하는 게 사람이고, metrics가 붙으면 알람이 먼저 발견한다.

  • 알람 없이 장애를 발견한 방법이 뭐였나 돌아보면 대부분 사용자 제보였다. 팀에 Slack 알람을 붙인 첫 주에 사용자 제보보다 알람이 먼저 울린 횟수가 4번이었다. 그중 2번은 새벽 2시였다. 로그만 있었으면 아침까지 몰랐을 사건이다.
  • 카디널리티 관리를 처음부터 해야 한다. user_id를 레이블로 붙였다가 Prometheus 메모리가 폭발한 적 있다. 시계열이 수십만 개 생기는 건 한 시간이면 충분하다. 레이블은 서비스명·엔드포인트·status_code 수준까지만 허용한다는 규칙을 코드 리뷰 체크리스트에 박아뒀다.
  • RED(Rate·Errors·Duration) 세 가지만 먼저 붙인다. 처음에 메트릭 20개를 한꺼번에 붙였는데 대시보드가 너무 넓어서 오히려 중요한 신호를 놓쳤다. 세 가지로 줄이고 나서 온콜 당번이 “이 세 개만 빨간 게 없으면 자도 된다”고 말하게 됐다.

traces는 세 번째 — 하지만 이유가 있다

traces를 마지막에 두는 건 중요하지 않아서가 아니다. 로그와 metrics 없이 traces를 먼저 붙이면 trace_id를 연결할 고리가 없어서 반쪽짜리가 된다.

  • MSA 이전에는 traces가 없어도 어느 서비스에서 터졌는지 바로 알았다. 서비스가 5개를 넘어가면서 달라졌다. 레이턴시가 1.2초에서 4.8초로 튀었는데, 로그 레벨에서는 모두 정상이었다. traces를 붙이고 나서 특정 서비스 간 호출에서 3초짜리 대기가 생기는 걸 15분 만에 찾았다. 그 서비스는 외부 API timeout 설정이 기본값인 30초 그대로였다.
  • 샘플링 전략을 미리 안 정하면 비용이 예측 불가다. head-based 100% 샘플링으로 시작했다가 트래픽이 늘면서 스토리지 비용이 주 단위로 튀었다. tail-based sampling으로 에러·고레이턴시 요청만 100%, 나머지는 1%로 바꾸니 비용이 70% 줄고 정작 필요한 trace는 다 남았다.
  • traces는 로그의 trace_id 필드가 있을 때 진가를 발휘한다. 로그에서 에러를 찾고 trace_id 하나로 전체 호출 경로를 펼치는 흐름이 완성되면, 장애 대응 시간이 체감상 절반으로 줄었다.

셋이 안 될 때 실제 결정 방법

이론은 알겠고 현실은 스프린트가 2주고 다른 피처 일정이 있다. 그럴 때 내가 쓰는 기준은 단순하다.

  • 지금 장애가 났을 때 원인을 찾는 데 얼마나 걸리냐. 30분 넘게 걸리면 로그부터다. 원인은 찾는데 언제 시작했는지·얼마나 퍼졌는지를 모르면 metrics다. 그 두 가지는 있는데 여러 서비스를 타고 가는 느린 요청이 문제면 traces다.
  • 하나씩 붙이는 것도 전략이다. 로그를 1스프린트에 제대로 붙이고, 다음 스프린트에 RED metrics와 알람, 그다음에 traces. 이 순서가 각 스프린트마다 ‘이번에 뭘 얻었나’가 명확하다. 세 개를 동시에 시작하면 세 개 다 절반쯤 된 채로 스프린트가 끝난다.

놓치기 쉬운 함정

다 붙인 뒤에도 자주 보는 실수가 있다.

  • 로그만 있고 알람이 없는 상태를 ‘관찰 가능성이 있다’고 착각한다. 로그는 수동 조회다. 사람이 Kibana를 열어야 보인다. metrics + 알람이 붙기 전까지는 모니터링이 아니라 기록이다.
  • 대시보드를 만들어두고 아무도 안 보는 문제. 우리 팀은 온콜 핸드오프 때 대시보드 링크 하나를 Slack에 붙여서 공유하는 걸 의식적으로 유지하고 있다. 보지 않으면 만든 의미가 없다.
  • traces 없이 레이턴시 문제를 로그로만 잡으려다 시간을 버렸던 경험이 있다. 로그에는 각 서비스 응답시간이 정상인데 전체 레이턴시는 높은 상황. 그 사이 어딘가에서 시간을 먹고 있었고, traces가 없었으면 못 찾았을 거다.

다음 한 가지

다음 분기에는 traces 샘플링 정책을 문서화해서 신규 서비스 온보딩 체크리스트에 박아넣는다 — 매번 구두로만 전달하고 끝내는 걸 이번엔 안 반복하기로.


🛒 이 글과 어울리는 추천 상품

위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.