학습 목표
비구조화 로그와 구조화 로그의 차이를 설명할 수 있다.
JSON 로그의 필드 설계 원칙을 설명할 수 있다.
correlation ID로 한 요청을 여러 서비스 로그에서 추적할 수 있다.
로그 레벨·메시지·컨텍스트 필드를 구분할 수 있다.
문제 상황
- 장애 시
grep "error"하면 수천 줄- 어떤 사용자·어떤 주문인지 한 줄씩 열어봐야 한다
- API 게이트웨이 → 주문 → 결제 → 알림, 네 군데 로그 형식이 제각각
user=123vsuserId:123vsuid|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_casevscamelCase통일 (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
- 모든 로그 라인에 동일 필드 포함
- HTTP 헤더:
- trace_id와의 관계
- OpenTelemetry
trace_id가 있으면 로그에 함께 넣기 - 트레이스 백엔드 ↔ 로그를 한 클릭으로 연결 (11편)
- OpenTelemetry
4. 로그 레벨과 실무
| 레벨 | 용도 |
|---|---|
| DEBUG | 개발·상세 디버그 (프로덕션은 보통 off) |
| INFO | 정상 비즈니스 이벤트 (주문 생성, 배포 완료) |
| WARN | 복구 가능 이상 (재시도, deprecated API) |
| ERROR | 실패·예외 (결제 실패, DB 연결 끊김) |
- 프로덕션 기본: INFO
- ERROR는 알람 후보, DEBUG는 샘플링·동적 레벨
- 한 이벤트 한 줄 (가능하면)
- 여러 줄 stack은
error.stack필드로
- 여러 줄 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 |