LLM WikiAccess-protected knowledge portal
← 스터디 홈
3편 · 약 15분

메트릭 API와 계측(Instrumentation)

메트릭이 답하는 질문

트레이스는 "요청 하나가 어디서 느렸는가"를 보여준다. 메트릭은 "지난 5분 동안 p99 레이턴시가 200ms를 넘은 적이 있는가", "현재 연결 풀에서 활성 연결이 몇 개인가"처럼 시스템의 상태를 연속적으로 보여준다. 개별 요청이 아니라 집계된 흐름을 보는 것이다.

OTel 메트릭 신호는 Prometheus, Datadog, Grafana Cloud 등 어느 메트릭 백엔드로도 내보낼 수 있도록, 측정 코드와 내보내기 방식을 분리한다.

MeterProvider → Meter → Instrument 계층

OTel 메트릭 API는 세 층으로 나뉜다.

MeterProvider SDK 설정, Reader, View, Exporter 등록 get_meter(name) Meter 계측 라이브러리 단위로 생성 create_*() Counter 단조 증가 요청 수, 바이트 [동기] UpDownCounter 증감 가능 큐 깊이, 연결 수 [동기] Histogram 분포 측정 레이턴시, 페이로드 크기 [동기] Gauge 현재 값 스냅샷 CPU 사용률, 온도 [동기] Observable* 콜백 기반 메모리, GC 통계 [비동기]
OTel 메트릭 API 계층 구조

MeterProvider는 SDK 전체 설정의 진입점이다. MetricReader(수집 주기), View(집계 규칙), Exporter를 여기에 등록한다. 전역 MeterProvider를 설정해두면 어디서든 metrics.get_meter()로 Meter를 얻을 수 있다.

Meter는 하나의 계측 단위(라이브러리, 모듈)를 대표한다. Meter를 얻을 때 이름과 버전을 넘겨서 어떤 코드에서 발생한 메트릭인지 식별한다.

Instrument는 실제로 값을 측정하는 객체다.

여섯 가지 인스트루먼트

동기 인스트루먼트 (Synchronous)

애플리케이션 코드 흐름 안에서 직접 값을 기록한다.

인스트루먼트특성기본 집계대표 사용 사례
Counter단조 증가(양수만)Sum (Cumulative)HTTP 요청 수, 처리된 메시지 수
UpDownCounter증가·감소 모두 가능Sum큐 대기 항목 수, 활성 연결 수
Histogram값의 분포 기록ExplicitBucketHistogram레이턴시(ms), 응답 크기(bytes)
Gauge현재 순간값 기록LastValueCPU 사용률, 현재 온도
from opentelemetry import metrics

meter = metrics.get_meter("order-service", version="1.0")

# Counter: 요청마다 1씩 증가
request_counter = meter.create_counter(
    "http.server.request.count",
    unit="1",
    description="처리한 HTTP 요청 수",
)
request_counter.add(1, {"http.method": "POST", "http.route": "/api/orders"})

# Histogram: 레이턴시 분포 기록
latency_histogram = meter.create_histogram(
    "http.server.request.duration",
    unit="ms",
    description="HTTP 요청 처리 시간",
)
latency_histogram.record(142, {"http.route": "/api/orders", "http.status_code": 200})

# UpDownCounter: 큐 깊이 추적
queue_depth = meter.create_up_down_counter(
    "job.queue.depth",
    unit="1",
    description="대기 중인 작업 수",
)
queue_depth.add(1)   # 작업 추가
queue_depth.add(-1)  # 작업 처리 완료

비동기 인스트루먼트 (Asynchronous / Observable)

SDK가 수집 주기마다 콜백 함수를 호출해서 값을 가져간다. 측정 시점에 코드가 실행 중이지 않아도 되는 시스템 리소스 등에 적합하다.

인스트루먼트대응 동기 버전대표 사례
ObservableCounterCounter네트워크 I/O 누적 바이트, GC 실행 횟수
ObservableUpDownCounterUpDownCounter힙 메모리 사용량
ObservableGaugeGaugeCPU 사용률, 파일 디스크립터 수
import psutil

def cpu_callback(options):
    yield metrics.Observation(psutil.cpu_percent(), {"cpu.core": "total"})

meter.create_observable_gauge(
    "system.cpu.utilization",
    callbacks=[cpu_callback],
    unit="%",
    description="CPU 사용률",
)

집계(Aggregation)와 임시성(Temporality)

집계 유형

집계설명기본으로 사용하는 인스트루먼트
Sum측정값의 합Counter, UpDownCounter
LastValue마지막으로 기록한 값Gauge, ObservableGauge
ExplicitBucketHistogram미리 정의한 버킷 경계로 분포 표현Histogram (기본)
ExponentialHistogram지수 스케일 버킷으로 분포 압축 저장Histogram (선택)
Drop해당 메트릭 폐기View로 명시적 지정

임시성(Temporality)

메트릭 데이터를 어떻게 누적해서 보고하는지에 관한 개념이다.

  • Cumulative(누적): 앱 시작 이후 전체 합계를 항상 보고한다. 수신 측에서 차이를 계산한다. Prometheus가 선호하는 방식.
  • Delta(증분): 이전 수집 이후 변화량만 보고한다. Datadog, OTLP 기본 Consumer가 선호하는 방식.

SDK는 기본으로 Cumulative를 사용하지만, Exporter 설정에서 변경할 수 있다.

View: 집계 규칙 커스터마이징

View는 특정 인스트루먼트에 대한 집계 방식을 덮어쓰는 규칙이다. 히스토그램 버킷 경계를 서비스 특성에 맞게 바꾸거나, 고카디널리티 속성을 제거하거나, 사용하지 않는 메트릭을 완전히 버릴 수 있다.

from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.view import View
from opentelemetry.sdk.metrics.export import (
    ConsoleMetricExporter, PeriodicExportingMetricReader,
)
from opentelemetry.sdk.metrics.aggregation import ExplicitBucketHistogramAggregation

# 레이턴시 히스토그램: API 서비스에 맞는 버킷 경계 지정
latency_view = View(
    instrument_name="http.server.request.duration",
    aggregation=ExplicitBucketHistogramAggregation(
        boundaries=[5, 10, 25, 50, 100, 250, 500, 1000, 2500]
    ),
)

# 디버그 속성 제거 — 카디널리티 절감
filtered_view = View(
    instrument_name="db.client.operation.duration",
    attribute_keys={"db.system", "db.operation"},  # 이 속성만 유지
)

reader = PeriodicExportingMetricReader(ConsoleMetricExporter(), export_interval_millis=30000)
provider = MeterProvider(metric_readers=[reader], views=[latency_view, filtered_view])

Exemplar: 메트릭에서 트레이스로 이동하기

Exemplar는 집계된 히스토그램 버킷 안에서 실제 측정 시점의 trace_idspan_id를 함께 저장하는 기능이다. Grafana 같은 UI에서 느린 버킷을 클릭하면 그 구간의 실제 트레이스로 바로 이동할 수 있다.

SDK가 활성 스팬 안에서 histogram.record()를 호출할 때 자동으로 현재 컨텍스트의 trace_id를 첨부한다. 별도 코드 없이 활성화된다.

SDK 파이프라인 전체 흐름

애플리케이션
counter.add()
histogram.record()
OTel SDK
Measurement
값 + 속성 + 컨텍스트
View / Aggregation
버킷, 필터, 집계
MetricReader
수집 주기(30s 등)
Exporter
OTLP / Prometheus
/ Console
백엔드
Prometheus
Datadog, etc.
메트릭 SDK 파이프라인

측정 값은 SDK 내부에서 View 규칙에 따라 집계된 뒤, MetricReader가 설정한 주기마다 Exporter를 통해 내보내진다. 트레이스의 SpanProcessor처럼 메트릭도 SDK 레이어에서 처리·필터·변환이 이루어진다.

자동 계측이 만들어주는 메트릭

opentelemetry-instrumentation-* 패키지를 사용하면 HTTP 서버·클라이언트, DB 드라이버에 대한 메트릭이 자동으로 생성된다. OTel 시맨틱 컨벤션이 정의한 이름과 속성을 따른다.

자동 메트릭설명
http.server.request.durationHTTP 요청 처리 시간(히스토그램)
http.client.request.duration아웃바운드 HTTP 레이턴시
db.client.operation.durationDB 쿼리 실행 시간
messaging.process.duration메시지 처리 시간

이 이름들은 Grafana 대시보드 템플릿과 Prometheus 알림 규칙이 기본으로 참조하기 때문에, 자동 계측만 활성화해도 표준 대시보드가 곧바로 동작한다.

한 줄 정리

MeterProvider에 View와 Reader를 등록하고, Meter에서 인스트루먼트를 생성해 값을 기록하면 된다. 히스토그램 버킷 경계는 View로 조정하고, Exemplar로 느린 버킷에서 실제 트레이스까지 바로 이동할 수 있다.

References

  • https://opentelemetry.io/docs/concepts/signals/metrics/
  • https://opentelemetry.io/docs/specs/otel/metrics/api/
  • https://opentelemetry.io/docs/specs/otel/metrics/sdk/
  • https://opentelemetry.io/docs/specs/otel/metrics/data-model/
  • https://opentelemetry-python.readthedocs.io/en/latest/api/metrics.html
  • https://last9.io/blog/opentelemetry-metrics-aggregation/
  • https://oneuptime.com/blog/post/2026-02-20-opentelemetry-metrics-guide/view
  • https://oneuptime.com/blog/post/2025-09-22-connecting-metrics-to-traces-with-exemplars/view