LabHub
배우기 러닝패스 코스

OTCA — OpenTelemetry Certified Associate

Seven Instruments, Twelve LogRecord Fields, and the Schema URL

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

메트릭 API 의 계측기(instrument)는 Counter·UpDownCounter·Gauge·Histogram 과 그 비동기(callback) 변형으로 나뉘고, SDK 가 내보내는 합(Sum)에는 delta 또는 cumulative 라는 temporality 가 붙습니다. 로그 시그널은 열두 개 필드를 가진 LogRecord 데이터 모델과, 기존 로깅 라이브러리를 그 모델로 잇는 브리지 API 로 이루어집니다. 스키마 URL 은 시맨틱 컨벤션이 바뀌어도 소비자가 데이터를 해석할 수 있게 하는 버전 표식입니다. 출처는 메트릭 API·메트릭 데이터 모델·로그 데이터 모델·텔레메트리 스키마 스펙입니다.

Concept map: temporality · 동기 · 비동기 · View

왜 이게 필요했나

"요청 수" 와 "큐 길이" 와 "방 온도" 는 모두 숫자지만 성질이 다릅니다. 요청 수는 늘기만 하고, 큐 길이는 늘었다 줄고, 온도는 더해서 합칠 의미가 없습니다. 계측기 종류가 나뉘는 이유는 이 성질을 API 단계에서 밝혀 두어 SDK 가 알맞은 기본 집계를 고르게 하기 위해서입니다. 스펙 개요는 이것을 "원시 측정값을 기록하면 집계 방식은 최종 사용자가 고른다" 고 표현합니다.

로그는 반대 방향의 문제입니다. 이미 모든 언어에 로깅 라이브러리가 있고, 그 로그를 버리고 새 API 로 다시 쓰라고 할 수는 없습니다. 그래서 로그 시그널은 "기존 로그를 어떻게 공통 모델로 옮기고 트레이스와 이어 줄 것인가" 에서 출발합니다. 스키마도 같은 종류의 문제입니다 — 시맨틱 컨벤션의 속성 이름이 바뀌면 그 이름을 기대하던 대시보드와 백엔드가 깨집니다.

어떻게 동작하나

계측기 일곱 종류

계측기는 이름·종류·단위(선택)·설명(선택)으로 정의됩니다. 개념 문서와 API 스펙의 설명을 합치면 이렇습니다.

계측기 동기/비동기 성질 기록 연산
Counter 동기 단조 증가(늘기만 함) Add
Asynchronous Counter 비동기 단조 증가, 관찰 시점의 누적값 보고 콜백
UpDownCounter 동기 증가·감소 모두(큐 길이) Add
Asynchronous UpDownCounter 비동기 더해서 합칠 수 있는 값(프로세스 힙 크기) 콜백
Gauge 동기 더할 수 없는 값(소음 수준), 바뀔 때 기록 Record
Asynchronous Gauge 비동기 더할 수 없는 값(방 온도), 관찰 시점에 보고 콜백
Histogram 동기 분포가 의미 있는 값(요청 지연) Record

동기 계측기는 애플리케이션 로직 안에서 직접 호출되고, 그 측정값은 Context(즉 현재 스팬)와 연결될 수 있습니다. 비동기 계측기는 콜백을 등록해 두면 SDK 가 수집(collection) 시점에만 부릅니다 — 스펙의 예는 15초마다 센서 온도를 읽는 것입니다. 비동기 측정값은 Context 와 연결되지 않습니다. 스펙은 여기서의 동기·비동기가 프로그래밍의 async 패턴과 무관하다고 덧붙입니다. UpDownCounter 의 설명에는 "값이 단조 증가라면 Counter 를 쓰라" 는 지침이 있습니다.

각 계측기에는 기본 집계가 있고, View 로 바꾸거나 특정 계측기를 무시하거나 보고할 속성을 고를 수 있습니다. 개념 문서는 카디널리티 한계도 설명합니다 — 메트릭 스트림당 고유 속성 조합의 기본 상한은 2000 이며, 넘치면 측정값을 버리지 않고 otel.metric.overflow=true 속성 하나로 접습니다. 합계는 맞지만 접힌 측정값은 다른 속성을 잃으므로, 그 속성으로 거르는 질의는 적게 셉니다.

temporality — delta 와 cumulative

데이터 모델에서 Sum 은 AggregationTemporality(delta 또는 cumulative)와 단조(monotonic) 플래그, 그리고 시작·끝 시각을 가진 데이터 포인트들로 이루어집니다. delta 는 겹치지 않는 시간 창마다 그 구간의 값을 보고하고, cumulative 는 시작(보통 프로세스 시작)부터의 전체 합을 보고합니다. 단조 delta 합은 음수가 아니어야 하고, 단조 cumulative 합은 이전 값보다 작지 않아야 합니다.

스펙은 둘 사이의 절충으로 프로세스 재시작 감지, 비율(rate) 계산, 푸시/풀 방식을 들고, OTLP 가 둘 다 지원하며 필요하면 Delta-to-Cumulative 또는 Cumulative-to-Delta 변환을 할 수 있다고 적습니다. 히스토그램도 temporality 를 가지며, min·max 는 delta 에서 더 쓸모 있고 delta→cumulative 변환은 되지만 그 반대는 되지 않습니다. 개념 문서는 카디널리티와의 관계도 짚습니다 — delta 는 주기마다 상태를 지워 한 주기 안의 조합만 세지만, cumulative 는 상태를 유지하므로 한 번 상한에 닿으면 프로세스가 재시작될 때까지 계속 넘칩니다. Gauge 는 특정 시각의 표본값이라 temporality 가 없습니다.

로그 레코드의 필드

로그 데이터 모델은 "기존 로그 형식을 모호함 없이 이 모델로 옮길 수 있어야 한다" 는 요구에서 출발했고, 자주 쓰이는 필드는 이름 있는 최상위 필드로, 나머지는 Attributes 로 둡니다. 최상위 필드는 열두 개입니다.

필드
Timestamp 이벤트가 일어난 시각(원본 시계)
ObservedTimestamp 수집 시스템이 관찰한 시각. SDK 에서 만든 로그는 Timestamp 와 같음
TraceId / SpanId / TraceFlags W3C Trace Context 의 식별자와 플래그. 요청 처리 중의 로그에 붙음
SeverityText 원본의 로그 레벨 문자열
SeverityNumber 정규화된 심각도 숫자
Body 본문
Resource 로그를 낸 개체
InstrumentationScope 로그를 낸 범위(라이브러리·모듈)
Attributes 추가 정보
EventName 이벤트의 종류를 나타내는 이름

시각이 하나만 지원되는 곳으로 옮길 때는 Timestamp 가 있으면 그것을, 없으면 ObservedTimestamp 를 씁니다. SeverityNumber 는 1~24 범위로, 1–4 TRACE, 5–8 DEBUG, 9–12 INFO, 13–16 WARN, 17–20 ERROR, 21–24 FATAL 이며 0 은 미지정입니다. 스펙의 예처럼 17 은 20 보다 덜 심각한 오류입니다. Events 는 이 LogRecord 의 표준화된 형식이며, 로그 시맨틱 컨벤션은 Event 형식으로 정의됩니다.

브리지 API

로그 API는 첫 문장에서 대상을 밝힙니다 — "로깅 라이브러리 작성자가 로그 어펜더(appender)를 만들도록 제공되며, 어펜더는 이 API 로 기존 로깅 라이브러리와 OpenTelemetry 로그 데이터 모델 사이를 잇는다." 구조는 LoggerProvider 에서 Logger 를 얻고(name, 선택적으로 version·schema_url·attributes), Logger 가 LogRecord 를 Emit 하는 것이 전부입니다. Emit 은 Timestamp·ObservedTimestamp·Context·SeverityNumber·SeverityText·Body·Attributes·EventName 을 받고, Context 를 주지 않으면 현재 Context 를 씁니다 — 이것이 로그에 TraceId·SpanId 가 자동으로 붙는 원리입니다. Enabled 는 LogRecord 를 만드는 비용이 클 때만 먼저 물어보는 최적화이며, 호출이 필수는 아닙니다.

로그 시그널 개요는 수집 방식을 둘로 나눕니다. 파일이나 표준 출력으로 나온 로그를 Collector 가 읽어 파싱하는 방식(애플리케이션 변경이 거의 없지만 파싱이 어렵다)과, 어펜더를 붙여 OTLP 로 직접 보내는 방식(구조화가 잘 되고 파일·회전·파싱이 사라지지만 OTLP 를 받는 목적지가 필요하다)입니다. 어느 쪽이든 애플리케이션 개발자는 시작 시점에 어펜더와 SDK 를 설정하기만 하면 됩니다.

스키마 URL

시맨틱 컨벤션은 진화하고, 텔레메트리 소스와 소비자는 서로 다른 속도로 바뀝니다. 스키마 스펙은 세 당사자를 분리하기 위해 다음을 정합니다. 스키마는 버전이 있고(MAJOR.MINOR.PATCH), 버전 사이의 변환(예: 속성 이름 바꾸기)을 스키마 파일이 명시하며, 각 버전은 고유한 Schema URL 로 식별됩니다. URL 의 마지막 경로가 버전이고 그 앞이 스키마 패밀리입니다. 스키마 파일은 발행되면 불변이라 영구 캐시해도 됩니다. 소스(계측 라이브러리)는 내보내는 텔레메트리에 스키마 URL 을 넣고 — Tracer·Meter·Logger 를 얻을 때의 schema_url 인자가 그 자리입니다 — 소비자는 받은 스키마 버전을 보고 필요하면 자기가 기대하는 버전으로 변환합니다. 문서의 예는 1.2.0 의 deployment.environment 를 1.1.0 을 기대하는 백엔드가 environment 로 바꿔 저장하는 것이고, 백엔드가 스키마를 모르면 Collector 의 스키마 변환 프로세서가 대신 합니다. OpenTelemetry 자체 스키마는 /schemas/<version> 경로로 발행됩니다.

현장에서 만나는 모습

한 팀이 "현재 활성 연결 수" 를 Counter 로 기록해서 그래프가 오르기만 했습니다. 연결이 닫힐 때 줄어야 하니 UpDownCounter 가 맞았고, 실제로는 연결 풀에서 값을 읽는 콜백이 더 자연스러워 Asynchronous UpDownCounter 로 바꿨습니다.

다른 팀은 Prometheus 로 가는 메트릭과 벤더 백엔드로 가는 메트릭의 값이 다르다고 했는데, 한쪽은 cumulative, 다른 쪽은 delta 로 설정되어 있었습니다. 같은 Counter 라도 temporality 가 다르면 한 점의 숫자가 뜻하는 구간이 다릅니다.

다음 퀴즈에서 확인할 것

퀴즈에서는 API 와 SDK 를 분리한 이유, Batching processor 의 기본값, 동기·비동기 계측기의 차이와 Context 연결, delta 와 cumulative 의 뜻, LogRecord 의 Timestamp 와 ObservedTimestamp, 브리지 API 의 대상, 스키마 URL 의 역할을 묻습니다.