관측성

구조화 로그

meellon 2026. 6. 19. 07:30

학습 목표

비구조화 로그와 구조화 로그의 차이를 설명할 수 있다.

JSON 로그의 필드 설계 원칙을 설명할 수 있다.

correlation ID로 한 요청을 여러 서비스 로그에서 추적할 수 있다.

로그 레벨·메시지·컨텍스트 필드를 구분할 수 있다.

문제 상황

  • 장애 시 grep "error" 하면 수천 줄
    • 어떤 사용자·어떤 주문인지 한 줄씩 열어봐야 한다
  • API 게이트웨이 → 주문 → 결제 → 알림, 네 군데 로그 형식이 제각각
    • user=123 vs userId:123 vs uid|123
  • "5초 걸린 요청"만 보고 싶은데
    • duration이 메시지 문자열 안에만 있고 필터·집계가 안 된다

오리엔테이션에서 로그·메트릭·트레이스 삼각형을 봤다. 구조화 로그는 검색·상관·집계 가능한 로그의 출발점이다.

1. 비구조화 vs 구조화

비구조화(Unstructured) 로그

2026-06-19 06:01:02 ERROR OrderService - payment failed for user 42 order 9001: timeout
  • 사람이 읽기엔 괜찮다
  • 파싱 규칙(grok)이 깨지기 쉽고, 필드 추가마다 정규식 수정

구조화(Structured) 로그

{
  "timestamp": "2026-06-19T06:01:02.123Z",
  "level": "ERROR",
  "service": "order-service",
  "message": "payment failed",
  "user_id": 42,
  "order_id": 9001,
  "error": "timeout",
  "duration_ms": 5123
}
  • 키-값 필드 — Loki·Elasticsearch·CloudWatch Logs Insights에서 order_id=9001 즉시 필터
  • 스키마가 안정적이면 대시보드·알람 자동화 가능

  비구조화 구조화 (JSON)
검색 텍스트 grep 필드 쿼리
집계 어려움 avg(duration_ms)
다중 서비스 파서마다 상이 공통 필드 규약
저장 압축은 유리할 수 있음 인덱스·필드 타입 설계 필요

2. 필드 설계

모든 것을 넣지 말고 질문에 답하는 필드를 고른다.

권장 공통 필드

필드 용도
timestamp ISO 8601, UTC 권장
level DEBUG, INFO, WARN, ERROR
service / service.name 어느 서비스인지
message 짧은 human-readable 요약
trace_id 분산 트레이스 연동 (있으면)
span_id 트레이스 내 span
correlation_id / request_id 한 HTTP 요청 단위 ID

도메인·이벤트 필드

  • 비즈니스 식별자: user_id, order_id, tenant_id
  • HTTP: method, path, status_code, duration_ms
  • 에러: error.type, error.message, stack (ERROR만, 민감 정보 주의)

설계 원칙

  • 고카디널리티 주의
    • user_id는 괜찮지만, UUID를 라벨/인덱스 키로 남발하면 저장·쿼리 비용 폭발 (메트릭 편에서 상세)
  • PII 분리·마스킹
    • 이메일·전화번호 전체를 로그에 넣지 않거나 해시
  • 메시지 vs 필드
    • 집계할 값은 필드로. 메시지는 설명용
  • 스키마 일관성
    • 팀·서비스 간 snake_case vs camelCase 통일 (OpenTelemetry semantic conventions 참고)

3. Correlation ID

엔드유저 요청이 여러 서비스를 거칠 때 같은 ID를 전파한다.

Client
  │  X-Request-ID: abc-123  (또는 게이트웨이가 생성)
  ▼
API Gateway     correlation_id=abc-123
  ▼
Order Service   correlation_id=abc-123
  ▼
Payment Service correlation_id=abc-123
  • 생성
    • API 게이트웨이·첫 서비스에서 UUID 생성
    • 없으면 각 hop마다 ID가 달라져 추적 불가
  • 전파
    • HTTP 헤더: X-Request-ID, X-Correlation-ID
    • 메시지 큐: message attribute / header
    • 모든 로그 라인에 동일 필드 포함
  • trace_id와의 관계
    • OpenTelemetry trace_id가 있으면 로그에 함께 넣기
    • 트레이스 백엔드 ↔ 로그를 한 클릭으로 연결 (11편)

4. 로그 레벨과 실무

레벨 용도
DEBUG 개발·상세 디버그 (프로덕션은 보통 off)
INFO 정상 비즈니스 이벤트 (주문 생성, 배포 완료)
WARN 복구 가능 이상 (재시도, deprecated API)
ERROR 실패·예외 (결제 실패, DB 연결 끊김)
  • 프로덕션 기본: INFO
    • ERROR는 알람 후보, DEBUG는 샘플링·동적 레벨
  • 한 이벤트 한 줄 (가능하면)
    • 여러 줄 stack은 error.stack 필드로
  • stdout으로 출력
    • 컨테이너·K8s는 파일이 아닌 stdout 수집이 표준 (수집 파이프라인은 다음 편)

다음에 다룰 것

  • 로그 수집 파이프라인
  • agent, aggregator, ELK/Loki 개요

해당 내용은 Observability Engineering (Charity Majors, Liz Fong-Jones, George Miranda) 의 내용을 기반으로 합니다.

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

Prometheus  (0) 2026.06.23
메트릭 기초  (0) 2026.06.22
로그로 장애 좁히기  (0) 2026.06.21
로그 수집 파이프라인  (0) 2026.06.20
관측성 오리엔테이션  (0) 2026.06.18