---
title: "OpenTelemetry로 LLM 요청 Trace 연결하기"
slug: "llm-backend-12-opentelemetry-tracing"
canonicalUrl: "https://moonshotnotes.com/posts/llm-backend-12-opentelemetry-tracing/"
sourceUrl: "https://moonshotnotes.com/posts/llm-backend-12-opentelemetry-tracing/"
markdownUrl: "https://moonshotnotes.com/agent/posts/llm-backend-12-opentelemetry-tracing.md"
language: "ko"
category: "AI Backend"
updatedAt: "2026-05-26"
agentTokenEstimate: 1914
---

# OpenTelemetry로 LLM 요청 Trace 연결하기

LLM 서비스에서 OpenTelemetry를 사용해 API 요청, retrieval, LLM 호출, validation, DB 저장을 하나의 trace로 연결하고 지연과 실패 원인을 분석하는 방법을 정리합니다.

## Agent metadata

- Source: https://moonshotnotes.com/posts/llm-backend-12-opentelemetry-tracing/
- Markdown: https://moonshotnotes.com/agent/posts/llm-backend-12-opentelemetry-tracing.md
- Language: ko
- Category: AI Backend
- Tags: LLM, Backend, OpenTelemetry, Tracing, Observability
- Updated: 2026-05-26
- Estimated tokens: 1914

## API, RAG, 모델 호출, 응답 검증을 하나의 흐름으로 보기

이 글에서는 OpenTelemetry를 사용해 LLM 서비스의 요청 흐름을 trace로 연결하는 방법을 정리합니다.

LLM 서비스의 장애 분석은 일반 API보다 어렵습니다. 한 요청 안에 검색, 모델 호출, 응답 검증, 저장, 캐시, 외부 provider 호출이 섞여 있기 때문입니다. 사용자는 “답변이 느리다”고 말하지만, 실제 원인은 vector search일 수도 있고, 모델 호출일 수도 있고, schema validation 재시도일 수도 있습니다.

그래서 trace가 필요합니다.

분석 기준일: 2026-05-12
실습 기준 환경: FastAPI, OpenTelemetry, PostgreSQL, Redis, LLM Provider API
주요 참고자료: OpenTelemetry Docs, W3C Trace Context, Google SRE

## 핵심 요약

- OpenTelemetry는 traces, metrics, logs를 생성·수집·내보내기 위한 관측성 프레임워크다.
- LLM 요청은 API, retrieval, LLM call, validation, DB save를 span으로 나눠야 한다.
- trace_id는 로그, metric, eval result와 연결되어야 한다.
- prompt 원문과 문서 전문은 span attribute에 넣지 않는다.
- p95/p99 latency, error rate, token usage를 trace와 함께 봐야 한다.

## 1. 왜 LLM 서비스에 trace가 필요한가

LLM 요청은 여러 하위 작업으로 나뉩니다.

```text
# 예시입니다.
POST /answers
→ authenticate
→ rate limit check
→ retrieval
→ prompt build
→ llm call
→ schema validation
→ db save
→ response
```

전체 응답 시간이 8초라고 할 때 어디서 시간이 걸렸는지 모르면 개선할 수 없습니다.

| 병목 위치 | 가능한 원인 |
|---|---|
| retrieval | vector index, metadata filter, DB 부하 |
| prompt build | context 과다, token 계산 |
| llm call | provider 지연, rate limit, output 길이 |
| validation | schema 실패, 재시도 |
| db save | connection pool, transaction 지연 |

Trace는 이 흐름을 span 단위로 나눠 보여줍니다.

## 2. OpenTelemetry 기본 개념

| 개념 | 설명 |
|---|---|
| Trace | 하나의 요청 전체 흐름 |
| Span | trace 안의 개별 작업 단위 |
| Attribute | span에 붙는 key-value metadata |
| Metric | 시간에 따른 수치 데이터 |
| Log | 개별 이벤트 기록 |
| Collector | telemetry 수집·가공·전송 컴포넌트 |

LLM 서비스에서는 trace와 token usage metric을 연결하는 것이 중요합니다.

## 3. LLM 요청 span 설계

추천 span 구조:

```text
# 예시입니다.
answer.create
├── auth.check
├── rate_limit.check
├── retrieval.search
│   └── vector.query
├── prompt.build
├── llm.call
├── output.validate
└── answer.save
```

각 span에는 duration과 error 여부가 기록됩니다.

## 4. Trace ID 전파

외부에서 들어온 `traceparent` header가 있으면 이어받고, 없으면 새 trace를 만듭니다.

```http
# 예시입니다.
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
```

응답에도 request_id나 trace_id를 포함하면 장애 신고를 받을 때 추적이 쉬워집니다.

```jsonc
// 예시 JSON 구조입니다.
{
  "answer": "...",
  "trace_id": "0af7651916cd43dd8448eb211c80319c"
}
```

## 5. Span Attribute 설계

좋은 attribute는 분석에 도움이 되면서 민감정보를 포함하지 않아야 합니다.

| Attribute | 예시 | 주의 |
|---|---|---|
| `llm.provider` | openai | OK |
| `llm.model` | configured-model | OK |
| `llm.prompt_version` | answer.v3 | OK |
| `llm.input_tokens` | 3200 | OK |
| `llm.output_tokens` | 800 | OK |
| `rag.top_k` | 5 | OK |
| `rag.document_scope` | official_docs | OK |
| `user.question` | 원문 질문 | 넣지 않기 |
| `prompt.full` | 전체 prompt | 넣지 않기 |
| `document.content` | chunk 전문 | 넣지 않기 |

## 6. 민감정보와 보안

> **보안 주의**
> OpenTelemetry attribute와 log에는 prompt 원문, 문서 전문, 개인정보, API key, access token을 넣지 않아야 합니다. 관측성 데이터도 외부 시스템으로 전송될 수 있으므로 보안 등급을 별도로 관리해야 합니다.

대신 hash나 길이, category를 기록합니다.

```text
# 예시입니다.
question_hash
prompt_version
context_token_count
retrieved_chunk_count
document_scope
```

## 7. Metrics와 Logs 연결

Trace만으로는 전체 경향을 보기 어렵습니다. Metrics와 Logs도 함께 설계해야 합니다.

| Signal | 역할 |
|---|---|
| Trace | 한 요청의 상세 흐름 |
| Metric | 전체 경향과 알림 |
| Log | 이벤트와 디버깅 정보 |

예시 metric:

```text
# 예시입니다.
llm_request_duration_ms
llm_provider_latency_ms
llm_schema_validation_fail_total
rag_retrieval_latency_ms
rag_empty_result_total
llm_input_tokens_total
llm_output_tokens_total
```

로그에는 trace_id를 반드시 포함합니다.

```jsonc
// 예시 JSON 구조입니다.
{
  "level": "ERROR",
  "message": "LLM schema validation failed",
  "trace_id": "0af...",
  "prompt_version": "answer.v3",
  "error_code": "SCHEMA_VALIDATION_FAILED"
}
```

## 8. Dashboard에서 볼 것

초기 dashboard는 복잡할 필요가 없습니다.

```text
# 예시입니다.
[ ] 요청 수
[ ] p50/p95/p99 latency
[ ] error rate
[ ] provider latency
[ ] schema validation failure rate
[ ] retrieval latency
[ ] token usage
[ ] cache hit rate
[ ] eval pass rate
```

Google SRE의 four golden signals인 latency, traffic, errors, saturation을 LLM 서비스에 맞게 확장하면 됩니다.

## 9. FastAPI 예제 구조

```python
# 예시 코드입니다.
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

# 이 선언은 예시 흐름을 보여줍니다.
async def create_answer(req):
    with tracer.start_as_current_span("answer.create") as span:
        span.set_attribute("llm.prompt_version", "answer.v3")

        with tracer.start_as_current_span("retrieval.search") as s:
            chunks = await retrieve(req.question)
            s.set_attribute("rag.top_k", len(chunks))

        with tracer.start_as_current_span("llm.call") as s:
            result = await call_llm(req, chunks)
            s.set_attribute("llm.input_tokens", result.usage.input_tokens)
            s.set_attribute("llm.output_tokens", result.usage.output_tokens)

        with tracer.start_as_current_span("output.validate"):
            validated = validate_output(result)

        return validated
```

이 예시는 구조를 보여주기 위한 코드입니다. 실제 서비스에서는 middleware, exporter, collector 설정이 필요합니다.

## 10. 실무 체크리스트

```text
# 예시입니다.
[ ] 요청 전체를 하나의 trace로 볼 수 있는가?
[ ] retrieval, llm call, validation이 별도 span인가?
[ ] trace_id가 API 응답과 로그에 포함되는가?
[ ] span attribute에 민감정보가 없는가?
[ ] token usage가 metric으로 기록되는가?
[ ] schema validation failure를 추적하는가?
[ ] p95/p99 latency를 dashboard에서 보는가?
[ ] eval result와 prompt_version을 연결할 수 있는가?
```

## 실패 사례: 로그는 많은데 병목을 찾지 못하는 경우

LLM 서비스에서 `request started`, `retrieval done`, `model done` 같은 로그를 많이 남겨도 장애 분석이 어려울 때가 있습니다. 각 로그가 같은 request에 속한다는 것은 알지만, 어느 단계가 p95 latency를 밀어 올리는지, retry가 몇 번 일어났는지, validation 실패가 model 호출 전인지 후인지 한눈에 보기 어렵기 때문입니다. 로그는 사건 목록이고, trace는 사건 사이의 부모-자식 관계와 시간을 보여줍니다.

특히 RAG 요청은 병목이 매번 바뀝니다. 어떤 요청은 vector search가 느리고, 어떤 요청은 provider queue가 길고, 어떤 요청은 schema validation retry 때문에 두 번째 모델 호출이 생깁니다. 하나의 trace 안에 `api.request`, `retrieval.search`, `llm.call`, `output.validate`, `answer.persist` span이 있으면 문제 위치를 단계별로 분리할 수 있습니다.

## 구현 예시: LLM 요청 span 구조

```text
trace llm_answer_request
  span api.request
    span auth.check
    span retrieval.search
    span llm.call
    span output.validate
    span answer.persist
```

각 span attribute에는 원문이 아니라 운영 가능한 요약값을 넣습니다.

```text
llm.model = "runtime-selected-model"
llm.prompt_version = "rag_answer.v4"
llm.input_tokens = 4200
llm.output_tokens = 620
retrieval.top_k = 8
retrieval.index = "document_chunks_hnsw_v2"
validation.schema = "answer_with_citations.v2"
```

prompt 전문, 문서 전문, 사용자 개인정보를 attribute에 넣지 않는 것이 중요합니다. 대신 hash, token count, version, chunk id처럼 재현과 분석에 필요한 최소값을 남깁니다.

## 체크리스트 적용 결과

| 확인 항목 | 좋은 신호 | 위험 신호 |
|---|---|---|
| trace 연결 | API, retrieval, LLM, validation span이 한 trace에 있음 | 단계별 로그만 흩어져 있음 |
| 민감정보 | prompt hash와 token count만 저장 | 원문 prompt와 chunk 전문 저장 |
| 비용 분석 | token usage가 trace와 metric에 연결 | 비용 리포트와 장애 trace가 분리 |
| 재시도 | retry span이 원 호출의 child로 보임 | 재시도가 새 request처럼 보임 |

이 구조가 있으면 "LLM이 느리다"는 모호한 신고를 "retrieval p95가 증가했다" 또는 "schema validation retry가 늘었다"로 바꿀 수 있습니다.

## 11. Q&A

### Q1. 로그만 잘 남기면 trace가 없어도 되나요?

작은 서비스에서는 로그로 시작할 수 있습니다. 하지만 요청이 여러 단계로 나뉘면 trace가 훨씬 유리합니다. 특히 LLM provider 호출과 retrieval 병목을 구분하려면 span이 필요합니다.

### Q2. 모든 요청을 trace하면 비용이 크지 않나요?

샘플링을 사용할 수 있습니다. 다만 오류 요청, 긴 지연 요청, eval 실패 요청은 우선적으로 보존하는 정책이 좋습니다.

### Q3. prompt 내용을 trace에 넣어도 되나요?

권장하지 않습니다. prompt와 문서 chunk에는 민감정보가 포함될 수 있습니다. hash, token count, version 등으로 대체합니다.

## 12. 참고자료와 불확실성

### 참고자료

- OpenTelemetry Docs: https://opentelemetry.io/docs/
- What is OpenTelemetry: https://opentelemetry.io/docs/what-is-opentelemetry/
- W3C Trace Context: https://www.w3.org/TR/trace-context/
- Google SRE — Monitoring Distributed Systems: https://sre.google/sre-book/monitoring-distributed-systems/

### 불확실성

- OpenTelemetry SDK 설정은 언어와 프레임워크에 따라 달라집니다.
- 샘플링 정책과 데이터 보관 기간은 조직의 비용·보안 정책에 따라 결정해야 합니다.

---
