OpenTelemetry로 LLM 요청 Trace 연결하기
메뉴
Moonshot Notes orbit notebook mark
Moonshot NotesAI 도구와 개발 워크플로우 기록하는 공간

AI Backend

OpenTelemetry로 LLM 요청 Trace 연결하기

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

OpenTelemetry로 LLM 요청 Trace 연결하기 hero image
Markdown약 2235 tokens

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 요청은 여러 하위 작업으로 나뉩니다.

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

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

병목 위치가능한 원인
retrievalvector index, metadata filter, DB 부하
prompt buildcontext 과다, token 계산
llm callprovider 지연, rate limit, output 길이
validationschema 실패, 재시도
db saveconnection pool, transaction 지연

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

2. OpenTelemetry 기본 개념

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

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

3. LLM 요청 span 설계

추천 span 구조:

# 예시입니다.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를 만듭니다.

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

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

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

5. Span Attribute 설계

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

Attribute예시주의
llm.provideropenaiOK
llm.modelconfigured-modelOK
llm.prompt_versionanswer.v3OK
llm.input_tokens3200OK
llm.output_tokens800OK
rag.top_k5OK
rag.document_scopeofficial_docsOK
user.question원문 질문넣지 않기
prompt.full전체 prompt넣지 않기
document.contentchunk 전문넣지 않기

6. 민감정보와 보안

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

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

# 예시입니다.question_hashprompt_versioncontext_token_countretrieved_chunk_countdocument_scope

7. Metrics와 Logs 연결

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

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

예시 metric:

# 예시입니다.llm_request_duration_msllm_provider_latency_msllm_schema_validation_fail_totalrag_retrieval_latency_msrag_empty_result_totalllm_input_tokens_totalllm_output_tokens_total

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

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

8. Dashboard에서 볼 것

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

# 예시입니다.[ ] 요청 수[ ] 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 예제 구조

# 예시 코드입니다.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. 실무 체크리스트

# 예시입니다.[ ] 요청 전체를 하나의 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 구조

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

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

llm.model = "runtime-selected-model"llm.prompt_version = "rag_answer.v4"llm.input_tokens = 4200llm.output_tokens = 620retrieval.top_k = 8retrieval.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 SDK 설정은 언어와 프레임워크에 따라 달라집니다.
  • 샘플링 정책과 데이터 보관 기간은 조직의 비용·보안 정책에 따라 결정해야 합니다.

독자 적용 노트

이 글은 프로토타입 LLM 기능을 운영 가능한 서비스로 바꾸려는 백엔드 개발자가 OpenTelemetry로 LLM 요청 Trace 연결하기이라는 주제를 단순 개념 설명이 아니라 운영 환경에서 재현 가능한 백엔드 판단 기준으로 점검하도록 작성했습니다. 본문에서 다룬 구조는 새 도구를 도입하거나 기존 워크플로우를 바꿀 때 "무엇을 먼저 확인해야 하는가"를 정하는 데 쓰는 것이 좋습니다.

원본 판단 근거

이 글의 판단은 API, RAG, 모델 호출, 응답 검증을 하나의 흐름으로 보기에서 설명한 구조와 본문 안의 예시, 표, 체크리스트를 함께 놓고 정리한 것입니다. 특히 독자가 그대로 복사할 수 있는 결론보다, 자신의 코드베이스나 팀 규칙에 맞춰 확인해야 할 경계와 실패 조건을 분리하는 데 초점을 둡니다.

실패 사례 또는 edge case

가장 흔한 실패는 운영 환경에서 재현 가능한 백엔드 판단 기준을 문서나 명칭만 맞춘 상태로 끝내는 것입니다. 예를 들어 실제 입력, 권한, 로그, 배포, 검증 기준이 연결되지 않으면 겉으로는 도입이 끝난 것처럼 보여도 다음 작업에서 같은 문제가 반복됩니다. 따라서 본문을 적용할 때는 "누가 실행하는가", "무엇을 기록하는가", "실패하면 어디서 멈추는가"를 같이 확인해야 합니다.

실무 체크리스트

  • 이 글의 핵심 개념을 현재 프로젝트의 실제 파일, API, 로그, 문서 중 하나에 연결했다.
  • 본문 예시를 그대로 쓰지 않고 우리 환경의 제약 조건으로 다시 검토했다.
  • 실패 사례나 edge case를 하나 이상 정하고, 재현 또는 확인 방법을 적었다.
  • 적용 후 바뀌어야 할 완료 기준과 검증 명령을 분리했다.

Q&A

Q1. 이 글의 내용을 바로 표준으로 써도 되나요?

바로 표준으로 고정하기보다 작은 작업 하나에 먼저 적용해 보는 편이 안전합니다. 실제 로그, 리뷰, 테스트에서 같은 판단이 반복해서 맞을 때 팀 표준으로 올리는 것이 좋습니다.

Q2. 본문 예시와 내 프로젝트가 다르면 어떻게 해야 하나요?

예시의 이름보다 경계를 보세요. 입력, 상태, 권한, 검증, 기록 중 어떤 경계를 다루는 글인지 먼저 찾고, 그 경계를 현재 프로젝트의 구조에 맞게 옮기면 됩니다.

참고자료와 불확실성

이 보강 노트는 본문에 이미 포함된 참고자료와 작성 시점의 공개 문서를 기준으로 합니다. 도구 버전, API 정책, 제품 UI는 바뀔 수 있으므로 실제 적용 전에는 공식 문서와 현재 런타임 동작을 다시 확인해야 합니다.

댓글

GitHub 계정으로 로그인하면 댓글을 남길 수 있습니다. 댓글은 GitHub Discussions를 통해 운영됩니다.

TOP