4-3. 구조화 로그와 request_id¶
구조화 로그는 운영 중 품질 문제를 요청 단위로 다시 확인하기 위한 근거입니다. 핵심은 어떤 로그 필드(field)가 있어야 request_id로 개별 요청을 찾고, trace_id로 관련 흐름을 넓혀 볼 수 있는지 판단하는 것입니다.
이 문서를 읽을 때는 다음 기준을 중심으로 확인합니다.
- 구조화 로그: 문자열이 아니라 JSON 필드로 남긴 로그의 필요성
- 추적 키:
request_id와trace_id가 각각 풀어 주는 문제 - 품질 필드:
model_version,score,threshold,prediction,validation_failure의 역할 - 원인 추적: 대시보드 이상 신호에서 대표 요청 로그로 내려가는 흐름
4-3-1. 구조화 로그가 필요한 이유¶
구조화 로그는 사람이 읽는 문장보다 검색과 집계가 쉬운 로그입니다. AI 서비스에서는 “요청이 성공했다”는 문장보다 request_id, model_version, score, prediction 같은 필드가 따로 남아 있어야 나중에 원인을 찾을 수 있습니다.
일반 문자열 로그도 장애 상황을 사람이 읽는 데는 도움이 됩니다. 하지만 prediction result is high_risk처럼 한 문장으로 남기면 prediction="high_risk" 조건으로 정확히 필터링하기 어렵고, 점수(score) 평균이나 검증 실패(validation failure) 건수를 안정적으로 집계하기도 어렵습니다.
Grafana Loki Log queries는 로그 파이프라인에서 JSON을 파싱(parsing)해 필드를 추출하고 필터링하는 방식을 설명합니다. 이 문서에서는 LogQL 문법을 깊게 배우지 않고, 나중에 그런 조회가 가능하려면 로그가 처음부터 필드 단위로 남아 있어야 한다는 점만 확인합니다.
아래 비교는 같은 예측 결과를 두 가지 방식으로 남겼을 때 QA가 어떤 차이를 겪는지 보여줍니다.
| 로그 방식 | 장점 | 한계 |
|---|---|---|
| 문자열 로그 | 작성이 쉽고 사람이 바로 읽기 쉬움 | request_id, score, prediction을 안정적으로 필터링하거나 집계하기 어려움 |
| 구조화 로그 | 필드 검색, 집계, 대시보드(dashboard) 연결에 유리 | 어떤 필드를 남길지 미리 설계해야 함 |
QA는 구조화 로그 필드를 테스트 기준으로 봐야 합니다. API가 정상 응답을 반환해도 로그에 request_id, model_version, threshold가 없다면 운영 중 품질 문제가 발생했을 때 “어떤 요청에서 어떤 기준으로 판단했는가”를 다시 설명하기 어렵습니다.
4-3-2. request_id, trace_id, 모델 버전, 점수 기록¶
구조화 로그는 이벤트(event) 단위로 설계합니다. 여기서 이벤트는 “한 번의 예측 요청이 처리되면서 남긴 기록”입니다. 아래 도메인 객체(domain object)는 실습에서 사용하는 예측 이벤트(prediction event)의 필드를 보여줍니다. 전체 코드는 packages/ai-quality/src/ai_quality/observability/domain/prediction_event.py에 있습니다.
@dataclass(frozen=True)
class PredictionEvent:
"""Structured log event for one prediction request."""
timestamp: str
request_id: str
trace_id: str
model_version: str
score: float
threshold: float
prediction: str
latency_ms: float
status_code: int
validation_failure: bool
client_id: str | None = None
source_system: str | None = None
failed_field: str | None = None
error_category: str | None = None
error_detail: str | None = None
owner: str | None = None
request_id는 요청(request) 하나를 다시 찾기 위한 식별자입니다. 대시보드에서 이상 시점을 찾은 뒤 대표 요청 하나를 확인할 때 사용합니다. trace_id는 여러 이벤트를 하나의 처리 흐름으로 묶기 위한 식별자입니다. 실제 서비스에서는 API 검증(validation), 모델 추론(model inference), 로그 전송 같은 단계가 여러 로그로 나뉠 수 있으므로 trace_id가 있으면 같은 흐름의 기록을 함께 볼 수 있습니다.
OpenTelemetry Logs Data Model은 로그 레코드(log record)에 시간, 추적 컨텍스트(trace context), 본문(body), 속성(attribute) 같은 정보를 담아 이벤트를 설명하는 관점을 제공합니다. 실습에서는 이 구조를 그대로 구현하지는 않지만, trace_id와 요청별 속성(attribute)을 남겨 품질 문제를 추적한다는 관점을 가져옵니다.
model_version, score, threshold, prediction은 AI 서비스 품질 관측의 핵심입니다. API 성공 여부만 기록하면 모델 품질 변화는 추적할 수 없습니다. 모델 버전(model_version)이 바뀐 시점, 점수 분포가 바뀐 시점, 임계값(threshold)이 바뀐 시점을 함께 볼 수 있어야 합니다.
로그 필드의 품질 가치는 어떤 질문에 답할 수 있는지로 판단합니다. 아래 표는 각 필드가 어떤 질문에 답하는지 보여줍니다.
| 필드 | 답할 수 있는 질문 | 없을 때의 문제 |
|---|---|---|
request_id |
어떤 요청에서 문제가 발생했는가 | 특정 요청을 다시 찾기 어려움 |
trace_id |
같은 처리 흐름에 어떤 로그가 묶이는가 | 여러 처리 단계를 연결하기 어려움 |
model_version |
어떤 모델이 응답했는가 | 배포 변경과 품질 변화를 연결하기 어려움 |
score |
임계값 적용 전 모델 출력은 얼마였는가 | 예측(prediction) 변화 원인을 좁히기 어려움 |
threshold |
어떤 기준으로 class가 결정되었는가 | 설정 변경과 모델 판단 변화를 구분하기 어려움 |
prediction |
최종 예측 결과는 무엇인가 | 예측 분포(prediction distribution)를 볼 수 없음 |
client_id |
어떤 client 요청에서 문제가 반복되는가 | 후속 확인 담당을 좁히기 어려움 |
source_system |
어떤 상위 시스템에서 들어온 요청인가 | upstream 변경 가능성을 확인하기 어려움 |
failed_field |
어떤 입력 필드가 검증에 실패했는가 | API schema와 payload 문제를 구체화하기 어려움 |
owner |
누구에게 다음 확인을 넘길 것인가 | 조치가 “확인 필요” 수준에서 멈춤 |
이 표의 목적은 필드 이름을 외우는 것이 아닙니다. 운영 중 품질 이상이 보였을 때 어떤 질문에 답해야 하는지 먼저 정하고, 그 질문에 필요한 필드가 로그에 남아 있는지 확인하는 것입니다.
4-3-3. 예측과 검증 실패 기록¶
예측은 모델 점수에 임계값을 적용한 최종 class입니다. 검증 실패는 요청 입력이 API 계약(contract)을 만족하지 못했다는 신호입니다. 두 필드를 함께 기록해야 입력 문제와 모델 응답 변화를 분리할 수 있습니다.
검증 실패 요청은 정상 예측 결과와 같은 집합으로 해석하면 안 됩니다. 예를 들어 validation_failure=true인 요청은 필수 필드가 누락되었거나 타입이 맞지 않아 예측 로직까지 가지 못했을 수 있습니다. 반대로 validation_failure=false인데 high_risk 비율이 증가했다면 입력 값의 분포, 모델 버전, 임계값을 함께 봐야 합니다.
운영 로그 조합은 원인을 확정하기보다 먼저 볼 후보를 정하는 데 사용합니다. 아래 표는 운영 로그에서 자주 만나는 관측 조합을 원인 후보로 바꾸는 방식입니다.
| 관측 조합 | 먼저 볼 원인 후보 | QA 해석 |
|---|---|---|
validation_failure 증가, 예측 변화 없음 |
입력 스키마(schema), 클라이언트 페이로드(client payload) 변경 | 모델 지표(metric)보다 입력 계약 위반을 먼저 확인 |
validation_failure 정상, 예측 분포 변화 |
입력 분포 변화, 임계값 변경, 모델 버전 변경 | 모델 자체 문제로 단정하지 않고 설정과 입력을 함께 확인 |
| 오류(error)와 지연 시간(latency)이 함께 증가 | API 처리 실패, 운영 부하, 외부 의존성 지연 | 품질 지표 해석 전에 서비스 상태를 먼저 확인 |
검증 실패가 발생한 요청은 정상 예측 분석과 분리해야 합니다. 잘못된 입력이 예측까지 가지 않았다면 그 요청을 점수 분포(score distribution)에 포함하는 것은 부적절합니다. 반대로 검증 실패는 없지만 예측 분포가 바뀌었다면 입력 값의 분포 변화나 모델/임계값 변경을 봐야 합니다.
QA 보고서에서는 검증 실패를 단순 오류 수로만 기록하지 말고, 어떤 필드에서 실패했는지와 언제부터 증가했는지를 함께 정리하는 것이 좋습니다. 실습 artifact는 client_id, source_system, failed_field, error_detail, owner를 함께 남겨 검증 실패 조치가 API owner인지, client integration owner인지, 운영 owner인지 구분할 수 있게 합니다.
4-3-4. 로그 기반 원인 추적 흐름¶
다음 명령으로 정상/이상 로그를 생성합니다. 특정 request_id 조회는 labs/ch04_observability/observability_lab.ipynb의 조회 셀에서 확인합니다.
uv run --group lab python labs/ch04_observability/build_observability_artifacts.py
이 명령은 정상 상태와 이상 상태의 JSONL 로그를 생성합니다. Notebook 조회 셀은 request_id=anomaly-0004인 요청을 찾아 출력합니다. 예상 출력은 다음과 같은 형태입니다.
request_id=anomaly-0004
{
"timestamp": "2026-01-01T09:00:20+00:00",
"request_id": "anomaly-0004",
"trace_id": "anomaly-trace-0001",
"model_version": "v1",
"score": 0.67,
"threshold": 0.5,
"prediction": "high_risk",
"latency_ms": 230.0,
"status_code": 200,
"validation_failure": false,
"client_id": "mobile-checkin-v2",
"source_system": "mobile-checkin",
"failed_field": null,
"error_category": null,
"error_detail": null,
"owner": null
}
이 결과는 “어떤 요청이 어떤 모델 버전과 임계값으로 처리되었는가”를 확인하는 최소 단위입니다. 원본 파일은 필드를 가진 JSONL 구조이고, 실습 스크립트는 수강생이 읽기 쉽도록 해당 이벤트를 펼쳐 보여줍니다. QA는 request_id를 기준으로 대표 요청 하나를 확인하고, trace_id를 기준으로 같은 흐름의 로그를 넓혀 볼 수 있어야 합니다.
검증 실패 요청은 별도 보고서로도 확인합니다. artifacts/reports/chapter_04_validation_failure_examples.md에는 대표 실패 요청의 client_id, source_system, failed_field, error_category, error_detail, owner가 정리됩니다. 이 값이 있어야 “검증 실패가 8건입니다”에서 멈추지 않고 어느 클라이언트와 상위 입력 생성 로직을 먼저 확인할지 말할 수 있습니다. 반대로 이 필드가 비어 있으면 owner를 단정하지 않고 “owner 미확정, API 계약과 upstream payload 변경 이력 확인 필요”라고 써야 합니다.
로그 기반 원인 추적은 다음 순서로 진행합니다.
| 단계 | 확인 | 판단 질문 |
|---|---|---|
| 1 | 대시보드에서 이상 시점 확인 | 언제부터 달라졌는가 |
| 2 | 해당 시간대 로그 필터링 | 어떤 요청들이 포함되는가 |
| 3 | request_id로 대표 요청 확인 |
한 요청의 기록이 충분한가 |
| 4 | trace_id로 관련 이벤트 확인 |
같은 처리 흐름에 다른 실패가 있는가 |
| 5 | 모델 버전, 임계값, 검증 실패(validation_failure), 실패 필드, client/source, 지연 시간 비교 | 입력, 설정, 모델, 운영 문제 중 어디를 먼저 볼 것인가 |
현재 증거로는 로그가 요청 단위 원인 추적에 필요한 필드를 갖추어야 한다는 판단을 남길 수 있습니다. 도구 사용법보다 중요한 기준은 request_id, trace_id, 모델 버전, 임계값, 검증 실패, 실패 필드, client/source가 검색과 집계 가능한 형태로 남아 있는가입니다.