관측성

트레이스와 로그·메트릭 연계

meellon 2026. 6. 28. 05:50

학습 목표

trace_id · span_id를 구조화 로그에 넣는 방법·필드명을 설명할 수 있다.

correlation_idtrace_id 역할 차이·병행 사용을 설명할 수 있다.

exemplar로 histogram 한 점에서 특정 trace로 점프하는 이유를 설명할 수 있다.

Grafana Tempo ↔ Loki derived field로 trace→log·log→trace 탐색을 구성할 수 있다.

alert → metric → exemplar/log → trace 조사 흐름을 설명할 수 있다.

문제 상황

  • p99 알람 — Grafana에서 집계만 보고 어느 요청인지 모름
    • histogram_quantile(0.99, …)숫자개별 slow request 없음
  • 로그에 trace_id는 있는데 Tempo에서 붙여넣기 검색 — 클릭 한 번이 아님
  • 트레이스 waterfall에서 Payment span이 빨간데 stack trace는 로그에만 있음
    • span attribute에 전체 stack 넣으면 비용·PII 폭발
  • trace_id를 Prometheus 라벨로 넣었다가 OOM (앞서 cardinality)
    • exemplar가 있는데 라벨로 우회
  • correlation_idtrace_id 둘 다 있는데 팀마다 다른 필드로 검색
    • on-call마다 쿼리 레시피 재발명

Part 3 마지막 — OTel·Tempo는 깔았다. 신호 세 개를 한 요청으로 묶는 운영 패턴.

1. 한 요청, 세 신호

신호 질문 연결 키
메트릭 얼마나 나쁜가 exemplar → trace_id
로그 무슨 이벤트·stack trace_id, span_id 필드
트레이스 어디서·얼마나 span → log derived field
조사 (양방향):

  Alert (5xx rate)
    → exemplar on slow histogram point → Tempo trace
    → span "Payment" → Logs for this span (Loki)
    → ERROR line + stack

  또는:

  Log ERROR (trace_id=7a3f…)
    → Explore Tempo by ID
    → waterfall → upstream span
    → metric panel same time range (검증)
  • 앞서 three signals — 이번 편은 도구로 연결하는 법
  • 샘플 안 된 trace도 로그는 남음 — ID로 상호 이동

2. 로그에 trace_id · span_id

OTel SDK·auto-instrumentation은 active span context를 제공한다. 모든 로그 라인에 주입.

{
  "timestamp": "2026-06-28T06:01:05.512Z",
  "level": "ERROR",
  "service": "payment-service",
  "message": "upstream timeout",
  "trace_id": "7a3f2c8b1d4e5f60718293a4b5c6d7e8",
  "span_id": "00f067aa0ba902b7",
  "correlation_id": "req-9f2a-8841",
  "duration_ms": 5012,
  "upstream": "stripe-api"
}
필드 형식 용도
trace_id 32 hex (W3C) Tempo/Jaeger 전 구간
span_id 16 hex 이 서비스 구간 좁히기
correlation_id UUID/string 레거시·비-OTel hop · 알람 메시지
  • 필드명 통일trace_id not traceId (팀 convention 하나)
  • 32자 hex — Tempo query·derived field regex와 맞춤
  • 앞서 구조화 로그 — trace_id권장 필드에 추가

주입 방법

스택 방법
Java Logback OpenTelemetry Logback appender / MDC trace_id
Python structlog contextvars + OTel get_current_span()
Go zap otelzap 또는 span context hook
Collector transform processor — 가능하지만 앱 주입이 일관
  • 로그에 ID 없으면 — propagation 누락·비-OTel 서비스 의심
  • ERROR 로그 alert payload에 trace_id 포함 — on-call 첫 클릭

전체 예시: code/log-with-trace.json

3. correlation_id vs trace_id

  correlation_id trace_id
범위 팀·API 관례 W3C 표준
생성 gateway 첫 서비스 OTel root span
전파 X-Request-ID traceparent
로그 검색
Tempo ❌ (직접 X)
exemplar
  • 둘 다 로그에 넣어도 됨 — gateway는 둘 다 생성·전파
  • 레거시는 correlation_id만 → OTel 도입 시 같은 요청에 trace_id 추가
  • 알람·Slack에는 trace_id 우선 (Grafana 링크용)

4. Exemplar — 메트릭에서 trace로

Exemplar — histogram (또는 counter) 샘플 하나trace_id(및 timestamp)를 붙임라벨이 아님 → cardinality 안전.

http_request_duration_seconds_bucket{le="2.5"}  ← 집계
  └── exemplar {trace_id="7a3f…", timestamp=…}  ← 이 버킷에 기여한 실제 요청 하나
  trace_id 라벨 exemplar
series 수 요청마다 폭발 변하지 않음
Grafana 불가 histogram 점 클릭 → Explore trace
전제 OTel SDK metrics exemplar + Prometheus exemplar storage

Prometheus 2.x — --enable-feature=exemplar-storage (버전별 확인).
OTel → Prometheus: exemplar 지원 remote write / scrape.

# exemplar가 붙은 histogram — Grafana Explore에서 "Query with exemplars"
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
  • p99 숫자만으로 부족할 때 — exemplar 점에서 대표 slow trace 열기
  • 앞서 cardinality — exemplar는 정석 우회

쿼리·설정: code/exemplar-query.promql

5. Grafana — trace ↔ log

Tempo datasource + Loki derived fields (또는 trace to logs 설정).

방향 설정 UX
Trace → Log Tempo: Loki datasource, trace_id → LogQL span 상세 "Logs for this span"
Log → Trace Loki: derived field regex on trace_id → Tempo 로그 줄 링크 클릭
# excerpt — code/grafana-tempo-loki.yaml
tracesToLogs:
  datasourceUid: loki
  filterByTraceID: true
  mapTagNamesEnabled: false
logsToTraces:
  datasourceUid: tempo
  tags: [{ key: trace_id }]

LogQL 예:

{service="payment-service"} | json | trace_id="7a3f2c8b1d4e5f60718293a4b5c6d7e8"
  • service 라벨 + trace_id 필드 — Loki 인덱스 설계 (앞서 label-first)
  • span_id까지 필터하면 해당 hop 로그만

6. 조사 플레이북

단계 행동 신호
1 알람 확인·시간창 metric
2 RED 패널·SLO metric
3 p99 histogram exemplar 클릭 metric → trace
4 slow/error span 선택 trace
5 Logs for span trace → log
6 root ERROR·stack log
7 같은 trace_id 다른 서비스 로그 log
8 배포·트래픽 같은 구간 metric 검증

역방향: 로그 ERROR → trace_id → Tempo → 어느 hop slow.

  • 앞서 로그 장애 좁히기 — trace_id 링크가 4편 playbook 업그레이드
  • postmortem — trace_id 하나로 증거 체인 첨부

7. 실무 체크리스트

항목 권장
로그 trace_id·span_id ERROR+WARN 최소, ideally 모든 레벨
필드명 trace_id snake_case 팀 표준
exemplar histogram RED duration + OTel metrics
Grafana Tempo↔Loki 한 대시보드에서 링크 테스트
alert notification에 trace_id·Explore URL
cardinality trace_id 라벨 금지 — exemplar·로그
legacy correlation_id 유지 + trace_id 병행
안티패턴  
trace_id metric label OOM (8편)
stack in span attribute 크기·PII — 로그
derived field regex 불일치 32 hex vs UUID 혼용
exemplar 없이 p99만 어느 요청인지 모름

8. 정리

  • 로그trace_id·span_id텍스트 증거·stack
  • exemplar — 집계 metric 한 점개별 trace (라벨 아님)
  • Grafana — Tempo↔Loki 양방향 — 클릭 조사
  • correlation_id — 레거시·사람용; trace_id — 도구 표준 키
  • Part 3 트레이스 마무리 — 다음 SLO·에러 버짓

다음에 다룰 것

  • SLO와 에러 버짓
  • SLI, Architecture 가용성

해당 내용은 Observability Engineering (Charity Majors, Liz Fong-Jones, George Miranda), Prometheus exemplars, Grafana Tempo traces-to-logs 문서를 기반으로 합니다.

'관측성' 카테고리의 다른 글

온콜과 인시던트  (0) 2026.06.30
SLO와 에러 버짓  (0) 2026.06.29
OpenTelemetry  (0) 2026.06.27
분산 트레이싱 개요  (0) 2026.06.26
메트릭 설계 안티패턴  (0) 2026.06.25