<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>OTLP on jyukki's Blog</title><link>https://jyukki.com/tags/otlp/</link><description>Recent content in OTLP on jyukki's Blog</description><generator>Hugo -- 0.147.0</generator><language>ko-KR</language><lastBuildDate>Tue, 01 Sep 2026 10:06:00 +0900</lastBuildDate><atom:link href="https://jyukki.com/tags/otlp/index.xml" rel="self" type="application/rss+xml"/><item><title>2026 개발 트렌드: OpenTelemetry Go Logs RC, 로그 상관관계가 라이브러리 관행에서 SDK 호환성 계약으로 이동한다</title><link>https://jyukki.com/posts/2026-09-01-opentelemetry-go-logs-rc-correlation-contract-trend/</link><pubDate>Tue, 01 Sep 2026 10:06:00 +0900</pubDate><guid>https://jyukki.com/posts/2026-09-01-opentelemetry-go-logs-rc-correlation-contract-trend/</guid><description>OpenTelemetry Go v1.47.0-rc.1의 Logs API·SDK release candidate를 계기로, 애플리케이션 로그를 단순 수집 대상이 아니라 trace context·resource·backpressure를 함께 검증하는 관측성 계약으로 운영하는 기준을 정리합니다.</description><content:encoded><![CDATA[<p>OpenTelemetry Go가 2026년 8월 31일 Logs API와 SDK를 v1.47.0-rc.1으로 올렸습니다. 대상은 go.opentelemetry.io/otel/log와 go.opentelemetry.io/otel/sdk/log 두 모듈입니다. beta였던 API와 SDK가 stable v1 호환성 약속 직전의 RC 단계로 들어갔고, 최소 14일 동안 실제 애플리케이션과 integration에서 피드백을 받습니다. 반면 log exporter와 logtest 모듈은 이번 안정성 범위에 포함되지 않습니다.</p>
<p>이 소식은 Go에서도 OpenTelemetry로 로그를 낼 수 있다는 기능 추가보다 운영상 더 큰 의미가 있습니다. trace와 metric은 provider, resource, processor, exporter, shutdown을 SDK 계약으로 다뤄 왔지만, 로그는 언어별 logger와 file/stdout agent의 관행에 더 강하게 묶여 있었습니다. 이제 Go 서비스는 logger bridge를 포함한 로그 경로를 상관관계, backpressure, lifecycle, attribute 한도까지 관측성 계약으로 시험할 수 있는 지점에 왔습니다.</p>
<p>이 글은 <a href="/posts/2026-08-29-opentelemetry-declarative-config-versioned-contract-trend/">OpenTelemetry 선언적 구성과 버전 계약</a>, <a href="/posts/2026-08-16-opentelemetry-blueprints-governed-observability-trend/">OpenTelemetry Blueprints와 관측성 운영 계약</a>, <a href="/learning/deep-dive/deep-dive-distributed-tracing-adoption-playbook/">분산 트레이싱 도입 플레이북</a>, <a href="/learning/deep-dive/deep-dive-structured-logging/">구조화 로깅 실무</a>의 다음 단계입니다. 앞선 글이 telemetry 설정·템플릿·trace propagation을 다뤘다면, 여기서는 가장 오래된 신호인 로그를 같은 신뢰 경계 안에 넣을 때의 기준을 다룹니다.</p>
<p>참고한 공식 자료:</p>
<ul>
<li><a href="https://opentelemetry.io/blog/2026/go-logs-api-sdk-rc/">OpenTelemetry: Go Logs API and SDK reach RC</a></li>
<li><a href="https://opentelemetry.io/docs/specs/otel/logs/">OpenTelemetry Logs 개요</a></li>
<li><a href="https://opentelemetry.io/docs/specs/otel/logs/data-model/">OpenTelemetry Logs Data Model</a></li>
<li><a href="https://opentelemetry.io/docs/languages/go/instrumentation/">OpenTelemetry Go instrumentation 문서</a></li>
</ul>
<h2 id="이-글에서-얻는-것">이 글에서 얻는 것</h2>
<ul>
<li>이번 RC에 포함된 API·SDK와 아직 experimental인 모듈을 구분하고, production 도입 범위를 판단합니다.</li>
<li>trace ID를 로그 문자열에 붙이는 수준을 넘어 resource, severity, span context, processor lifecycle을 하나의 correlation contract로 설계합니다.</li>
<li>bounded queue와 비동기 export가 만드는 성능·유실·종료 trade-off를 숫자로 검증하는 방법을 얻습니다.</li>
</ul>
<h2 id="핵심-개념이슈">핵심 개념/이슈</h2>
<h3 id="1-로그는-새-포맷으로-갈아타는-대상이-아니라-기존-생태계를-연결하는-신호다">1) 로그는 새 포맷으로 갈아타는 대상이 아니라 기존 생태계를 연결하는 신호다</h3>
<p>OpenTelemetry Logs의 방향은 모든 팀이 당장 기존 logger를 버리고 하나의 API만 쓰라는 뜻이 아닙니다. 공식 Logs 문서도 로그에는 이미 언어 표준 라이브러리, Logrus·Zap 같은 logger, stdout/file 수집기, SaaS agent가 넓게 존재한다는 점을 전제로 합니다. 목표는 이 기존 생태계가 trace·metric과 약하게 연결되던 문제를 줄이는 것입니다.</p>
<p>로그 data model은 application file, system log, third-party log를 공통 record로 표현하고, record에 timestamp·observed timestamp·severity·body·resource·instrumentation scope·attribute·trace context를 둘 수 있게 합니다. 그중 운영 가치가 큰 것은 같은 request context에서 나온 trace ID와 span ID, 같은 실행 단위의 resource attribute를 다른 signal과 동일하게 보게 하는 것입니다.</p>
<p>예를 들어 장애 대응자가 결제 실패 로그를 볼 때 필요한 것은 message 전문 하나가 아닙니다. 다음 질문을 즉시 따라갈 수 있어야 합니다.</p>
<ol>
<li>이 로그는 어떤 service.name, version, environment, pod에서 나왔는가?</li>
<li>어느 trace와 span의 어느 시점에 발생했는가?</li>
<li>같은 trace의 downstream timeout, retry, DB error metric과 연결되는가?</li>
<li>같은 error code가 어느 release·tenant·route에 집중되는가?</li>
</ol>
<p>trace ID 문자열을 수동으로 추가하는 방식도 첫 단계로는 쓸 수 있지만, logger마다 field 이름·누락·sampling 경로가 갈리기 쉽습니다. Collector가 log·trace·metric에 같은 Kubernetes resource를 일관되게 붙이고, application context가 trace·span 식별자를 record에 전달할 수 있어야 상관관계가 검색 편의가 아니라 데이터 계약이 됩니다.</p>
<h3 id="2-이번-rc의-안정성-범위가-곧-도입-범위는-아니다">2) 이번 RC의 안정성 범위가 곧 도입 범위는 아니다</h3>
<p>RC에는 go.opentelemetry.io/otel/log와 go.opentelemetry.io/otel/sdk/log가 포함됩니다. beta v0.22.0에서 coordinated version인 v1.47.0-rc.1로 이동했고, root go.opentelemetry.io/otel에 Logger, GetLoggerProvider, SetLoggerProvider도 추가됐습니다. 기존 otel/log/global의 동등 API는 deprecated됐습니다.</p>
<p>하지만 이 사실만으로 모든 로그 도구가 stable인 것은 아닙니다. exporter와 logtest는 experimental이며, RC는 stable v1 호환성 약속 전의 검증 단계입니다. 공식 발표도 direct API, logging bridge, custom processor/exporter, high-volume workload, attribute limit, application shutdown을 지금 시험해 달라고 명시합니다.</p>
<table>
  <thead>
      <tr>
          <th>층</th>
          <th>RC에서 검증할 것</th>
          <th>아직 고정하지 말 것</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>애플리케이션 API</td>
          <td>provider 생성, context correlation, record field, global API migration</td>
          <td>전사 공용 API의 장기 ABI</td>
      </tr>
      <tr>
          <td>SDK lifecycle</td>
          <td>batch queue, processor 순서, Flush와 Shutdown, 고부하 동작</td>
          <td>exporter 내부 동작에 대한 비문서 의존</td>
      </tr>
      <tr>
          <td>전송·시험 도구</td>
          <td>현재 OTLP Collector와 실제 backend에서의 전송·검색 결과</td>
          <td>experimental exporter와 logtest의 안정 API 가정</td>
      </tr>
  </tbody>
</table>
<p>이 구분은 팀을 느리게 하기 위한 것이 아닙니다. 지금 RC에서 호환성·성능 문제가 발견돼야 v1 안정화 뒤 breaking change 없이 고치기 어려운 계약이 줄어듭니다. 실험 코드를 production shared package에 바로 흡수하기보다, version pin된 canary와 재현 가능한 benchmark를 먼저 두는 이유입니다.</p>
<h3 id="3-bounded-queue는-로그가-빨라진다는-약속이-아니라-손실-경계를-명시하는-선택이다">3) bounded queue는 로그가 빨라진다는 약속이 아니라 손실 경계를 명시하는 선택이다</h3>
<p>공식 RC 설명에서 BatchProcessor는 bounded queue를 사용하고 exporter I/O 때문에 애플리케이션의 log emission이 기다리지 않도록 설계됩니다. hot path에서 heap allocation과 GC 압력을 줄이려는 최적화도 포함됩니다. 이는 request thread가 느린 Collector나 backend 때문에 멈추지 않게 한다는 점에서 중요합니다.</p>
<p>대신 queue는 무한 버퍼가 아닙니다. exporter가 느리거나 네트워크가 막히고 로그 유입이 계속되면, 운영자는 어느 정도의 로그가 언제부터 버려지거나 지연되는가를 알아야 합니다. export 성공률 하나만 보면 안 됩니다. exporter가 정상으로 돌아온 뒤에도 앞선 burst가 사라졌을 수 있기 때문입니다.</p>
<table>
  <thead>
      <tr>
          <th>항목</th>
          <th>초기 gate 예시</th>
          <th>판단 이유</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>correlation coverage</td>
          <td>오류 로그의 99% 이상이 trace ID 또는 명시적 non-request 사유를 가짐</td>
          <td>요청 경로의 고립 로그를 줄임</td>
      </tr>
      <tr>
          <td>필수 resource 누락</td>
          <td>0.1% 미만</td>
          <td>service와 environment가 없으면 signal 조인이 불안정</td>
      </tr>
      <tr>
          <td>processor queue saturation</td>
          <td>7일 canary에서 0회 또는 원인별 재현 가능</td>
          <td>exporter 병목을 조용히 숨기지 않음</td>
      </tr>
      <tr>
          <td>log export/drop</td>
          <td>기존 pipeline 대비 +0.1%p 이내</td>
          <td>관측성 변경이 장애 증거를 줄이지 않음</td>
      </tr>
      <tr>
          <td>request CPU/latency</td>
          <td>baseline 대비 +5% 이내 또는 사전 승인 예산</td>
          <td>고볼륨 API의 application SLO를 보호</td>
      </tr>
      <tr>
          <td>shutdown flush</td>
          <td>deploy와 termination test 100회 중 누락 0건</td>
          <td>재시작 시 마지막 오류 로그를 잃지 않음</td>
      </tr>
  </tbody>
</table>
<p>수치는 보수적인 출발점이지 OpenTelemetry가 보장하는 SLA가 아닙니다. 로그량, retention 비용, SLO, Collector topology에 맞춰 조정해야 합니다. 중요한 점은 어떤 값을 고르든 queue를 키워 문제를 미루는 것보다, 어느 로그를 얼마나 잃어도 되는지와 backpressure 때 어떤 신호로 즉시 조사할지를 먼저 합의하는 것입니다.</p>
<h3 id="4-attribute-정책이-없으면-correlation은-pii와-비용의-통로가-된다">4) attribute 정책이 없으면 correlation은 PII와 비용의 통로가 된다</h3>
<p>record에 attribute를 자유롭게 넣을 수 있다는 것은 검색·분석에는 강점이지만, user.email, session token, Authorization header, 전체 request body, 고유 URL path 같은 값을 무제한 붙일 이유는 아닙니다. trace·metric에서 같은 문제가 발생하듯 log에서도 cardinality와 민감정보가 증폭됩니다.</p>
<p>각 log attribute에는 최소한 owner, 목적, 허용값 또는 cardinality, 개인정보 등급, retention, query 소비처를 둡니다. 예를 들면 http.response.status_code와 error.type은 저위험·저카디널리티이며 공통 contract 후보입니다. 반면 raw user.id는 해시·bucket·권한 분리 없이는 기본 금지로 두는 편이 낫습니다. <a href="/learning/deep-dive/deep-dive-metric-cardinality-budget-label-governance-playbook/">메트릭 카디널리티 예산과 라벨 거버넌스</a>의 label 예산과 같은 원칙을 로그에도 적용해야 합니다.</p>
<h2 id="실무-적용">실무 적용</h2>
<h3 id="1-한-서비스에서-legacy-pipeline과-나란히-검증한다">1) 한 서비스에서 legacy pipeline과 나란히 검증한다</h3>
<p>첫 적용은 logger를 한꺼번에 바꾸는 migration이 아닙니다. Go 서비스 한 개와 대표 경로 세 개를 고릅니다.</p>
<ol>
<li>정상 HTTP 요청: inbound span, application log, outbound dependency log가 같은 trace에 연결되는지 확인합니다.</li>
<li>오류 요청: error status, error type, severity, stack 정보의 정책적 범위가 유지되는지 확인합니다.</li>
<li>취소·timeout 요청: context cancellation 뒤 로그가 중복되거나 flush되지 않는지 확인합니다.</li>
</ol>
<p>기존 stdout/file pipeline을 즉시 끄지 않고, RC pipeline을 staging 또는 production 5% canary에 병행합니다. 동일한 request sample에서 record 수, correlation coverage, resource completeness, ingest bytes, backend query 결과, PII scan 결과를 비교합니다. 이 방식이면 OTLP에 도착했다가 아니라 장애 때 찾던 증거가 더 잘 남았는가를 검증할 수 있습니다.</p>
<p>최소 log contract 예시는 아래처럼 작게 시작합니다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>required resource: service.name, service.version, deployment.environment.name
</span></span><span style="display:flex;"><span>required request context: trace_id, span_id 또는 reason=no_active_span
</span></span><span style="display:flex;"><span>required event fields: severity, event.name 또는 stable message, error.type(오류 시)
</span></span><span style="display:flex;"><span>default deny: authorization, cookie, session, raw body, raw email, unbounded identifier
</span></span></code></pre></div><p>이 contract는 각 logger 형식을 통일하라는 명령이 아닙니다. bridge, Collector transform, direct Logs API 중 어느 방식을 써도 backend에서 같은 의미로 해석할 최소 필드를 정하는 일입니다.</p>
<h3 id="2-lifecycle과-deploy를-함께-시험한다">2) lifecycle과 deploy를 함께 시험한다</h3>
<p>로그는 프로세스가 끝나는 순간 가장 필요할 때가 많습니다. deploy, autoscaling down, panic recovery, SIGTERM 뒤에 남는 마지막 error log를 못 보내면 postmortem이 약해집니다. BatchProcessor를 붙였다면 graceful shutdown 순서를 서비스 runbook에 넣어야 합니다.</p>
<p>권장 순서는 새 요청 차단, in-flight request의 deadline 대기, logger/provider flush, exporter와 Collector 전송 확인, process 종료입니다. 현실에서는 deadline을 무한히 기다릴 수 없으므로 shutdown budget도 정합니다. 예를 들어 Pod termination grace period가 30초라면 application의 log flush는 3~5초 안에 끝나도록 예산을 분리하고, 넘으면 timeout과 drop reason을 metric으로 남깁니다. <a href="/learning/deep-dive/deep-dive-end-to-end-deadline-cancellation-playbook/">종단간 Deadline과 Cancellation Propagation</a>에서처럼 남은 시간 예산이 없는 flush retry는 가용성을 더 나쁘게 만들 수 있습니다.</p>
<h3 id="3-rc-feedback-window에는-실패-경로를-의도적으로-만든다">3) RC feedback window에는 실패 경로를 의도적으로 만든다</h3>
<p>이번 RC는 발표 뒤 최소 14일의 feedback window를 둡니다. 이 기간에 정상 트래픽만 보고 stable을 기다리면 가장 중요한 검증을 놓칩니다. 다음 항목은 staging에서 의도적으로 재현할 가치가 있습니다.</p>
<ul>
<li>Collector endpoint 지연·일시 장애를 주고 application p95, processor queue, drop과 recovery를 관찰</li>
<li>attribute limit을 넘기는 로그와 큰 body를 만들어 limit·redaction·오류 처리 확인</li>
<li>custom processor 또는 logger bridge를 통해 context·severity·resource가 바뀌지 않는지 비교</li>
<li>100회 이상의 start/stop loop에서 flush 완료와 종료 시간 확인</li>
<li>root package API로 migration한 코드와 deprecated global API가 섞였을 때 provider ownership을 점검</li>
</ul>
<p>발견한 문제는 RC version, Go version, 최소 재현 코드, 기대와 실제 동작을 붙여 upstream에 공유할 수 있습니다. 실제 workload 증거를 가진 feedback은 API가 stable된 뒤 팀별 fork나 patch를 줄이는 가장 값싼 시점입니다.</p>
<h2 id="트레이드오프주의점">트레이드오프/주의점</h2>
<p>첫째, direct Logs API가 생겨도 기존 logger bridge를 모두 제거할 필요는 없습니다. bridge는 migration 비용을 낮추지만, field mapping·context injection·level 변환이 추가되는 만큼 end-to-end test가 필요합니다. 반대로 새 API만 강제하면 운영 도구와 팀 습관을 깨면서 adoption이 늦어질 수 있습니다.</p>
<p>둘째, bounded batch queue는 application latency를 보호하지만 로그를 무한 보존하지 않습니다. queue를 과도하게 키우면 memory와 종료 시간이 커지고, 작게 잡으면 burst에서 증거를 잃습니다. signal별 우선순위, error log 보존, sampling과 drop 정책을 명시하지 않으면 이 선택은 장애 때 임의로 보입니다.</p>
<p>셋째, RC의 API·SDK와 experimental exporter/logtest를 같은 성숙도로 취급하면 upgrade 비용을 잘못 계산합니다. 이번 단계에서는 version pin, staging canary, rollback, release note 검토를 유지하고 stable 발표 뒤에도 실제 pipeline compatibility를 별도 확인해야 합니다.</p>
<p>넷째, log·trace correlation이 좋아지면 개인 데이터도 더 쉽게 연결될 수 있습니다. 관측 가능하다와 모아도 된다는 다른 질문입니다. resource와 attribute allowlist, access control, retention, redaction 검증을 함께 도입해야 합니다.</p>
<h2 id="체크리스트-또는-연습">체크리스트 또는 연습</h2>
<h3 id="체크리스트">체크리스트</h3>
<ul>
<li><input disabled="" type="checkbox"> RC 안정성 범위인 API·SDK와 experimental exporter/logtest를 문서에서 구분했다.</li>
<li><input disabled="" type="checkbox"> service.name, version, environment와 trace/span correlation의 필수·예외 규칙이 있다.</li>
<li><input disabled="" type="checkbox"> high-volume, exporter 지연, attribute limit, custom bridge, shutdown을 포함한 test fixture가 있다.</li>
<li><input disabled="" type="checkbox"> queue saturation, export/drop, correlation coverage, resource 누락, shutdown flush를 함께 본다.</li>
<li><input disabled="" type="checkbox"> 민감정보와 고카디널리티 attribute의 default deny·redaction·retention 정책이 있다.</li>
<li><input disabled="" type="checkbox"> 기존 stdout/file pipeline을 끄기 전 parallel canary와 rollback 기준을 통과했다.</li>
<li><input disabled="" type="checkbox"> root otel package의 global logger API와 deprecated API 혼용 여부를 점검했다.</li>
</ul>
<h3 id="연습">연습</h3>
<ol>
<li>Go 서비스 하나의 오류 로그 100개를 표본으로 잡아 trace ID, span ID, service.name, environment, error type의 현재 누락률을 계산해 보세요.</li>
<li>Collector를 60초간 느리게 만든다는 가정으로 queue, request latency, drop, recovery에 대한 canary 중단 기준을 각각 하나씩 정해 보세요.</li>
<li>배포 종료를 30초로 제한하고, request drain·logger flush·exporter 종료에 나눠 줄 시간을 runbook으로 작성한 뒤 20회 반복 시험해 보세요.</li>
</ol>
<h2 id="관련-글">관련 글</h2>
<ul>
<li><a href="/posts/2026-08-29-opentelemetry-declarative-config-versioned-contract-trend/">OpenTelemetry 선언적 구성과 버전 계약</a></li>
<li><a href="/posts/2026-08-16-opentelemetry-blueprints-governed-observability-trend/">OpenTelemetry Blueprints와 관측성 운영 계약</a></li>
<li><a href="/learning/deep-dive/deep-dive-distributed-tracing-adoption-playbook/">분산 트레이싱 도입 플레이북</a></li>
<li><a href="/learning/deep-dive/deep-dive-structured-logging/">구조화 로깅 실무</a></li>
<li><a href="/learning/deep-dive/deep-dive-metric-cardinality-budget-label-governance-playbook/">메트릭 카디널리티 예산과 라벨 거버넌스</a></li>
</ul>
]]></content:encoded></item></channel></rss>