콘텐츠로 이동

4-3. 구조화 로그와 request_id

구조화 로그는 운영 중 품질 문제를 요청 단위로 다시 확인하기 위한 근거입니다. 핵심은 어떤 로그 필드(field)가 있어야 request_id로 개별 요청을 찾고, trace_id로 관련 흐름을 넓혀 볼 수 있는지 판단하는 것입니다.

이 문서를 읽을 때는 다음 기준을 중심으로 확인합니다.

  • 구조화 로그: 문자열이 아니라 JSON 필드로 남긴 로그의 필요성
  • 추적 키: request_idtrace_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가 검색과 집계 가능한 형태로 남아 있는가입니다.