ai-machine-learning

LLM 로깅 모범 사례: 프로덕션에서 지키는 9가지 규칙 [2026]

작성자 Mert Batur
Aug 2, 2026
11 분 읽기
LLM 로깅 모범 사례: 프로덕션에서 지키는 9가지 규칙 [2026]

LLM 로깅 모범 사례: 프로덕션에서 지키는 9가지 규칙 [2026]

이 9가지 LLM 로깅 모범 사례는 저희 프로덕션 스택이 실제로 따르는 규칙입니다. 네 개 서비스에서 월 120만 건의 LLM 요청을 로깅하며, 모든 요청은 model, tokens, latency, cost_usd, trace_id를 담은 한 줄의 JSON으로 Grafana Loki에 쌓입니다. structlog 25.4.0이 레코드를 쓰고, Presidio가 먼저 PII를 제거하며, 이 파이프라인 전체가 저희 옵저버빌리티 스택의 로깅 영역에 해당합니다.

핵심 요약

  • 모든 LLM 요청을 14개 이상의 네임드 필드를 가진 구조화 JSON으로 로깅하세요. 자유 텍스트는 절대 안 됩니다.
  • PII는 로그를 쓰기 전에 Presidio 또는 동급 도구로 마스킹하세요. 쓴 다음에는 늦습니다.
  • 모든 트레이스에 OpenTelemetry GenAI 시맨틱 컨벤션 속성을 붙이세요.
  • 하루 100만 요청이면 같은 60GB가 Datadog에서 월 $108, Loki에서 $30, ClickHouse에서 $1.20입니다.

LLM 로깅이란 정확히 무엇인가 (그리고 "그냥 다 남기자"가 실패하는 이유)

LLM 로깅은 모든 모델 요청과 응답의 구조화된 레코드를 캡처하는 일입니다. 프롬프트, 완성 응답, 토큰 수, 지연 시간, 비용, 그리고 이 모든 것을 사용자 세션에 묶어주는 트레이스까지. 인프라 로깅과는 다릅니다. CPU, 메모리, 파드 재시작은 메트릭 스택의 영역이고, 이 글은 모델 동작을 디버깅하고, 비용을 계산하고, 감사할 수 있게 해주는 요청 수준의 레코드만 다룹니다.

"그냥 다 로깅하자"는 본능은 좀처럼 사라지지 않으며, 비쌉니다. 하루 100만 요청의 전체 프롬프트와 완성 응답은 월 약 60GB의 텍스트를 만들고, 그 텍스트의 상당 부분은 이제 영구 저장하게 되는 고객 PII입니다. GDPR 제5조 데이터 최소화 원칙은 개인 데이터를 "적절하고, 관련성 있으며, 필요한 범위로 제한"하라고 요구하는데, 원본 프롬프트 덤프는 첫날부터 이 테스트를 통과하지 못합니다. 모든 것을 로깅하는 것은 전략이 아니라 월 청구서가 따라붙는 법적 리스크입니다.

9가지 LLM 로깅 규칙은 무엇인가?

아홉 가지 규칙을, 저희가 도입할 순서대로 나열하면 이렇습니다. 해시된 식별자와 함께 전체 프롬프트·응답 로깅, 구조화 JSON 발행, 요청당 토큰·비용 캡처, OpenTelemetry 트레이스 컨텍스트 부착, 쓰기 전 PII 마스킹, 대량 트래픽 샘플링, 보관 등급 설정, 안전 이벤트 분리, 그리고 결과를 쿼리 가능하게 만들기. 아래 각 규칙에는 이를 강제하는 코드나 표가 함께 붙습니다.

규칙 1: 프롬프트와 응답 전체를 로깅하되 (원본 PII가 아닌 해시로)

모든 요청의 프롬프트 전문과 완성 응답 전문을 로깅하세요. 부분 로그만 남기면, 모델이 실제로 무엇을 봤는지 기록도 없는 채로 장애를 들여다보는 상황이 옵니다. 단 한 가지 예외는 식별 정보입니다. 원본 사용자 ID, 이메일, 이름을 레코드에 절대 쓰지 마세요. 대신 사용자 ID의 SHA-256 해시를 저장하세요. 해시가 있어도 오프라인 조회로 한 사용자의 전체 세션 이력을 재구성할 수 있지만, 로그 줄 자체는 읽을 권한이 없는 사람에게 무용지물로 남습니다. 시스템 프롬프트도 같은 논리입니다. 해시해서 해시를 로깅하고, 평문은 이미 버전 관리 중인 프롬프트 레지스트리에 보관하세요.

규칙 2: 구조화 JSON 사용 — 모든 필드에 이름을, 자유 텍스트는 제로

Python이든 다른 언어든 LLM 로깅 모범 사례에서 구조화 로깅만은 타협 불가입니다. 모든 필드에 이름과 타입이 있고 쿼리할 수 있어야 하며, 포맷팅된 문자열로 덤프하는 것은 없어야 합니다. INFO called gpt-4o, took 812ms 같은 자유 텍스트 줄은 grep이 한계입니다. JSON 레코드는 모델별로 집계하고, 비용별로 합산하고, 트레이스와 조인할 수 있습니다. OpenAI의 자체 프로덕션 모범 사례도 같은 방향을 권합니다. print 문이 아니라 SDK 계층에서 구조화된 메타데이터를 캡처하라는 것입니다.

다음은 Techsy의 모든 서비스가 발행하는 스키마로, 14개 필드입니다.

json
{
  "request_id": "req_01J9XK4M7Q",
  "timestamp": "2026-07-18T09:24:31.482Z",
  "environment": "production",
  "model": "claude-sonnet-4-20250514",
  "prompt_tokens": 1284,
  "completion_tokens": 396,
  "latency_ms": 812,
  "cost_usd": 0.0098,
  "system_prompt_hash": "sha256:9f2c1a4e...",
  "user_id_hash": "sha256:b7e4d831...",
  "rag_sources": ["kb/pricing-2026.md", "kb/refund-policy.md"],
  "guardrail_result": "pass",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "span_id": "00f067aa0ba902b7"
}

세 필드는 짚고 넘어갈 가치가 있습니다. cost_usd는 요청 시점에 토큰 수와 모델의 공시 요금으로 계산하며, 야간 배치로 사후 보정하지 않습니다. 두 해시 필드는 규칙 1의 절충안입니다. 오프라인에서는 상관 분석이 가능하지만 로그에서는 불투명합니다. 그리고 trace_idspan_id는 W3C 트레이스 컨텍스트 값이며, 정확히 규칙 4의 주제입니다.

네 서비스가 프로바이더를 직접 호출하면 계측할 곳도 네 군데라고 느껴진다면, LiteLLM 프록시로 중앙화할 수 있습니다. 모든 프로바이더 앞에 로깅 훅 하나만 두면 됩니다.

규칙 3: 요청당 토큰 수와 비용을 캡처하라

토큰 사용량 추적은 내일 돌아가는 웨어하우스 잡이 아니라 로그 줄 자체에 들어가야 합니다. 모든 프로바이더는 응답에 입력·출력 토큰 수를 돌려줍니다. 바로 그 시점의 모델 토큰당 요율을 곱해 cost_usd를 레코드에 쓰세요. 요율은 바뀌고, 캐시된 입력 토큰과 신규 입력 토큰의 요율은 다르므로, 나중에 고정 가격표로 비용을 계산하면 조용히 역사가 다시 쓰입니다. 모든 줄에 비용이 붙으면 "어떤 기능이 비싼가?"가 재무 프로젝트가 아니라 한 줄 쿼리가 되고, LLM API 지출 줄이기 작업으로도 바로 이어집니다.

규칙 4: 트레이스 컨텍스트를 붙여라 (OpenTelemetry GenAI Semconv)

트레이스 ID가 없는 로그 줄은 고아입니다. 읽을 수는 있지만 어떤 재시도에서, 어떤 RAG 단계에서, 어떤 사용자 턴에서 만들어졌는지 알 수 없습니다. 해법은 OpenTelemetry의 GenAI 시맨틱 컨벤션으로, 모델 호출을 계측하기 위한 표준 속성 이름입니다. 활성 스팬 안에서 로그를 발행하면 trace_idspan_id가 자동으로 붙고, Grafana에서 클릭 한 번이면 트레이스 폭포에서 원본 레코드로 바로 갑니다.

모든 gen_ai 스팬에 설정할 가치가 있는 속성들입니다.

속성타입예시용도
gen_ai.systemstring"anthropic"프로바이더 이름
gen_ai.request.modelstring"claude-sonnet-4-20250514"요청한 모델
gen_ai.response.modelstring"claude-sonnet-4-20250514"실제로 응답한 모델
gen_ai.usage.input_tokensint1284프롬프트 크기
gen_ai.usage.output_tokensint396완성 응답 크기
gen_ai.response.finish_reasonsstring[]["stop"]생성 종료 사유
gen_ai.response.idstring"msg_01XK9..."프로바이더 응답 ID
python
from opentelemetry import trace

tracer = trace.get_tracer("ai-sdr")

with tracer.start_as_current_span("chat.completion") as span:
    span.set_attribute("gen_ai.system", "anthropic")
    span.set_attribute("gen_ai.request.model", "claude-sonnet-4-20250514")
    response = client.messages.create(**payload)
    span.set_attribute("gen_ai.usage.input_tokens", response.usage.input_tokens)
    span.set_attribute("gen_ai.usage.output_tokens", response.usage.output_tokens)
    span.set_attribute("gen_ai.response.finish_reasons", [response.stop_reason])
    log.info("llm.request", cost_usd=cost)  # Rule 2 record, trace IDs auto-attached

규칙 5: 로그를 쓰기 전에 PII를 마스킹하라

PII 마스킹은 레코드가 쓰기 전에 일어나야지, 쓴 다음에 닦아낼 일이 아닙니다. 이메일 주소가 Loki에 들어가는 순간 오브젝트 스토리지 백업에도 들어간 것이고, "나중에 지웠습니다"는 GDPR의 답이 되지 못합니다. 저희 구성에서는 Microsoft Presidio가 structlog 프로세서로 실행되며 이메일과 전화번호의 **94%**를 Loki 도달 전에 잡아냅니다. 놓치는 경우는 거의 모두 특이한 포맷 때문이며, 발견할 때마다 커스텀 리코그나이저로 패치합니다.

훅 전체는 열다섯 줄입니다.

python
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine

analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()

PII_ENTITIES = ["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER", "CREDIT_CARD", "IBAN_CODE"]

def redact_pii(text: str) -> str:
    results = analyzer.analyze(text=text, language="en", entities=PII_ENTITIES)
    return anonymizer.anonymize(text=text, analyzer_results=results).text

# In the structlog processor chain, before JSONRenderer:
# event_dict["prompt"] = redact_pii(event_dict["prompt"])

마스킹은 입력·출력 필터와 같은 파이프라인 층에 있고, 테스트도 같은 방식으로 해야 합니다. 저희 가드레일 파이프라인은 로그에 새어나간 이메일을 운영 메모가 아니라 실패한 평가로 취급합니다.

규칙 6: 대량 트래픽에서는 지능적으로 샘플링하라

하루 약 10만 요청 이하라면 모든 것을 로깅하세요. 그 이상에서 전체 볼륨 로깅은 절대 읽지 않을 데이터에 매기는 저장세이며, 샘플링이 중요한 레코드를 지키는 방법입니다. 함정이 있습니다. 무작위 샘플링은 LLM 트래픽에 최악의 선택지입니다. 실패, 거부, 5달러짜리 요청은 정의상 드물기 때문에, 균일한 10% 비율은 정확히 디버깅할 이벤트만 버립니다. 코인 던지기가 아니라 결과별로 샘플링하세요.

전략쓸 때복잡도
무작위 (고정 10%)안정 트래픽에서 기본 볼륨 지표낮음
규칙 기반특정 모델·테넌트·라우트는 항상 보관낮음
테일 기반느리거나 비싸거나 오류 난 요청을 보관, 정상은 드롭중간
트리거 기반가드레일 발동 또는 평가 실패 시에만 전체 컨텍스트중간
적응형샘플링 비율이 트래픽 볼륨에 따라 증감높음

흔한 구성은 양 끝단(프로덕션과 엔터프라이즈 테넌트: 항상 로깅)에 규칙 기반을, 중간에 테일 기반을 쓰는 것입니다. 이 프레임워크의 로깅 관점 포인트: guardrail_resultcost_usd 필드가 곧 샘플링 시그널이며, 규칙 2와 8을 따랐다면 이미 존재합니다.

규칙 7: 필요해지기 전에 보관 정책을 정하라

로그 보관 정책은 마음이 차분할 때 내려야 하는 결정입니다. 대안은 볼륨이 두 배가 된 비용 리뷰 자리에서 결정하는 것이니까요. GDPR 제5조 저장 제한 원칙은 개인 데이터를 "필요한 기간 이상" 보관하지 말라고 하며, 실무적으로는 등급별 보관을 뜻합니다.

등급보관 기간저장소용도
Hot7일Loki / ClickHouse 로컬 디스크실시간 디버깅, 온콜 쿼리
Warm30일오브젝트 스토리지 기반 인덱스 (S3)스프린트 비용 분석, 장애 리뷰
Cold1년압축된 S3/GCS 아카이브컴플라이언스 요청, 연간 감사

Hot은 "10분 전에 무슨 일이 있었지?"에 빠르고 비싸게 답하고, Cold는 "3월에 이 고객에게 뭐라고 답했지?"에 느리고 싸게 답합니다. 일정대로 자동으로 삭제하세요. 그렇지 않으면 등급제는 그냥 그림입니다.

규칙 8: 가드레일·안전 이벤트는 따로 로깅하라

안전 이벤트(가드레일 차단, 거부, 정책 위반)는 텔레메트리가 아니라 감사 레코드이며, 전용 스트림에 속합니다. 이유는 세 가지입니다. 알림: 프롬프트 인젝션 차단 급증은 누군가를 페이지해야 하며, 100만 줄의 일상 로그 위에서는 그 알림을 튜닝할 수 없습니다. 보관: 컴플라이언스가 안전 레코드를 디버그 로그보다 몇 년 더 오래 보관하라고 요구할 수 있습니다. 접근: 감사 담당자는 전체 파이어호스가 아니라 안전 스트림만 받습니다. 메인 레코드에는 판정을 태그하고(guardrail_result: "block"), 전체 레코드는 별도 스트림으로 라우팅하세요. 무엇을 안전 이벤트로 칠지는 가드레일 이벤트 가이드에서 다룹니다.

규칙 9: 로그를 저장만 하지 말고 쿼리 가능하게 만들어라

1분 안에 쿼리할 수 없는 로그는 옵저버빌리티 시그널이 아니라 백업입니다. 쿼리 가능하다는 것은 인덱싱된 필드, 온콜 엔지니어가 실제로 아는 쿼리 언어, 그리고 장애 전에 만들어 둔 대시보드를 뜻합니다. 저희는 Loki를 운영하며 비용 이상, 지연 시간 회귀, "어제 테넌트 X의 모든 거부를 보여줘"를 위해 주 30회 이상 쿼리합니다. 구문은 Grafana Loki 문서가 레퍼런스입니다. 진짜 값어치를 하는 패턴은 파싱된 JSON 필드를 직접 필터링하는 것입니다.

logql
{service="ai-sdr"} |= "llm.request" | json
  | model = "claude-sonnet-4-20250514"
  | cost_usd > 0.05
  | line_format "{{.timestamp}} {{.request_id}} ${{.cost_usd}} {{.latency_ms}}ms"

다섯 줄, 노트북으로 내보내기 없음. 지금 쓰는 저장소가 이걸 못 한다면, 그게 가장 먼저 고칠 문제입니다.

프로덕션에서 실제로 로깅하는 것

이론은 충분합니다. 다음은 저희 AI SDR 파이프라인의 마스킹된 설정으로, 서두의 월 120만 요청이라는 숫자를 만드는 서비스입니다. structlog 25.4.0으로 JSON을 렌더링하고 Promtail로 Grafana Cloud Loki에 전송합니다. 이 파이프라인의 모델은 claude-sonnet-4-20250514이며, 모든 호출이 정확히 규칙 2와 5의 프로세서 체인을 거칩니다.

python
import structlog

structlog.configure(
    processors=[
        structlog.contextvars.merge_contextvars,
        structlog.processors.add_log_level,
        structlog.processors.TimeStamper(fmt="iso"),
        redact_pii,          # Rule 5: Presidio, before serialization
        add_otel_trace_ids,  # Rule 4: trace_id + span_id from the active span
        structlog.processors.JSONRenderer(),
    ],
)
log = structlog.get_logger()

이 구성으로 첫 분기에 나온 숫자 두 개. 월 인제스트는 네 서비스 합산 47GB에 정착했고, p95 로그 쓰기 지연은 3ms로, 파이프라인이 요청 시간에 더하는 것은 측정 가능한 수준이 없습니다.

스스로 비용을 회수한 설정 변경 하나: 2026년 3월에 모든 로그 항목에 cost_usd를 추가했습니다. 일주일 안에 재시도 루프로 월 $340을 태우는 프롬프트 템플릿 하나를 찾았습니다. 일시적인 API 오류가 3회 재시도를 유발하고 있었고, 각 재시도는 4,000토큰 컨텍스트 전체를 다시 보냈습니다. 로그 덕분에 한 줄 쿼리로 끝났습니다. 요청당 비용이 없었다면 다음 분기 예산 리뷰에서 설명되지 않는 항목으로 떠올랐을 것입니다.

대규모 LLM 로그 저장 비용은 얼마나 드나?

하루 100만 요청에서 LLM 로그 저장 비용은 같은 데이터에 대해 저장소에 따라 대략 월 $1.20에서 $108 사이입니다. 계산은 이렇습니다. 완전한 구조화 레코드는 평균 약 2KB이므로, 하루 100만 요청이면 하루 2GB, 월 60GB입니다. 아래 벤더 공시 가격(2026년 7월 기준)은 그 60GB가 흔한 백엔드 세 곳에서 드는 비용입니다.

백엔드가격 모델 (벤더 공시, 2026년 7월)60GB/월비고
Datadog LLM Observability인제스트 $0.10/GB + 인덱싱 $1.70/GB~$108비싼 쪽은 인덱싱 항목
Grafana Cloud Loki오브젝트 스토리지 경유 ~$0.50/GB~$30셀프 호스팅이면 더 저렴
ClickHouse (셀프 호스팅, S3)압축 저장 ~$0.02/GB~$1.20 + 컴퓨트실제 비용은 컴퓨트

출처: Datadog 가격, Grafana Loki, ClickHouse 옵저버빌리티 문서.

주의할 점 두 가지. 벤더 요율로 계산한 저희 산출물이지 직접 돌린 벤치마크가 아니라는 점입니다. 첫째, Datadog 숫자는 모든 것을 인덱싱한다고 가정합니다. 대부분의 팀은 일부만 인덱싱해 훨씬 적게 내고, Loki와 ClickHouse는 주로 저장량에 과금합니다. 둘째, 셀프 호스팅 ClickHouse의 $1.20에는 실제 청구서가 숨어 있습니다. 클러스터를 돌리는 컴퓨트와 운영하는 엔지니어 시간입니다. 월 60GB에서는 매니지드 서비스가 거의 항상 총비용 면에서 더 쌉니다. 셀프 호스팅이 합리적이기 시작하는 건 대략 월 1TB를 넘어서면서, GB당 격차가 운영 오버헤드를 압도하는 지점입니다.

핵심은 이 격차입니다. 하루 100만 요청에서 Datadog 인덱싱과 셀프 호스팅 ClickHouse의 차이는 약 90배입니다. 같은 60GB에 $108 대 $1.20. 저장소는 아키텍처 단계에서 고르세요. 청구서가 도착한 다음이 아니라.

어떤 로깅 도구를 골라야 하나?

대부분의 팀에서 선택지는 네 가지로 좁혀집니다. LLM 네이티브 플랫폼(Langfuse 또는 LangSmith), 프록시 계층 도구(Helicone), 또는 이미 운영 중인 인프라로 보내는 평범한 OpenTelemetry 파이프라인. 표는 실제로 차이가 나는 결정 포인트만 담았습니다. 대시보드, 재생, 프롬프트 버전 관리는 네 옵션 모두의 기본값입니다.

LangfuseLangSmithHeliconeOTel 네이티브 (Loki/ClickHouse)
셀프 호스팅가능 (오픈소스 코어)불가 (SaaS)가능 (오픈소스)완전 가능
OTel 호환가능 (OTLP 인제스트)부분 (OTLP 익스포트)부분네이티브
비용 추적가능가능가능직접 구현 (cost_usd 계산)
내장 PII 마스킹불가 (전처리)불가불가불가 (규칙 5대로 Presidio)
무료 티어가능 (클라우드 + 셀프 호스트)가능 (제한적)가능소프트웨어는 무료, 인프라는 본인 부담

저희 의견을 솔직히 말하면, OTel 네이티브 + Loki로 운영합니다. 이미 다른 모든 것에 Grafana 스택이 있었고, 데이터 소스 하나를 추가하는 것이 네 번째 벤더를 들이는 것보다 나았기 때문입니다. 옵저버빌리티 스택이 전혀 없는 제로 상태에서 시작한다면, Langfuse의 트레이싱 모델과 무료 티어가 유용함에 이르는 가장 빠른 길이고, 셀프 호스트 옵션은 출구를 열어둡니다. 두 LLM 네이티브 리더 사이에서 고민 중이라면 Langfuse vs LangSmith 비교에서 전체 비교를 볼 수 있습니다. 로깅이 더 큰 모니터링 결정의 한 조각이라면 전체 플랫폼 비교가 더 넓은 영역을 다룹니다.

가장 흔한 LLM 로깅 실수는 무엇인가?

저희가 본 고장 난 LLM 로깅 구성의 대부분은 여섯 가지 실수로 설명됩니다. 각각은 로그 볼륨이 먼저 알아채기 전에 잡으면 피하는 비용이 저렴합니다.

  • 마스킹 없이 원본 PII 로깅. 가장 흔하고 가장 비쌉니다. 지원 내보내기 한 번이나 버킷 침해 한 번이면 프롬프트 로그가 데이터 보호 사고로 바뀝니다. 읽을 때가 아니라 쓰기 전에 마스킹하세요(규칙 5).
  • 보관 정책 없음. 무기한 저장이 모든 곳의 기본값이고, 매년 조용히 청구서를 두 배로 만듭니다. 절대 삭제하지 않는다면 로깅 시스템이 있는 게 아닙니다. 과대망상에 빠진 아카이브가 있는 것입니다.
  • 비정형 텍스트 로그. grep만 되는 print 출력은 데모 규모에서는 작동하지만, 하루 10만 요청에서 무너집니다. "모델 X의 모든 실패 요청을 찾아줘"가 쿼리 대신 셸 스크립트 반나절이 됩니다.
  • 에러만 로깅. 성공한 요청은 드리프트를 감지하는 기준선이며, 평가 파이프라인의 원재료입니다. 성공도 로깅하세요. 볼륨이 압박하면 샘플링해서라도.
  • 비용 필드 무시. 요청당 cost_usd가 없으면 비용 알림도, 기능별 귀속도 없고, 앞의 프로덕션 섹션 그 월 $340 재시도 루프는 분기 청구서까지 보이지 않습니다.
  • 트레이스 상관 없음. 스팬과 끊긴 로그는 다단계 에이전트 디버깅을 찍기로 만듭니다. 로그 줄에 trace_id가 없다면 규칙 4가 해법입니다.

저자 소개

Mert Batur는 Techsy.io의 공동 창업자로, 팀은 B2B 클라이언트를 위한 AI 에이전트, 자동화 시스템, 보이스/SDR 파이프라인을 만듭니다. Techsy 팀이 프로덕션에서 실제로 쓰는 LLM 도구 스택에 대해 씁니다. LinkedIn에서 연결됩니다.

자주 묻는 질문

모든 LLM 요청에 무엇을 로깅해야 하나요?

최소한 이것만은 남기세요. PII를 마스킹한 프롬프트·완성 응답 전문, 모델 이름, 입력·출력 토큰 수, 지연 시간, USD 비용, 해시된 사용자 식별자, OpenTelemetry 트레이스·스팬 ID. 파이프라인에 해당 단계가 있다면 RAG 소스 ID와 가드레일 판정도 추가하세요. 14개 네임드 필드, 요청당 한 줄의 JSON입니다.

LLM 로그에 가장 좋은 포맷은 무엇인가요?

구조화 JSON, 요청당 객체 하나, 모든 필드에 명시적 이름. 자유 텍스트 로그는 grep이 한계지만, JSON 레코드는 모델별로 집계하고 비용별로 합산하고 트레이스와 조인할 수 있습니다. Python의 structlog, Node의 pino 같은 구조화 로거로 레코드를 발행하고, 문자열 포맷팅이 아니라 JSON 시리얼라이저로 렌더링하세요.

LLM 로그의 PII는 어떻게 처리하나요?

로그 쓰기 전에 마스킹하세요. 쓴 다음에는 안 됩니다. 로깅 파이프라인 안에서 Microsoft Presidio 같은 탐지기로 프롬프트와 완성 응답을 통과시켜 이름, 이메일, 전화번호를 <EMAIL_ADDRESS> 같은 토큰으로 교체하세요. 원본 PII가 로그 저장소에 도달하는 순간 백업에도 들어가며, 사후 삭제는 대부분 GDPR 최소화 테스트를 만족시키지 못합니다.

대규모 LLM 로그 저장 비용은 얼마나 드나요?

하루 100만 요청, 레코드당 2KB로 월 약 60GB라면, Datadog 인덱싱 LLM Observability 가격으로 약 월 $108, Grafana Cloud Loki로 월 $30, 셀프 호스팅 ClickHouse는 압축 S3 저장 약 월 $1.20에 컴퓨트가 별도입니다. 2026년 7월 기준 벤더 공시 요율이며, 셀프 호스팅에는 엔지니어 시간이 더해집니다.

OpenTelemetry GenAI 시맨틱 컨벤션이란 무엇인가요?

LLM 호출 계측을 위한 OpenTelemetry의 표준 속성 이름입니다. 프로바이더는 gen_ai.system, 모델은 gen_ai.request.model, 토큰 수는 gen_ai.usage.input_tokens와 output_tokens, 생성 종료 사유는 gen_ai.response.finish_reasons입니다. 이를 쓰면 Jaeger부터 Tempo, Langfuse까지 모든 OTel 호환 백엔드가 커스텀 파서 없이 트레이스를 읽습니다.

트래픽이 많을 때 LLM 로그를 어떻게 샘플링하나요?

모든 에러, 모든 가드레일 차단, 비용 임계값 초과 요청은 전부 남기고, 나머지를 샘플링하세요. 이 테일 기반 방식은 실제로 디버깅하는 드문 이벤트를 보존하지만, 균일 무작위 샘플링은 지루한 트래픽과 같은 비율로 버립니다. 하루 10만 요청 이하라면 샘플링 자체를 건너뛰고 전부 로깅하세요.

LLM 로그는 얼마나 오래 보관해야 하나요?

등급을 나누세요. 실시간 디버깅용 Hot 7일, 장애 리뷰와 비용 분석용 Warm 30일, 컴플라이언스와 감사용 Cold는 압축 오브젝트 스토리지에 최대 1년. GDPR 저장 제한 원칙은 개인 데이터를 필요한 기간 이상 보관하는 것을 금지하므로, 각 등급에 수동 정리가 아닌 자동 삭제를 묶으세요.

LLM 로깅과 LLM 트레이싱의 차이는 무엇인가요?

로그는 이벤트 하나의 평면 레코드입니다. 이 요청이 이 필드들로 일어났다. 트레이스는 전체 요청 경로에 걸친 스팬의 인과 트리입니다. 이를테면 검색, 그다음 모델 호출, 그다음 도구 호출 두 번 같은 식입니다. 로그는 무엇을 알려주고, 트레이스는 어디서와 왜를 알려줍니다. 프로덕션 구성은 둘 다 발행하며 trace_id로 조인됩니다.

결론

정리하면 이렇습니다. 모든 요청을 네임드 필드 JSON으로 로깅하고, 비용은 요청 시점에 계산하고, OTel 트레이스 컨텍스트를 붙이고, PII는 쓰기 전에 마스킹하고, 하루 10만 요청을 넘기면 결과별로 샘플링하고, 실제로 쿼리할 수 있는 저장소를 고르세요. 아홉 규칙은 스프린트당 하나씩 도입할 수 있도록 순서를 정했고, 규칙 2, 4, 5가 가장 빠르게 투자 대비 효과를 돌려주는 세 가지입니다. 로그 주변의 더 넓은 모니터링 스택을 고르는 중이라면 최고의 AI 옵저버빌리티 플랫폼 라운드업부터 시작하세요. 그리고 LLM 스택의 구조화 로깅을 연결하는 데 도움이 필요하다면 무료 상담을 받으세요.

태그

llm 로깅 모범 사례구조화 로깅opentelemetry genaipii 마스킹llm 옵저버빌리티로그 보관 정책

이 기사 공유하기

프로젝트 시작하기

새로운 것을 만들 준비가 되었다면 특별함은?

여러분의 비전을 현실로 만들어 보세요. 차이를 만드는 소프트웨어, 우리 팀이 함께 만들겠습니다.