OpenMetrics 1.0 사양

OpenMetrics 1.0 사양 (OpenMetrics 1.0 specification)

OpenMetrics 1.0의 공식 사양 문서예요. 프로메테우스의 널리 쓰이는 텍스트 exposition 형식 0.0.4를 기반으로, 그것을 정리하고 엄격하게 다듬어 IETF 표준으로 가져가려는 의도를 담고 있어요. 메트릭을 어떤 데이터 모델로 표현하고, 어떤 텍스트·protobuf 와이어 형식으로 교환해야 하는지, 그리고 이름 짓기·단위·보안 같은 설계 지침까지 한 문서에 모두 정의돼 있어요.

exporter 개발자나 OpenMetrics 형식 파서·수집기를 만드는 사람에게 가장 권위 있는 참고 문서랍니다. "MUST", "SHOULD" 같은 RFC 2119 키워드를 따라 정확한 요구사항을 표현하고 있어서, 구현할 때 그대로 따르면 되는 구조예요.

출처: 문서

본문

  • 버전: 1.0
  • 상태: 발행됨(Published)
  • 날짜: 2020년 11월
  • 저자: Richard Hartmann, Ben Kochie, Brian Brazil, Rob Skillington

2012년에 만들어진 프로메테우스는 2015년부터 클라우드 네이티브 관측성의 기본 도구가 됐어요. 프로메테우스 설계의 핵심 부분은 텍스트 메트릭 exposition 형식이며, 2014년부터 안정된 Prometheus exposition format 0.0.4라고 불러요. 이 형식에서는 생성, 수집, 사람이 이해하기 쉽도록 특별히 신경을 썼어요. 2020년 기준으로 공개적으로 등록된 exporter가 700개 이상 있고, 등록되지 않은 exporter는 알 수 없는 수만큼 있으며, 이 형식을 사용하는 수천 개의 네이티브 라이브러리 연동이 있어요. 다양한 프로젝트와 회사의 수십 개 수집기가 이를 소비하도록 지원해요.

OpenMetrics로 우리는 IETF로 가져가려는 명확한 목적을 가지고 사양을 정리하고 강화하고 있어요. 광범위하고 유기적으로 채택된 실무 표준을 문서화하면서 최소한의, 대체로 역호환 가능하며 잘 고려된 변경을 도입해요. 2020년 기준으로 수십 개의 exporter, 연동, 수집기가 이미 OpenMetrics를 사용하고 우선적으로 협상해요.

광범위한 채택과 생태계의 상당한 조정 요구사항을 고려할 때, Prometheus exposition format 0.0.4나 OpenMetrics 1.0에 대한 전면적인 변경은 범위 밖으로 간주돼요.

참고: OpenMetrics 2.0 개발이 진행 중이에요. 프로메테우스 OM 2.0 작업 그룹에 참여하는 방법은 여기를 읽어보세요.

개요 (Overview)

메트릭은 특정한 종류의 텔레메트리 데이터예요. 일련의 데이터에 대한 현재 상태의 스냅샷을 나타내요. 개별 사건에 대한 기록이나 정보에 초점을 맞춘 로그나 이벤트와는 구별돼요.

OpenMetrics는 주로 와이어 형식이며, 그 형식에 대한 특정 전송과는 독립적이에요. 그 형식은 정기적으로 소비되고 연속적인 exposition에 걸쳐 의미가 있도록 기대돼요.

구현자는 주어진 프로세스나 장치에 대해 문서화된 URL에 대한 단순한 HTTP GET 요청에 응답해 OpenMetrics 텍스트 형식으로 메트릭을 노출해야(MUST) 해요. 이 엔드포인트는 "/metrics"라고 불러야(SHOULD) 해요. 구현자는 HTTP를 통해 운영자 구성 엔드포인트로 메트릭 세트를 정기적으로 푸시하는 것과 같은 다른 방식으로도 OpenMetrics 형식 메트릭을 노출할 수(MAY) 있어요.

메트릭과 시계열

이 표준은 모든 시스템 상태를 수치 값으로 표현해요. 카운트, 현재 값, 열거, 불리언 상태가 흔한 예시예요. 메트릭과 달리 단일 사건은 특정 시간에 발생해요. 메트릭은 데이터를 시간적으로 집계하는 경향이 있어요. 이것은 정보를 잃을 수 있지만, 오버헤드 감소는 많은 현대 모니터링 시스템에서 흔히 선택되는 엔지니어링 트레이드오프예요.

시계열은 시간에 따라 변하는 정보의 기록이에요. 시계열은 임의의 문자열이나 이진 데이터를 지원할 수 있지만, 이 RFC의 범위에는 수치 데이터만 해당해요.

메트릭 시계열의 흔한 예는 네트워크 인터페이스 카운터, 장치 온도, BGP 연결 상태, 알림 상태예요.

데이터 모델

이 섹션은 ABNF 섹션과 함께 읽어야(MUST) 해요. 둘 사이에 불일치가 있으면 ABNF의 제약이 우선해야(MUST) 해요. 텍스트 와이어 형식이 지원되어야(MUST) 하므로 반복을 줄여줘요.

데이터 타입

값 (Values)

OpenMetrics의 메트릭 값은 부동 소수점이나 정수여야(MUST) 해요. 수집기가 float64만 지원할 수도(MAY) 있다는 점에 유의하세요. NaN, +Inf, -Inf 같은 비실수 값을 지원해야(MUST) 해요. NaN을 누락 값으로 간주해서는 안(MUST NOT) 되지만, 0으로 나누기를 신호하는 데 사용할 수(MAY) 있어요.

불리언 (Booleans)

불리언 값은 1==true, 0==false를 따라야(MUST) 해요.

타임스탬프 (Timestamps)

타임스탬프는 초 단위의 Unix Epoch여야(MUST) 해요. 음수 타임스탬프를 사용할 수(MAY) 있어요.

문자열 (Strings)

문자열은 유효한 UTF-8 문자로만 구성되어야(MUST) 하고 길이가 0일 수(MAY) 있어요. NULL(ASCII 0x0)을 지원해야(MUST) 해요.

레이블 (Label)

레이블은 문자열로 구성된 키-값 쌍이에요.

밑줄로 시작하는 레이블 이름은 RESERVED(예약)이며, 이 표준이 지정하지 않는 한 사용해서는 안(MUST NOT) 돼요. 레이블 이름은 ABNF 섹션의 제약을 따라야(MUST) 해요.

빈 레이블 값은 레이블이 없는 것처럼 취급해야(SHOULD) 해요.

LabelSet

LabelSet은 Label로 구성되어야(MUST) 하며 비어 있을 수(MAY) 있어요. 레이블 이름은 LabelSet 내에서 고유해야(MUST) 해요.

MetricPoint

각 MetricPoint는 MetricFamily 타입에 따라 값 세트로 구성돼요.

Exemplar

Exemplar는 MetricSet 외부 데이터에 대한 참조예요. 일반적인 사용 사례는 프로그램 트레이스의 ID예요.

Exemplar는 LabelSet과 값으로 구성되어야(MUST) 하며 타임스탬프가 있을 수(MAY) 있어요. 각각 MetricPoint의 LabelSet과 타임스탬프와 다를 수(MAY) 있어요.

Exemplar의 LabelSet의 레이블 이름과 값의 결합 길이는 128 UTF-8 문자 코드 포인트를 초과해서는 안(MUST NOT) 돼요. ",= 같은 exemplar 텍스트 렌더링의 다른 문자는 구현 단순성과 텍스트·proto 형식 간 일관성을 위해 이 한도에 포함되지 않아요.

수집기는 exemplar를 버릴 수(MAY) 있어요.

메트릭 (Metric)

메트릭은 MetricFamily 내의 고유한 LabelSet으로 정의돼요. 메트릭은 하나 이상의 MetricPoint 목록을 포함해야(MUST) 해요. 주어진 MetricFamily의 같은 이름을 가진 메트릭은 LabelSet에 같은 레이블 이름 세트를 가져야(SHOULD) 해요.

MetricPoint는 명시적 타임스탬프를 가지지 않는 것이 좋아요(SHOULD NOT).

메트릭에 대해 둘 이상의 MetricPoint가 노출되면 해당 MetricPoint는 단조 증가하는 타임스탬프를 가져야(MUST) 해요.

MetricFamily

MetricFamily는 메트릭이 0개 이상 있을 수(MAY) 있어요. MetricFamily는 이름, HELP, TYPE, UNIT 메타데이터를 가져야(MUST) 해요. MetricFamily의 모든 메트릭은 고유한 LabelSet을 가져야(MUST) 해요.

이름 (Name)

MetricFamily 이름은 문자열이며 MetricSet 내에서 고유해야(MUST) 해요. 이름은 snake_case여야(SHOULD) 해요. 메트릭 이름은 ABNF 섹션의 제약을 따라야(MUST) 해요.

MetricFamily 이름의 콜론은 해당 MetricFamily가 범용 모니터링 시스템의 계산 또는 집계 결과임을 신호하도록 RESERVED(예약)돼요.

밑줄로 시작하는 MetricFamily 이름은 RESERVED(예약)이며 이 표준이 지정하지 않는 한 사용해서는 안(MUST NOT) 돼요.

접미사 (Suffixes)

MetricFamily의 이름은 ABNF에 따라 텍스트 형식 내에서 다른 MetricFamily와 샘플 메트릭 이름의 잠재적 충돌을 초래해서는 안(MUST NOT) 돼요. 예는 "foo"라는 counter가 텍스트 형식에서 "foo_created"를 만들 수 있으므로 "foo_created"라는 gauge가 될 수 있는 것이에요.

Exposer는 텍스트 형식 샘플 메트릭 이름이 사용하는 접미사와 혼동될 수 있는 이름을 피해야(SHOULD) 해요.

  • 각 타입의 접미사는 다음과 같아요:
    • Counter: _total, _created
    • Summary: _count, _sum, _created, (빈 값)
    • Histogram: _count, _sum, _bucket, _created
    • GaugeHistogram: _gcount, _gsum, _bucket
    • Info: _info
    • Gauge: (빈 값)
    • StateSet: (빈 값)
    • Unknown: (빈 값)
타입 (Type)

Type은 MetricFamily 타입을 지정해요. 유효한 값은 "unknown", "gauge", "counter", "stateset", "info", "histogram", "gaugehistogram", "summary"예요.

단위 (Unit)

Unit은 MetricFamily 단위를 지정해요. 비어 있지 않으면 밑줄로 분리된 MetricFamily 이름의 접미사여야(MUST) 해요. 추가 생성 규칙이 텍스트 형식에서 접요사(infix)로 만들 수 있다는 점을 유의하세요.

Help

Help는 문자열이며 비어 있지 않아야(SHOULD) 해요. 사람이 소비할 MetricFamily의 간단한 설명을 제공하는 데 사용되며, 툴팁으로 사용될 만큼 짧아야(SHOULD) 해요.

MetricSet

MetricSet은 OpenMetrics가 노출하는 최상위 객체예요. MetricFamily로 구성되어야(MUST) 하며 비어 있을 수(MAY) 있어요.

각 MetricFamily 이름은 고유해야(MUST) 해요. 같은 레이블 이름과 값이 MetricSet의 모든 메트릭에 나타나서는 안(SHOULD NOT) 돼요.

MetricSet 내에서 MetricFamily의 특정 순서는 요구되지 않아요. Exposer는 사람이 읽기 쉽게 exposition을 만들 수(MAY) 있는데, 예를 들어 성능 트레이드오프가 말이 되면 알파벳순으로 정렬해요.

존재하면 아래 "푸시 기반과 풀 기반 시스템 모두에서 타깃 메타데이터 지원" 섹션에 따라 "target"이라고 하는 Info MetricFamily가 처음이어야(SHOULD) 해요.

메트릭 타입

Gauge

게이지는 현재 측정값이에요. 현재 사용 중인 메모리 바이트나 큐의 항목 수 같은 것이에요. 게이지에서는 절대값이 사용자에게 관심사예요.

gauge 타입의 메트릭에 있는 MetricPoint는 단일 값을 가져야(MUST) 해요.

게이지는 시간에 따라 증가, 감소, 또는 일정하게 유지될 수(MAY) 있어요. 한 방향으로만 움직여도 여전히 게이지일 수 있고 counter가 아닐 수 있어요. 로그 파일의 크기는 보통 증가만 하고, 리소스는 감소할 수 있으며, 큐 크기의 한계는 일정할 수 있어요.

게이지는 많은 상태를 가지고 시간에 따라 변하는 enum을 인코딩하는 데 사용될 수(MAY) 있어요. 가장 효율적이지만 가장 덜 사용자 친화적이에요.

Counter

카운터는 이산적인 사건을 측정해요. 흔한 예는 수신된 HTTP 요청 수, 사용된 CPU 초, 보낸 바이트예요. 카운터에서는 시간에 따라 얼마나 빨리 증가하는지가 사용자에게 관심사예요.

Counter 타입의 메트릭에 있는 MetricPoint는 Total이라고 하는 값 하나를 가져야(MUST) 해요. Total은 NaN이 아니고 0에서 시작해 시간에 따라 단조 비감소해야(MUST) 해요.

Counter 타입의 메트릭에 있는 MetricPoint는 Created라고 하는 타임스탬프 값을 가져야(SHOULD) 해요. 이는 수집기가 이전에 보지 못한 새 메트릭과 오래 실행 중인 메트릭을 구별하는 데 도움을 줄 수 있어요.

메트릭의 Counter의 Total의 MetricPoint는 0으로 리셋될 수(MAY) 있어요. 존재하면 해당 Created 시간도 리셋의 타임스탬프로 설정해야(MUST) 해요.

메트릭의 Counter의 Total의 MetricPoint는 exemplar를 가질 수(MAY) 있어요.

StateSet

StateSet은 관련 불리언 값의 시리즈(비트셋이라고도 함)를 나타내요. ENUM을 인코딩해야 한다면 StateSet을 통해 할 수(MAY) 있어요.

StateSet 메트릭의 포인트는 여러 상태를 포함할 수(MAY) 있고 상태마다 불리언 하나를 포함해야(MUST) 해요. 상태에는 이름이 있으며 이것은 문자열이에요.

StateSet 메트릭의 LabelSet은 자신의 MetricFamily 이름과 같은 레이블 이름을 가져서는 안(MUST NOT) 돼요.

StateSet으로 인코딩되면 ENUM은 MetricPoint 내에서 정확히 하나의 true 불리언을 가져야(MUST) 해요.

이것은 enum 값이 시간에 따라 변하고 상태 수가 몇 개보다 많지 않을 때 적합해요.

StateSet 타입의 MetricFamily는 빈 Unit 문자열을 가져야(MUST) 해요.

Info

Info 메트릭은 프로세스 수명 동안 변하지 않아야 하는 텍스트 정보를 노출하는 데 사용돼요. 흔한 예는 애플리케이션 버전, 리비전 관리 커밋, 컴파일러 버전이에요.

Info 메트릭의 MetricPoint는 LabelSet을 포함해요. Info MetricPoint의 LabelSet은 자신의 Metric의 LabelSet의 레이블 이름과 같은 레이블 이름을 가져서는 안(MUST NOT) 돼요.

Info는 시간에 따라 변하지 않는 ENUM을 인코딩하는 데 사용될 수(MAY) 있어요. 네트워크 인터페이스의 타입 같은 것들이에요.

Info 타입의 MetricFamily는 빈 Unit 문자열을 가져야(MUST) 해요.

Histogram

히스토그램은 이산적 사건의 분포를 측정해요. 흔한 예는 HTTP 요청의 지연 시간, 함수 실행 시간, I/O 요청 크기예요.

Histogram MetricPoint는 적어도 하나의 버킷을 포함해야(MUST) 하고 Sum, Created 값을 포함해야(SHOULD) 해요. 모든 버킷은 임계값과 값을 가져야(MUST) 해요.

Histogram MetricPoint는 +Inf 임계값을 가진 버킷 하나를 가져야(MUST) 해요. 버킷은 누적적이어야(MUST) 해요. 초 단위 요청 지연 시간을 나타내는 메트릭의 예로, 임계값 1, 2, 3, +Inf를 가진 버킷의 값은 value_1 <= value_2 <= value_3 <= value_+Inf를 따라야(MUST) 해요. 10개의 요청이 각각 1초 걸렸다면 1, 2, 3, +Inf 버킷의 값은 10과 같아야(MUST) 해요.

+Inf 버킷은 모든 요청을 센다. 존재하면 Sum 값은 측정된 모든 이벤트 값의 합과 같아야(MUST) 해요. MetricPoint 내의 버킷 임계값은 고유해야(MUST) 해요.

의미상 Sum과 버킷 값은 카운터이므로 NaN이거나 음수여서는 안(MUST NOT) 돼요. 음수 임계값 버킷을 사용할 수(MAY) 있지만, 그러면 Histogram MetricPoint는 sum 값을 포함해서는 안(MUST NOT) 돼요. 의미상 더 이상 카운터가 아니기 때문이에요. 버킷 임계값은 NaN과 같아서는 안(MUST NOT) 돼요. Count와 버킷 값은 정수여야(MUST) 해요.

Histogram MetricPoint는 Created라고 하는 타임스탬프 값을 가져야(SHOULD) 해요. 이는 수집기가 이전에 보지 못한 새 메트릭과 오래 실행 중인 메트릭을 구별하는 데 도움을 줄 수 있어요.

Histogram의 Metric의 LabelSet은 "le" 레이블 이름을 가져서는 안(MUST NOT) 돼요.

버킷 값은 exemplar를 가질 수(MAY) 있어요. 버킷은 모니터링 시스템이 성능/anti-denial-of-service 이유로 +Inf가 아닌 버킷을 세분성을 잃지만 여전히 유효한 히스토그램인 방식으로 버릴 수 있게 누적적이에요.

각 버킷은 그것보다 작거나 같은 값을 포함하고, exemplar의 값은 이 범위 안에 있어야(MUST) 해요. Exemplar는 가장 높은 값을 가진 버킷에 넣어야(SHOULD) 해요. 버킷은 둘 이상의 exemplar를 가져서는 안(MUST NOT) 돼요.

GaugeHistogram

GaugeHistogram은 현재 분포를 측정해요. 흔한 예는 항목이 큐에서 얼마나 오래 기다렸는지, 큐의 요청 크기예요.

GaugeHistogram MetricPoint는 +Inf 임계값을 가진 버킷 하나를 가져야(MUST) 하고 Gsum 값을 포함해야(SHOULD) 해요. 모든 버킷은 임계값과 값을 가져야(MUST) 해요.

GaugeHistogram의 버킷은 Histogram과 같은 모든 규칙을 따라요.

GaugeHistogram의 버킷과 Gsum은 개념적으로 게이지이지만, 버킷 값은 음수나 NaN이어서는 안(MUST NOT) 돼요. 음수 임계값 버킷이 있으면 sum은 음수일 수(MAY) 있어요. Gsum은 NaN이어서는 안(MUST NOT) 돼요. 버킷 값은 정수여야(MUST) 해요.

GaugeHistogram의 Metric의 LabelSet은 "le" 레이블 이름을 가져서는 안(MUST NOT) 돼요.

버킷 값은 exemplar를 가질 수 있어요.

각 버킷은 그것보다 작거나 같은 값을 포함하고, exemplar의 값은 이 범위 안에 있어야(MUST) 해요. Exemplar는 가장 높은 값을 가진 버킷에 넣어야(SHOULD) 해요. 버킷은 둘 이상의 exemplar를 가져서는 안(MUST NOT) 돼요.

Summary

Summary도 이산적 사건의 분포를 측정하며, 히스토그램이 너무 비싸거나 평균 사건 크기만으로 충분할 때 사용할 수(MAY) 있어요.

일부 기존 계측 라이브러리가 미리 계산된 quantile을 노출하고 히스토그램을 지원하지 않으므로 역호환성을 위해서도 사용될 수(MAY) 있어요. 미리 계산된 quantile은 사용하면 안(SHOULD NOT) 돼요. quantile은 집계할 수 없고 사용자가 그것이 다루는 기간을 종종 추론할 수 없기 때문이에요.

Summary MetricPoint는 Count, Sum, Created, quantile 세트로 구성될 수(MAY) 있어요.

의미상 Count와 Sum 값은 카운터이므로 NaN이거나 음수여서는 안(MUST NOT) 돼요. Count는 정수여야(MUST) 해요.

Count나 Sum 값을 포함하는 Summary 타입의 메트릭의 MetricPoint는 Created라고 하는 타임스탬프 값을 가져야(SHOULD) 해요. 이는 수집기가 이전에 보지 못한 새 메트릭과 오래 실행 중인 메트릭을 구별하는 데 도움을 줄 수 있어요. Created는 quantile 값의 수집 기간과 관련해서는 안(MUST NOT) 돼요.

Quantile은 quantile에서 값으로의 맵이에요. 예는 myapp_http_request_duration_seconds라는 메트릭에서 값 0.2를 가진 quantile 0.95로, 알 수 없는 기간에 걸쳐 95번째 백분위수 지연 시간이 200ms라는 뜻이에요. 관련 기간에 이벤트가 없으면 quantile 값은 NaN이어야(MUST) 해요. Quantile의 Metric의 LabelSet은 "quantile" 레이블 이름을 가져서는 안(MUST NOT) 돼요. Quantile은 0과 1 사이(포함)여야(MUST) 해요. Quantile 값은 음수여서는 안(MUST NOT) 돼요. Quantile 값은 최근 값을 나타내야(SHOULD) 해요. 일반적으로 지난 5-10분에 걸친 값이에요.

Unknown

Unknown은 사용하면 안(SHOULD NOT) 돼요. 제3자 시스템에서 개별 메트릭의 타입을 결정하는 것이 불가능할 때 사용할 수(MAY) 있어요.

unknown 타입의 메트릭의 포인트는 단일 값을 가져야(MUST) 해요.

데이터 전송 및 와이어 형식

텍스트 와이어 형식을 지원해야(MUST) 하며 기본값이에요. protobuf 와이어 형식을 지원할 수(MAY) 있으며 협상 후에만 사용해야(MUST ONLY) 해요.

OpenMetrics 형식은 Regular Chomsky Grammars라서 빠르고 작은 파서를 작성할 수 있어요. 텍스트 형식은 잘 압축되고 protobuf는 이미 이진이면서 효율적으로 인코딩돼요.

부분적이거나 유효하지 않은 exposition은 전체적으로 오류로 간주해야(MUST) 해요.

프로토콜 협상

모든 수집기 구현은 TLS 1.2 이상으로 보호된 데이터를 수집할 수 있어야(MUST) 해요. 모든 exposer는 TLS 1.2 이상으로 보호된 데이터를 내보낼 수 있어야(SHOULD) 해요. 수집기 구현은 TLS 없는 HTTP에서 데이터를 수집할 수 있어야(SHOULD) 해요. 모든 구현은 데이터 전송에 TLS를 사용해야(SHOULD) 해요.

사용할 OpenMetrics 형식 버전의 협상은 대역외(out-of-band)예요. 예를 들어 HTTP를 통한 풀 기반 exposition의 경우 표준 HTTP 콘텐츠 타입 협상이 사용되며, 더 새로운 버전이 요청되지 않으면 표준의 가장 오래된 버전(즉 1.0.0)으로 기본 설정되어야(MUST) 해요.

푸시 기반 협상은 본질적으로 더 복잡해요. exposer가 일반적으로 연결을 시작하기 때문이에요. 생산자는 수집기가 달리 요청하지 않는 한 표준의 가장 오래된 버전(즉 1.0.0)을 사용해야(MUST) 해요.

텍스트 형식

ABNF

ABNF는 RFC 5234에 따름

"exposition"은 ABNF의 최상위 토큰이에요.

exposition = metricset HASH SP eof [ LF ]

metricset = *metricfamily

metricfamily = *metric-descriptor *metric

metric-descriptor = HASH SP type SP metricname SP metric-type LF
metric-descriptor =/ HASH SP help SP metricname SP escaped-string LF
metric-descriptor =/ HASH SP unit SP metricname SP *metricname-char LF

metric = *sample

metric-type = counter / gauge / histogram / gaugehistogram / stateset
metric-type =/ info / summary / unknown

sample = metricname [labels] SP number [SP timestamp] [exemplar] LF

exemplar = SP HASH SP labels SP number [SP timestamp]

labels = "{" [label *(COMMA label)] "}"

label = label-name EQ DQUOTE escaped-string DQUOTE

number = realnumber
; Case insensitive
number =/ [SIGN] ("inf" / "infinity")
number =/ "nan"

timestamp = realnumber

; Not 100% sure this captures all float corner cases.
; Leading 0s explicitly okay
realnumber = [SIGN] 1*DIGIT
realnumber =/ [SIGN] 1*DIGIT ["." *DIGIT] [ "e" [SIGN] 1*DIGIT ]
realnumber =/ [SIGN] *DIGIT "." 1*DIGIT [ "e" [SIGN] 1*DIGIT ]

; RFC 5234 is case insensitive.
; Uppercase
eof = %d69.79.70
type = %d84.89.80.69
help = %d72.69.76.80
unit = %d85.78.73.84
; Lowercase
counter = %d99.111.117.110.116.101.114
gauge = %d103.97.117.103.101
histogram = %d104.105.115.116.111.103.114.97.109
gaugehistogram = gauge histogram
stateset = %d115.116.97.116.101.115.101.116
info = %d105.110.102.111
summary = %d115.117.109.109.97.114.121
unknown = %d117.110.107.110.111.119.110

BS = "\"
EQ = "="
COMMA = ","
HASH = "#"
SIGN = "-" / "+"

metricname = metricname-initial-char 0*metricname-char

metricname-char = metricname-initial-char / DIGIT
metricname-initial-char = ALPHA / "_" / ":"

label-name = label-name-initial-char *label-name-char

label-name-char = label-name-initial-char / DIGIT
label-name-initial-char = ALPHA / "_"

escaped-string = *escaped-char

escaped-char = normal-char
escaped-char =/ BS ("n" / DQUOTE / BS)
escaped-char =/ BS normal-char

; Any unicode character, except newline, double quote, and backslash
normal-char = %x00-09 / %x0B-21 / %x23-5B / %x5D-D7FF / %xE000-10FFFF

전체 구조

UTF-8을 사용해야(MUST) 해요. 바이트 순서 표시(BOM)는 사용해서는 안(MUST NOT) 돼요. 구현자에게 중요한 알림으로, 바이트 0은 유효한 UTF-8이지만 예를 들어 바이트 255는 그렇지 않아요.

콘텐츠 타입은 다음과 같아야(MUST) 해요:

application/openmetrics-text; version=1.0.0; charset=utf-8

줄 끝은 줄 바꿈(\n)으로 신호되어야(MUST) 하고 캐리지 리턴(\r)을 포함해서는 안(MUST NOT) 돼요. Exposition은 EOF로 끝나야(MUST) 하고 EOF\n으로 끝나야(SHOULD) 해요.

완전한 exposition의 예:

# TYPE acme_http_router_request_seconds summary
# UNIT acme_http_router_request_seconds seconds
# HELP acme_http_router_request_seconds Latency though all of ACME's HTTP request router.
acme_http_router_request_seconds_sum{path="/api/v1",method="GET"} 9036.32
acme_http_router_request_seconds_count{path="/api/v1",method="GET"} 807283.0
acme_http_router_request_seconds_created{path="/api/v1",method="GET"} 1605281325.0
acme_http_router_request_seconds_sum{path="/api/v2",method="POST"} 479.3
acme_http_router_request_seconds_count{path="/api/v2",method="POST"} 34.0
acme_http_router_request_seconds_created{path="/api/v2",method="POST"} 1605281325.0
# TYPE go_goroutines gauge
# HELP go_goroutines Number of goroutines that currently exist.
go_goroutines 69
# TYPE process_cpu_seconds counter
# UNIT process_cpu_seconds seconds
# HELP process_cpu_seconds Total user and system CPU time spent in seconds.
process_cpu_seconds_total 4.20072246e+06
# EOF
이스케이프

ABNF가 이스케이프를 언급하는 곳에서 다음 이스케이프를 적용해야(MUST) 해요 줄 바꿈, \n(0x0A) -> 문자 그대로 \\n(바이트코드 0x5c 0x6e) 이중 따옴표 -> \\"(바이트코드 0x5c 0x22) 백슬래시 -> \\\\(바이트코드 0x5c 0x5c)

이중 백슬래시는 백슬래시 문자를 나타내는 데 사용해야(SHOULD) 해요. 정의되지 않은 이스케이프 시퀀스에는 단일 백슬래시를 사용해서는 안(SHOULD NOT) 돼요. 예를 들어 \\\\a\\a와 동등하며 선호돼요.

숫자

정수는 소수점을 가져서는 안(MUST NOT) 돼요. 예는 23, 0042, 1341298465647914예요.

부동 소수점 숫자는 소수점이나 과학적 표기법으로 표현해야(MUST) 해요. 예는 8903.1234211.89e-7이에요. 부동 소수점 숫자는 IEEE 754가 정의하는 64비트 부동 소수점 값 범위 안에 맞아야(MUST) 하지만, 정밀도 손실을 초래하는 가수부 비트가 너무 많을 수(MAY) 있어요. 이는 나노초 해상도 타임스탬프를 인코딩하는 데 사용될 수(MAY) 있어요.

임의의 정수·부동 소수점 숫자 렌더링은 "Canonical Numbers" 섹션에서처럼 "quantile"과 "le" 레이블 값에 사용해서는 안(MUST NOT) 돼요. 숫자가 사용되는 다른 곳에서는 사용할 수(MAY) 있어요.

고려사항: 정규 숫자 (Canonical Numbers)

히스토그램의 "le" 레이블 값과 summary 메트릭의 "quantile" 레이블 값의 숫자는 레이블 값이므로 특별해요. 레이블 값은 불투명하도록 의도됐어요. 최종 사용자가 이 문자열 값과 직접 상호작용할 가능성이 높고, 많은 모니터링 시스템이 그것을 1급 숫자로 다루는 능력이 부족하므로, 주어진 숫자가 정확히 같은 텍스트 표현을 가지는 것이 유익할 거예요.

일관성은 매우 바람직하지만, 언어와 런타임의 실제 구현이 이것을 의무화하는 것을 비실용적으로 만들어요. 가장 중요한 공통 quantile은 0.5, 0.95, 0.9, 0.99, 0.999이고, 버킷 값은 밀리초에서 10.0초까지의 값을 나타내며, 이는 일반적인 웹 서비스의 지연 시간 SLA와 Apdex 같은 경우를 다루기 때문이에요. 10의 거듭제곱은 런타임에 따라 변하는 고정점과 지수 렌더링 사이의 전환이 일관되도록 하기 위해 포함돼요. 대상 렌더링은 float64 값의 기본 Go 렌더링(즉 %g)과 동등하며, 소수점이나 지수가 없으면 float임을 명확히 하기 위해 .0을 붙여요.

Exposer는 양의 무한대에 대한 출력을 +Inf로 생성해야(MUST) 해요.

Exposer는 0.0에서 10.0까지 0.001 증분의 값을 다음 예시에 따라 생성해야(SHOULD) 해요: 0.0 0.001 0.002 0.01 0.1 0.9 0.95 0.99 0.999 1.0 1.7 10.0

Exposer는 1e-10에서 1e+10까지의 10의 거듭제곱 값을 다음 예시에 따라 생성해야(SHOULD) 해요: 1e-10 1e-09 1e-05 0.0001 0.1 1.0 100000.0 1e+06 1e+10

파서는 정규 값 밖의 입력을 정규 값과 일관되지 않다는 이유만으로 거부해서는 안(MUST NOT) 돼요. 예를 들어 1.1e-4는 0.00011의 일관된 렌더링이 아니더라도 거부해서는 안 돼요.

Exposer는 비정규 숫자에 대해 이 패턴을 따라야(SHOULD) 해요. 그리고 이 값들에 대해 렌더링 알고리즘을 일관되게 조정함으로써 다른 값의 압도적 다수도 일관된 렌더링을 가질 것이라는 의도예요. 특정 le/quantile 값 몇 개만 사용하는 exposer는 하드코딩할 수도 있어요. Grisu3 같은 최소 부동 소수점 렌더링 알고리즘을 쉽게 이용할 수 없는 C 같은 언어에서는 exposer가 다른 렌더링을 사용할 수(MAY) 있어요.

C와 printf 구현을 공유하는 다른 언어의 구현자에게 경고: %f, %e, %g의 표준 정밀도는 6개의 유효 숫자뿐이에요. 전체 정밀도에는 17개의 유효 숫자가 필요해요. 예: printf("%.17g", d).

타임스탬프

나노초 정밀도가 필요하면 타임스탬프에 지수 float 렌더링을 사용해서는 안(SHOULD NOT) 돼요. float64의 렌더링은 충분한 정밀도가 없기 때문이에요. 예: 1604676851.123456789.

MetricFamily

MetricFamily 사이에 명시적 구분자가 있어서는 안(MUST NOT) 돼요. 다음 MetricFamily는 이전 MetricFamily의 일부가 될 수 없는 메타데이터나 새 샘플 메트릭 이름으로 신호되어야(MUST) 해요.

MetricFamily는 인터리브되어서는 안(MUST NOT) 돼요.

MetricFamily 메타데이터

네 가지 메타데이터가 있어요: MetricFamily 이름, TYPE, UNIT, HELP. foo라는 Counter 메트릭의 메타데이터 예:

# TYPE foo counter

TYPE이 노출되지 않으면 MetricFamily는 Unknown 타입이어야(MUST) 해요.

단위가 지정되면 UNIT 메타데이터 라인에 제공되어야(MUST) 해요. 추가로 밑줄과 단위가 MetricFamily 이름의 접미사여야(MUST) 해요.

"seconds" 단위를 가진 foo_seconds 메트릭의 유효한 예:

# TYPE foo_seconds counter
# UNIT foo_seconds seconds

단위가 이름의 접미사가 아닌 유효하지 않은 예:

# TYPE foo counter
# UNIT foo seconds

다음을 갖는 것도 유효해요:

# TYPE foo_seconds counter

단위가 알려져 있으면 제공해야(SHOULD) 해요.

UNIT이나 HELP 라인의 값은 비어 있을 수(MAY) 있어요. 이것은 MetricFamily에 대한 메타데이터 라인이 없는 것처럼 취급해야(MUST) 해요.

# TYPE foo_seconds counter
# UNIT foo_seconds seconds
# HELP foo_seconds Some text and \n some \" escaping

MetricFamily에 대해 각 유형의 메타데이터 라인이 둘 이상 있어서는 안(MUST NOT) 돼요. 순서는 TYPE, UNIT, HELP여야(SHOULD) 해요.

이 메타데이터와 메시지 끝의 EOF 라인 외에는 #으로 시작하는 라인을 노출해서는 안(MUST NOT) 돼요.

메트릭 (Metric)

메트릭은 인터리브되어서는 안(MUST NOT) 돼요.

"Text format -> MetricPoint"의 예를 참조하세요. 레이블 레이블이나 타임스탬프가 없고 값이 0인 샘플은 다음과 같이 렌더링해야(MUST) 해요:

bar_seconds_count 0

또는 다음과 같이:

bar_seconds_count{} 0

레이블 값은 유효한 UTF-8 값이면 무엇이든 될 수(MAY) 있으므로 ABNF에 따라 이스케이프를 적용해야(MUST) 해요. 두 레이블이 있는 유효한 예:

bar_seconds_count{a="x",b="escaping\" example \n "} 0

MetricPoint의 값 렌더링은 추가 레이블(예: Histogram 타입의 "le" 레이블)을 포함할 수 있으며, 이는 메트릭 자신의 LabelSet과 같은 방식으로 렌더링되어야(MUST) 해요.

MetricPoint

MetricPoint는 인터리브되어서는 안(MUST NOT) 돼요.

MetricFamily 내에 여러 MetricPoint와 Sample이 있었던 올바른 예:

# TYPE foo_seconds summary
# UNIT foo_seconds seconds
foo_seconds_count{a="bb"} 0 123
foo_seconds_sum{a="bb"} 0 123
foo_seconds_count{a="bb"} 0 456
foo_seconds_sum{a="bb"} 0 456
foo_seconds_count{a="ccc"} 0 123
foo_seconds_sum{a="ccc"} 0 123
foo_seconds_count{a="ccc"} 0 456
foo_seconds_sum{a="ccc"} 0 456

메트릭이 인터리브된 잘못된 예:

# TYPE foo_seconds summary
# UNIT foo_seconds seconds
foo_seconds_count{a="bb"} 0 123
foo_seconds_count{a="ccc"} 0 123
foo_seconds_count{a="bb"} 0 456
foo_seconds_count{a="ccc"} 0 456

MetricPoint가 인터리브된 잘못된 예:

# TYPE foo_seconds summary
# UNIT foo_seconds seconds
foo_seconds_count{a="bb"} 0 123
foo_seconds_count{a="bb"} 0 456
foo_seconds_sum{a="bb"} 0 123
foo_seconds_sum{a="bb"} 0 456

메트릭 타입

Gauge

Gauge 타입의 MetricFamily의 MetricPoint 값에 대한 Sample MetricName은 접미사를 가져서는 안(MUST NOT) 돼요.

레이블이 없는 Metric과 타임스탬프가 없는 MetricPoint가 있는 MetricFamily 예:

# TYPE foo gauge
foo 17.0

레이블이 있는 두 개의 Metric과 타임스탬프가 없는 MetricPoint가 있는 MetricFamily 예:

# TYPE foo gauge
foo{a="bb"} 17.0
foo{a="ccc"} 17.0

Metric이 없는 MetricFamily 예:

# TYPE foo gauge

레이블이 있는 Metric과 타임스탬프가 있는 MetricPoint 예:

# TYPE foo gauge
foo{a="b"} 17.0 1520879607.789

레이블이 없는 Metric과 타임스탬프가 있는 MetricPoint의 예:

# TYPE foo gauge
foo 17.0 1520879607.789

레이블이 없는 Metric과 타임스탬프가 있는 두 MetricPoint의 예:

# TYPE foo gauge
foo 17.0 123
foo 18.0 456
Counter

MetricPoint의 Total 값 Sample MetricName은 _total 접미사를 가져야(MUST) 해요. 존재하면 MetricPoint의 Created 값 Sample MetricName은 _created 접미사를 가져야(MUST) 해요.

레이블이 없는 Metric, 타임스탬프와 created가 없는 MetricPoint 예:

# TYPE foo counter
foo_total 17.0

레이블이 없는 Metric, 타임스탬프가 있고 created가 없는 MetricPoint 예:

# TYPE foo counter
foo_total 17.0 1520879607.789

레이블이 없는 Metric, 타임스탬프가 없고 created가 있는 MetricPoint 예:

# TYPE foo counter
foo_total 17.0
foo_created 1520430000.123

레이블이 없는 Metric, 타임스탬프와 created가 있는 MetricPoint 예:

# TYPE foo counter
foo_total 17.0 1520879607.789
foo_created 1520430000.123 1520879607.789

Exemplar는 MetricPoint의 Total 샘플에 붙일 수(MAY) 있어요.

StateSet

StateSet 타입의 MetricFamily의 MetricPoint 값에 대한 Sample MetricName은 접미사를 가져서는 안(MUST NOT) 돼요.

StateSet은 MetricPoint에 상태마다 하나의 샘플을 가져야(MUST) 해요. 각 상태의 샘플은 레이블 이름이 MetricFamily 이름이고 레이블 값이 상태 이름인 레이블을 가져야(MUST) 해요. 상태의 값은 상태가 true이면 1이어야(MUST) 하고 false이면 0이어야(MUST) 해요.

값 bb만 활성화되고 메트릭 이름이 foo인 상태 "a", "bb", "ccc"가 있는 예:

# TYPE foo stateset
foo{foo="a"} 0
foo{foo="bb"} 1
foo{foo="ccc"} 0

Metric에 "entity" 레이블이 있는 예:

# TYPE foo stateset
foo{entity="controller",foo="a"} 1.0
foo{entity="controller",foo="bb"} 0.0
foo{entity="controller",foo="ccc"} 0.0
foo{entity="replica",foo="a"} 1.0
foo{entity="replica",foo="bb"} 0.0
foo{entity="replica",foo="ccc"} 1.0
Info

Info 타입의 MetricFamily의 MetricPoint 값에 대한 Sample MetricName은 _info 접미사를 가져야(MUST) 해요. Sample 값은 항상 1이어야(MUST) 해요.

레이블이 없는 Metric과 "name", "version" 레이블을 가진 하나의 MetricPoint 값 예:

# TYPE foo info
foo_info{name="pretty name",version="8.2.7"} 1

"entity" 레이블이 있는 Metric과 "name", "version" 레이블을 가진 하나의 MetricPoint 값 예:

# TYPE foo info
foo_info{entity="controller",name="pretty name",version="8.2.7"} 1.0
foo_info{entity="replica",name="prettier name",version="8.1.9"} 1.0

메트릭 레이블과 MetricPoint 값 레이블은 어떤 순서든 될 수(MAY) 있어요.

Summary

존재하면 MetricPoint의 Sum 값 Sample MetricName은 _sum 접미사를 가져야(MUST) 해요. 존재하면 MetricPoint의 Count 값 Sample MetricName은 _count 접미사를 가져야(MUST) 해요. 존재하면 MetricPoint의 Created 값 Sample MetricName은 _created 접미사를 가져야(MUST) 해요. 존재하면 MetricPoint의 Quantile 값은 측정된 quantile을 "quantile"이라는 레이블 이름과 측정된 quantile의 레이블 값을 가진 레이블로 지정해야(MUST) 해요.

레이블이 없는 Metric과 Sum, Count, Created 값이 있는 MetricPoint 예:

# TYPE foo summary
foo_count 17.0
foo_sum 324789.3
foo_created 1520430000.123

레이블이 없는 Metric과 두 개의 quantile이 있는 MetricPoint 예:

# TYPE foo summary
foo{quantile="0.95"} 123.7
foo{quantile="0.99"} 150.0

Quantile은 어떤 순서든 될 수(MAY) 있어요.

Histogram

MetricPoint의 Bucket 값 Sample MetricName은 _bucket 접미사를 가져야(MUST) 해요. 존재하면 MetricPoint의 Sum 값 Sample MetricName은 _sum 접미사를 가져야(MUST) 해요. 존재하면 MetricPoint의 Created 값 Sample MetricName은 _created 접미사를 가져야(MUST) 해요. MetricPoint에 Sum 값이 존재하는 경우에만, 그 MetricPoint의 +Inf Bucket 값도 "_count" 접미사를 가진 MetricName을 가진 Sample에 나타나야(MUST) 해요.

버킷은 "le"의 숫자 증가 순서로 정렬되어야(MUST) 하고 "le" 레이블의 값은 정규 숫자 규칙을 따라야(MUST) 해요.

레이블이 없는 Metric과 Sum, Count, Created 값과 12개 버킷이 있는 MetricPoint 예. 넓고 비정형이지만 유효한 다양한 "le" 값을 의도적으로 보여줘요:

# TYPE foo histogram
foo_bucket{le="0.0"} 0
foo_bucket{le="1e-05"} 0
foo_bucket{le="0.0001"} 5
foo_bucket{le="0.1"} 8
foo_bucket{le="1.0"} 10
foo_bucket{le="10.0"} 11
foo_bucket{le="100000.0"} 11
foo_bucket{le="1e+06"} 15
foo_bucket{le="1e+23"} 16
foo_bucket{le="1.1e+23"} 17
foo_bucket{le="+Inf"} 17
foo_count 17
foo_sum 324789.3
foo_created 1520430000.123
Exemplar

레이블이 없는 Exemplar는 빈 LabelSet을 {}로 나타내야(MUST) 해요.

여러 유효한 경우를 보여주는 Exemplar 예: "0.01" 버킷에는 Exemplar가 없어요. 0.1 버킷에는 레이블이 없는 Exemplar가 있어요. 1 버킷에는 레이블이 하나 있는 Exemplar가 있어요. 10 버킷에는 레이블과 타임스탬프가 있는 Exemplar가 있어요. 실제로 모든 버킷은 같은 스타일의 Exemplar를 가져야(SHOULD) 해요.

# TYPE foo histogram
foo_bucket{le="0.01"} 0
foo_bucket{le="0.1"} 8 # {} 0.054
foo_bucket{le="1"} 11 # {trace_id="KOO5S4vxi0o"} 0.67
foo_bucket{le="10"} 17 # {trace_id="oHg5SJYRHA0"} 9.8 1520879607.789
foo_bucket{le="+Inf"} 17
foo_count 17
foo_sum 324789.3
foo_created  1520430000.123
GaugeHistogram

MetricPoint의 Bucket 값 Sample MetricName은 _bucket 접미사를 가져야(MUST) 해요. 존재하면 MetricPoint의 Sum 값 Sample MetricName은 _gsum 접미사를 가져야(MUST) 해요. MetricPoint에 Sum 값이 존재하는 경우에만, 그 MetricPoint의 +Inf Bucket 값도 "_gcount" 접미사를 가진 MetricName을 가진 Sample에 나타나야(MUST) 해요.

버킷은 "le"의 숫자 증가 순서로 정렬되어야(MUST) 하고 "le" 레이블의 값은 정규 숫자 규칙을 따라야(MUST) 해요.

레이블이 없는 Metric과 버킷에 Exemplar가 없는 하나의 MetricPoint 값 예:

# TYPE foo gaugehistogram
foo_bucket{le="0.01"} 20.0
foo_bucket{le="0.1"} 25.0
foo_bucket{le="1"} 34.0
foo_bucket{le="10"} 34.0
foo_bucket{le="+Inf"} 42.0
foo_gcount 42.0
foo_gsum 3289.3
Unknown

Unknown 타입의 MetricFamily의 MetricPoint 값에 대한 sample metric name은 접미사를 가져서는 안(MUST NOT) 돼요.

레이블이 없는 Metric과 타임스탬프가 없는 MetricPoint 예:

# TYPE foo unknown
foo 42.23

Protobuf 형식

전체 구조

Protobuf 메시지는 이진으로 인코딩되어야(MUST) 하고 application/openmetrics-protobuf; version=1.0.0을 콘텐츠 타입으로 가져야(MUST) 해요.

모든 페이로드는 OpenMetrics protobuf 스키마가 정의하는 단일 이진 인코딩 MetricSet 메시지여야(MUST) 해요.

버전

protobuf 형식은 protocol buffer 언어의 proto3 버전을 따라야(MUST) 해요.

문자열

모든 문자열 필드는 UTF-8로 인코딩되어야(MUST) 해요.

타임스탬프

OpenMetrics protobuf 스키마의 타임스탬프 표현은 게시된 google.protobuf.Timestamp 메시지를 따라야(MUST) 해요. 타임스탬프 메시지는 Unix epoch 초의 int64와, seconds 타임스탬프 구성 요소에서 앞으로 세는 나노초 해상도의 음수가 아닌 초 미만 분수인 int32여야(MUST) 해요. 0에서 999,999,999 사이(포함)여야(MUST) 해요.

Protobuf 스키마

Protobuf 스키마는 현재 여기에서 사용할 수 있어요.

참고: 프로메테우스와 생태계는 OpenMetrics protobuf 스키마를 지원하지 않고, 대신 유사한 io.prometheus.client 형식을 사용해요. OpenMetrics 2.0에서 protobuf 스키마의 미래에 대한 논의가 진행 중이에요.

설계 고려사항

범위

OpenMetrics는 온라인 시스템에 텔레메트리를 제공하기 위한 것이에요. 하드나 소프트 실시간 보장을 제공하지 않는 프로토콜 위에서 동작하므로 스스로 실시간 보장을 할 수 없어요. OpenMetrics의 지연 시간과 지터 속성은 밑바탕의 네트워크, 운영체제, CPU 등과 마찬가지로 부정확해요. 집계가 의사결정의 기초로 사용될 만큼은 충분히 정확하지만, 개별 사건을 반영하지는 않아요.

시간당 몇 개의 요청을 받는 애플리케이션부터 400Gb 네트워크 포트의 대역폭 사용량 모니터링까지 모든 크기의 시스템이 지원되어야 해요. 전송된 텔레메트리의 집계와 분석은 임의의 시간 기간에 걸쳐 가능해야 해요.

데이터 전송 시점의 상태 스냅샷을 정기적인 간격으로 운반하도록 의도됐어요.

범위 밖

수집기가 어떤 exposer가 존재하는지 발견하는 방법과 그 반대는 이 표준의 범위 밖이며 따라서 정의되지 않아요.

확장과 개선

OpenMetrics의 이 첫 버전은 잘 확립되고 사실상 표준인 프로메테우스 텍스트 형식 0.0.4에 기반하며, 의도적으로 그 위에 주요 구문·의미 확장이나 최적화를 추가하지 않았어요. 예를 들어 히스토그램 버킷의 텍스트 표현을 더 간결하게 만들려는 시도는 하지 않았고, 그 반복적인 특성을 처리하기 위해 밑바탕 스택의 압축에 의존해요.

이것은 의도적인 선택이에요. 표준이 기존 사용자 기반의 채택과 모멘텀을 활용할 수 있게 하기 위해서예요. 이는 프로메테우스 텍스트 형식 0.0.4로부터 상대적으로 쉬운 전환을 보장해요.

또한 구현이 쉬운 기본 표준이 있다는 것을 보장해요. 이것은 표준의 미래 버전에서 확장될 수 있어요. 표준의 미래 버전이 항상 이 1.0 버전을 구문과 의미 양쪽에서 지원하는 것을 요구할 것이라는 의도예요.

우리는 모니터링 시스템이 과도한 부담 없이 OpenMetrics exposition에서 유용한 정보를 얻도록 허용하고 싶어요. 모든 메타데이터와 구조를 벗겨내고 OpenMetrics exposition을 정렬되지 않은 샘플 세트로만 보면 그것만으로 사용 가능해야 해요. 그와 같은 것으로, 사용자 지정 파싱과 처리를 요구할 수 있는 게이지와 카운터의 혼합으로 표현될 수 없는 스케치나 t-digest 같은 불투명한 이진 타입도 없어요.

이 원칙은 표준 전체에 걸쳐 일관되게 적용돼요. 예를 들어 MetricFamily의 단위는 단위 메타데이터를 이해하지 못하는 시스템에서도 단위를 사용할 수 있도록 이름에 중복된다. "le" 레이블은 특별한 구문을 얻는 대신 일반 레이블 값이라서 수집기가 그것을 수집하기 위해 특별한 히스토그램 처리 코드를 추가할 필요가 없어요. 추가 예로, 복합 데이터 타입이 없어요. 예를 들어 위도/경도를 위한 지리 위치 타입이 없는데, 이는 별도의 gauge 메트릭으로 할 수 있기 때문이에요.

단위와 기본 단위

시스템 간 일관성과 혼동을 피하기 위해 단위는 대체로 SI 기본 단위에 기반해요. 기본 단위에는 초, 바이트, 줄, 그램, 미터, 비율, 볼트, 암페어, 섭씨가 포함돼요. 해당되면 단위를 제공해야 해요.

예를 들어 모든 기간 메트릭을 초 단위로 두면, 주어진 메트릭이 나노초, 마이크로초, 밀리초, 초, 분, 시간, 일, 주 중 무엇인지 추측하거나 혼합 단위를 다룰 위험이 없어요. 접두사 없는 단위를 선택함으로써 복잡한 시스템의 창발적 행동의 결과로 킬로밀리초 같은 상황을 피해요.

값이 부동 소수점일 수 있으므로 기본 단위 이하 정밀도가 표준에 내장돼 있어요.

유사하게 비트와 바이트를 섞는 것은 혼동을 주므로 바이트가 기본으로 선택돼요. 켈빈이 이론적으로 더 나은 기본 단위이지만 실제로 대부분의 기존 하드웨어는 섭씨를 노출해요. 킬로그램이 SI 기본 단위이지만 킬로 접두사가 문제가 있어서 그램이 기본 단위로 선택돼요.

기본 단위를 모든 가능한 경우에 사용해야(SHOULD) 하지만, 켈빈은 색 온도나 흑체 온도처럼 섭씨와 켈빈 메트릭의 비교가 없을 것 같은 사용 사례에서 섭씨 대신 사용할 수(MAY) 있는 잘 확립된 단위예요.

기본 단위는 비율이지 백분율이 아니에요. 가능하면 주어진 분자와 분모에 대한 게이지나 카운터 형태의 원시 데이터를 노출해야 해요. 이것은 수집기에서 분석과 집계에 더 나은 수학적 속성을 가져요.

데시벨은 기본 단위가 아니에요. 첫째, deci는 SI 접두사이고 둘째, bel은 대수적이기 때문이에요. 신호/에너지/전력 비율을 노출하려면 비율을 직접 노출하는 것이 더 낫고, 가능하면 원시 전력/에너지를 노출하는 것이 더 낫습니다. 부동 소수점 지수는 극단적인 과학적 사용조차 충분히 덮을 수 있어요. 전자볼트(~1e-19 J)부터 초신성이 방출하는 에너지(~1e44 J)까지는 63 자릿수이고, 64비트 부동 소수점 숫자는 2000 자릿수 이상을 덮을 수 있어요.

비기본 단위를 피할 수 없고 변환이 실행 불가능하다면, 명확성을 위해 실제 단위를 여전히 메트릭 이름에 포함해야 해요. 예를 들어 줄은 에너지와 전력 둘 다의 기본 단위이고, 와트는 줄 단위의 카운터로 표현될 수 있기 때문이에요. 실제로 주어진 제3자 시스템이 와트만 노출할 수 있으므로, 그 경우 와트로 표현된 게이지가 유일한 현실적인 선택이 될 거예요.

모든 MetricFamily에 단위가 있는 것은 아니에요. 예를 들어 HTTP 요청 수는 단위가 없을 거예요. 기술적으로 단위는 HTTP 요청이 될 텐데, 그 의미로는 전체 MetricFamily 이름이 단위가 돼요. 그런 극단으로 가는 것은 유용하지 않아요. 다운스트림 시스템의 사람이 소비하는 그래프에 좋은 축을 가질 가능성을 항상 염두에 두어야 해요.

무상태성

OpenMetrics가 정의하는 와이어 형식은 exposition에 걸쳐 무상태예요. 이전에 노출된 정보가 미래의 exposition에 영향을 미쳐서는 안(MUST NOT) 돼요. 각 exposition은 exposer의 현재 상태에 대한 자급자족적 스냅샷이에요.

같은 자급자족적 exposition이 기존 및 새 수집기 모두에 제공되어야(MUST) 해요.

핵심 설계 선택은 exposer가 최근에 변경이나 관측이 없었다는 이유만으로 메트릭을 제외해서는 안(MUST NOT) 된다는 것이에요. exposer는 수집기가 exposition을 얼마나 자주 소비하는지에 대한 어떤 가정도 해서는 안 돼요.

시간에 따른 Exposition과 메트릭 진화

메트릭은 시간에 따른 진화를 분석할 수 있을 때 가장 유용하므로, 그에 따라 exposition은 시간에 걸쳐 의미가 있어야 해요. 따라서 단일 exposition만으로 유용하고 유효한 것은 충분하지 않아요. 메트릭 의미의 일부 변경도 다운스트림 사용자를 깨뜨릴 수 있어요.

파서는 이전 결과를 캐싱해 최적화하는 것이 일반적이에요. 따라서 exposition 간에 레이블이 노출되는 순서를 바꾸는 것은 기술적으로 깨뜨리지는 않더라도 피해야(SHOULD) 해요. 이는 exposition에 대한 단위 테스트를 쓰기도 더 쉽게 만들어요.

메트릭과 샘플은 exposition에서 나타나고 사라져서는 안(SHOULD NOT) 돼요. 예를 들어 카운터는 역사가 있을 때만 유용해요. 원칙적으로 주어진 메트릭은 프로세스가 시작될 때부터 종료될 때까지 exposition에 존재해야 해요. 주어진 프로세스의 수명 동안 MetricFamily가 가질 메트릭을 미리 아는 것은 종종 불가능하지만(예: 지연 시간 히스토그램의 레이블 값인 HTTP 경로는 런타임에 최종 사용자가 제공), counter형 메트릭이 일단 노출되면 프로세스가 종료될 때까지 계속 노출되어야 해요. 카운터가 증가를 받지 않는다고 해서 여전히 현재 값을 가진다는 것이 무효화되지는 않아요. 주어진 메트릭 노출을 중단하는 것이 말이 되는 경우가 있으며, 누락 데이터 섹션을 참조하세요.

일반적으로 MetricFamily의 타입을 바꾸거나 그 메트릭에서 레이블을 추가·제거하는 것은 수집기에게 깨뜨리는 변경이 될 거예요.

주목할 만한 예외는 Info MetricPoint의 값에 레이블을 추가하는 것이 깨뜨리지 않는다는 것이에요. 이는 추가 레이블 값이 있는 완전히 새로운 info 메트릭을 만들어야 강제받지 않고, 기존 Info MetricFamily에 추가 정보를 추가하는 것이 말이 되는 곳에 추가할 수 있게 하기 위해서예요. 수집기 시스템은 그런 추가에 탄력적이도록 보장해야 해요.

MetricFamily의 Help를 바꾸는 것은 깨뜨리지 않아요. 가능한 값에 대해 float와 int 사이를 전환하는 것은 깨뜨리지 않아요. stateset에 새 상태를 추가하는 것은 깨뜨리지 않아요. 메트릭 이름을 바꾸지 않는 단위 메타데이터 추가는 깨뜨리지 않아요.

히스토그램 버킷은 exposition 간에 바뀌어서는 안(SHOULD NOT) 돼요. 이는 성능 문제를 일으키고 수집기를 깨뜨리고 일으킬 가능성이 높기 때문이에요. 유사하게 애플리케이션의 일관된 이진·환경의 모든 exposition은 주어진 Histogram MetricFamily에 대해 같은 버킷을 가져야(SHOULD) 해요. 그래서 수집기가 이질적인 버킷에 대한 히스토그램 병합 논리를 구현할 필요 없이 모든 수집기가 집계할 수 있어야 해요. 예외는 버킷에 대한 가끔의 수동 변경일 수 있는데, 이는 깨뜨리는 것으로 간주되지만 새 소프트웨어 릴리스로 성능 특성이 변할 때 유효한 트레이드오프일 수 있어요.

변경이 기술적으로 깨뜨리지 않더라도 여전히 비용을 수반해요. 예를 들어 빈번한 변경은 수집기의 성능 문제를 일으킬 수 있어요. exposition마다 변하는 Help 문자열은 각 Help 값이 저장되게 할 수 있어요. int와 float 값 사이를 빈번히 전환하면 효율적인 압축을 막을 수 있어요.

NaN

NaN은 OpenMetrics에서 다른 숫자와 같은 숫자이며, 보통 0으로 나누기에서 나와요. 예를 들어 최근에 관측이 없었다면 summary quantile이 그렇지요. NaN은 OpenMetrics에서 특별한 의미가 없고, 특히 누락되거나 그 외 나쁜 데이터의 마커로 사용해서는 안(MUST NOT) 돼요.

누락 데이터

데이터가 더 이상 존재하지 않는 유효한 경우가 있어요. 예를 들어 파일시스템이 마운트 해제되어 여유 디스크 공간에 대한 Gauge 메트릭이 더 이상 존재하지 않을 수 있어요. 이 상황에 대한 특별한 마커나 신호는 없어요. 이후의 exposition은 단순히 이 메트릭을 포함하지 않아요.

Exposition 성능

메트릭은 합리적인 시간 안에 수집될 수 있을 때만 유용해요. 노출하는 데 몇 분이 걸리는 메트릭은 유용한 것으로 간주되지 않아요.

경험 법칙으로 exposition은 1초를 넘지 않아야(SHOULD) 해요.

OpenMetrics를 통해 직렬화된 레거시 시스템의 메트릭은 더 오래 걸릴 수 있어요. 이런 이유로 하드 성능 가정을 할 수 없어요.

Exposition은 가장 최근 상태여야(SHOULD) 해요. 예를 들어 exposition 요청을 서비스하는 스레드는 가능한 한 캐싱을 우회할 수 있는 한 캐시된 값에 의존해서는 안(SHOULD NOT) 돼요.

동시성

고가용성과 임시 접근을 위해 일반적인 접근은 여러 수집기를 두는 것이에요. 이를 지원하려면 동시 exposition을 지원해야(MUST) 해요. 동시 시스템에 대한 모든 BCP를 따라야(SHOULD) 하며, 흔한 함정에는 데드락, 경쟁 조건, exposition이 동시에 진행되는 것을 막는 과도하게 거친 잠금이 포함돼요.

메트릭 이름 짓기와 네임스페이스

우리는 메트릭과 레이블 이름의 이해 가능성, 충돌 회피, 간결함 사이의 균형을 목표로 해요. 이름은 밑줄로 분리되므로 메트릭 이름은 "snake_case"가 돼요.

예를 들어 "http_request_seconds"는 간결하지만 많은 수의 애플리케이션 사이에서 충돌할 것이고 이 메트릭이 정확히 무엇을 측정하는지도 불분명해요. 예를 들어 복잡한 시스템에서 인증 미들웨어 앞인지 뒤인지일 수 있어요.

메트릭 이름은 그것이 나온 코드 조각을 나타내야 해요. 그래서 "A Company Manufacturing Everything"이라는 회사는 코드의 모든 메트릭에 "acme_" 접두사를 붙일 수 있고, 지연 시간을 측정하는 HTTP 라우터 라이브러리가 있다면 "acme_http_router_request_seconds" 같은 메트릭을 가질 수 있으며, Help 문자열은 그것이 전체 지연 시간임을 나타내요.

모든 애플리케이션 간의 모든 잠재적 충돌을 막는 것이 목표는 아니에요. 그것은 메트릭 네임스페이스의 전역 레지스트리나 DNS 기반의 매우 긴 네임스페이스 같은 무거운 해결책을 요구할 것이기 때문이에요. 오히려 목표는 가벼운 비공식 접근을 유지해서, 주어진 애플리케이션에 대해 그것이 구성하는 라이브러리 간에 충돌이 없을 가능성이 매우 높은 것이에요.

전체적으로 주어진 모니터링 시스템 배포에 걸쳐 같은 메트릭 이름이 서로 다른 것을 의미하는 충돌이 드문 것이 목표예요. 예를 들어 acme_http_router_request_seconds는 A Company Manufacturing Everything이 개발한 수백 개의 서로 다른 애플리케이션에 끝날 수 있으며, 이것은 정상이에요. Another Corporation Making Entities도 HTTP 라우터에서 acme_http_router_request_seconds 메트릭 이름을 사용한다면 그것도 괜찮아요. 두 회사의 애플리케이션이 같은 모니터링 시스템으로 모니터링된다면 충돌은 바람직하지 않지만, 어떤 애플리케이션도 두 이름을 모두 노출하려 하지 않고 어떤 타깃도 같은 메트릭 이름을 (잘못) 두 번 노출하려 하지 않으므로 허용 가능해요. 애플리케이션이 My Example Company와 Mega Exciting Company 둘 다의 HTTP 라우터 라이브러리를 포함하고 싶다면 문제가 되고, 메트릭 이름 중 하나를 어떻게든 바꿔야 해요.

따름정리로, 라이브러리가 더 공개적일수록 그 메트릭 이름이 더 잘 네임스페이스 되어 그런 시나리오의 위험을 줄여야 해요. acme_는 회사 내부 사용에 나쁜 선택은 아니지만, 그런 회사는 회사 밖에 공유하는 코드에는 예를 들어 acmeverything_이나 acorpme_ 같은 접두사를 선택할 수 있어요.

회사나 조직으로 네임스페이스한 후, 네임스페이스와 이름 짓기는 위의 http_router 라이브러리처럼 필요에 따라 라이브러리/하위시스템/애플리케이션으로 프랙탈적으로 계속되어야 해요. 목표는 코드베이스의 전체 구조에 익숙하다면 주어진 메트릭의 계측이 메트릭 이름으로 어디에 있는지 좋게 추측할 수 있는 것이에요.

매우 잘 알려진 기존 소프트웨어의 경우 소프트웨어 자체의 이름이 충분히 구별될 수 있어요. 예를 들어 DNS 소프트웨어에는 bind_가 아마 충분하며, isc_bind_가 더 흔한 이름짓기이지만요.

scrape_로 접두사된 메트릭 이름은 개별 exposition과 관련된 정보를 붙이기 위해 수집기가 사용하므로, 애플리케이션이 직접 노출해서는 안 돼요. 이미 소비되어 범용 모니터링 시스템을 통과한 메트릭은 이후의 exposition에 그런 메트릭 이름을 포함할 수 있어요. exposer가 개별 exposition에 대한 정보를 제공하고 싶다면 myexposer_scrape_ 같은 메트릭 접두사를 사용할 수 있어요. 흔한 예는 exposer 관점에서 그 exposition이 얼마나 오래 걸렸는지에 대한 gauge myexposer_scrape_duration_seconds예요.

프로메테우스 생태계 내에서 모든 구현에서 일관된 프로세스당 메트릭 세트가 생겼으며, process_로 접두사됩니다. 예를 들어 열린 파일 ulimit의 경우 MetricFamily process_open_fds와 process_max_fds 게이지는 현재 값과 최대 값을 모두 제공해요. (이 이름은 레거시이며, 오늘 정의된다면 process_fds_open과 process_fds_limit이라고 불릴 가능성이 높아요.) 일반적으로 이렇게 동일한 의미를 가진 이름을 얻는 것은 매우 어려우므로, 서로 다른 계측은 서로 다른 이름을 사용해야 해요.

메트릭 이름의 중복을 피하세요. "metric", "timer", "stats", "counter", "total", "float64" 같은 부분 문자열을 피하세요 - 주어진 타입(그리고 가능하다면 단위)의 메트릭이 OpenMetrics를 통해 노출되는 것만으로 이런 정보는 이미 암시되므로 명시적으로 포함해서는 안 돼요. 같은 이유로 메트릭의 레이블 이름을 메트릭 이름에 포함해서는 안 되며, 추가로 모니터링 시스템에 의한 메트릭의 이후 집계가 그런 정보를 부정확하게 만들 수 있어요.

모니터링 시스템의 다른 계층의 구현 세부 사항을 계측에 포함된 메트릭 이름에 넣는 것을 피하세요. 예를 들어 MetricFamily 이름이 단지 어딘가에서 현재 OpenMetrics를 통해 노출된다는 이유로 "openmetrics" 문자열을 포함해서는 안 되고, 현재 모니터링 시스템이 프로메테우스라는 이유만으로 "prometheus"를 포함해서는 안 돼요.

레이블 네임스페이스

레이블 이름에 대해서는 회사나 라이브러리에 의한 명시적 네임스페이싱이 권장되지 않아요. 레이블 이름의 길이 증가를 고려할 때 메트릭 이름으로부터의 네임스페이싱이 이에 충분해요. 하지만 일반적인 충돌을 피하기 위한 약간의 최소한의 주의가 권장돼요.

region, zone, cluster, availability_zone, az, datacenter, dc, owner, customer, stage, service, team, job, instance, environment, env 같은 레이블 이름은 범용 모니터링 시스템이 추가할 수 있는 타깃 식별에 사용되는 레이블과 충돌할 가능성이 매우 높아요. 그것들을 피하려고 노력하고, 이러한 경우 최소한의 네임스페이싱을 추가하는 것이 적절할 수 있어요.

"type"이라는 레이블 이름은 매우 일반적이므로 피해야 해요. 예를 들어 HTTP 관련 메트릭에서 GET, POST, PUT 요청을 구별한다면 "method"가 더 나은 레이블 이름이 될 거예요.

HELP, TYPE, UNIT 같은 메트릭 이름에 대한 메타데이터는 있지만 레이블 이름에 대한 메타데이터는 없어요. 이는 이득이 거의 없는데 형식을 부풀리는 것이 될 것이기 때문이에요. 대역외 문서화는 exposer가 이것을 수집기에게 제공할 수 있는 한 가지 방식이에요.

메트릭 이름 대 레이블

MetricFamily 내 여러 메트릭 또는 여러 MetricFamily를 사용하는 것이 말이 되는 상황이 있어요. MetricFamily의 합산이나 평균은 항상 유용한 것은 아니더라도 의미가 있어야 해요. 예를 들어 전압과 팬 속도를 섞는 것은 의미가 없어요.

상기시킬 점으로, OpenMetrics는 수집기가 데이터를 처리하고 집계를 수행할 수 있다는 가정으로 만들어졌어요.

다른 메트릭과 함께 총 합을 노출하는 것은 잘못된 것이에요. 다운스트림 수집기에서 집계 시 이중 계산이 되기 때문이에요.

wrong_metric{label="a"} 1
wrong_metric{label="b"} 6
wrong_metric{label="total"} 7

메트릭의 레이블은 고유성을 보장하는 데 필요한 최소한이어야 해요. 추가 레이블마다 사용자가 다운스트림에서 작업할 레이블을 결정할 때 고려해야 할 레이블이 하나 더 생기기 때문이에요. 많은 MetricFamily에 적용될 수 있는 레이블은 데이터베이스 {{정규화}}와 유사하게 _info 메트릭으로 옮겨지는 후보예요. 메트릭의 거의 모든 사용자가 추가 레이블을 원할 것으로 기대된다면 모든 MetricFamily에 추가하는 것이 더 나은 트레이드오프일 수 있어요. 예를 들어 전체 SQL 문의 해시를 포함하는 레이블로 고유성이 제공된 서로 다른 SQL 문과 관련된 MetricFamily가 있다면, 사람이 읽을 수 있도록 SQL 문의 처음 500자를 가진 다른 레이블을 가지는 것이 괜찮아요.

경험상 다운스트림 수집기는 하나의 MetricFamily 안에서 {result="success"}와 {result="failure"} 레이블을 사용하는 것보다 분리된 total과 failure MetricFamily로 작업하는 것을 더 쉽게 여겨요. 또한 전이중 시스템이 흔하고 다운스트림 수집기가 그 값들을 집계보다는 개별적으로 더 신경 쓸 가능성이 높으므로 분리된 read/write와 send/receive MetricFamily를 노출하는 것이 보통 더 좋아요.

이 모든 것이 들리는 것처럼 쉽지는 않아요. exposition과 노출되는 시스템 양쪽에서 도메인별 전문가의 경험과 엔지니어링 트레이드오프가 좋은 균형을 찾기 위해 요구되는 영역이에요.

메트릭과 레이블 이름 문자

OpenMetrics는 기존에 널리 채택된 프로메테우스 텍스트 exposition 형식과 주변에 형성된 생태계 위에 구축돼요. 역호환성은 핵심 설계 목표예요. 프로메테우스 텍스트 형식이 지원하는 문자 세트를 확장하거나 축소하는 것은 그 목표에 역행할 거예요. 역호환성을 깨는 것은 와이어 형식보다 더 넓은 함의를 가질 거예요. 특히 프로메테우스 생태계 내에서 전송되는 데이터로 작업하기 위해 만들어지거나 채택된 쿼리 언어는 이 정확한 문자 세트에 의존해요. 레이블 값은 전체 UTF-8을 지원하므로 형식이 다국어 메트릭을 표현할 수 있어요.

메타데이터 타입

메타데이터는 서로 다른 출처에서 올 수 있어요. 수년에 걸쳐 두 가지 주요 출처가 생겼어요. 기능적으로 종종 같지만, 개념적 차이를 말하는 것이 이해에 도움이 돼요.

"타깃 메타데이터"는 exposer 외부에 흔히 존재하는 메타데이터예요. 흔한 예는 서비스 디스커버리, CMDB 등에서 오는 데이터로, 데이터센터 지역, 서비스가 특정 배포의 일부인지, 프로덕션인지 테스트인지 같은 정보예요. 이것은 exposer나 수집기가 이 메타데이터를 포착하는 레이블을 모든 메트릭에 추가해 달성할 수 있어요. 수집기를 통해 하는 것이 더 유연하고 오버헤드가 적으므로 선호돼요. 유연성 측면에서 하드웨어 유지보수 팀은 머신이 위치한 서버 랙에 관심이 있을 수 있는 반면, 그 머신을 사용하는 데이터베이스 팀은 프로덕션 데이터베이스의 복제본 2번인지에 관심이 있을 수 있어요. 오버헤드 측면에서 이 정보를 하드코딩하거나 구성하는 것은 추가 배포 경로가 필요해요.

"exposer 메타데이터"는 exposer 내부에서 오는 것이에요. 흔한 예는 소프트웨어 버전, 컴파일러 버전, Git commit SHA예요.

푸시 기반과 풀 기반 시스템 모두에서 타깃 메타데이터 지원

푸시 기반 소비에서는 exposer가 관련 타깃 메타데이터를 수집기에 제공하는 것이 일반적이에요. 풀 기반 소비에서는 푸시 기반 접근을 취할 수 있지만, 더 일반적으로 수집기는 타깃의 메타데이터를 머신 데이터베이스나 서비스 디스커버리 시스템 같은 곳에서 이미 사전에 알고 있고, exposition을 소비하면서 그것을 메트릭과 연관시켜요.

OpenMetrics는 무상태이고 모든 수집기에 같은 exposition을 제공하며, 이는 푸시 스타일 접근과 충돌해요. 추가로 푸시 스타일 접근은 원치 않는 메타데이터가 노출되므로 풀 스타일 수집기를 깨뜨릴 거예요.

한 가지 접근은 푸시 스타일 수집기가 운영자 구성(예: HTTP 헤더)에 기반해 대역외로 타깃 메타데이터를 제공하는 것이에요. 이것은 푸시 스타일 수집기에게 타깃 메타데이터를 운반하고 이 표준에 의해 배제되지 않지만, 풀 스타일 수집기가 자신의 타깃 메타데이터를 사용해야 함에도 exposer 자신이 알고 있는 메타데이터에 접근하는 것이 여전히 종종 유용하다는 단점이 있어요.

선호되는 해결책은 이 타깃 메타데이터를 exposition의 일부로 제공하되 exposition 전체에 영향을 주지 않는 방식으로 하는 것이에요. Info MetricFamily가 이를 위해 설계됐어요. exposer는 레이블이 없는 단일 Metric과 메타데이터를 가진 "target"이라고 하는 Info MetricFamily를 포함할 수 있어요. 텍스트 형식의 예:

# TYPE target info
# HELP target Target metadata
target_info{env="prod",hostname="myhost",datacenter="sdc",region="europe",owner="frontend"} 1

exposer가 이 목적을 위해 이 메트릭을 제공할 때 exposition에서 처음이어야(SHOULD) 해요. 효율성을 위한 것이에요. 그것에 의존하는 수집기가 exposition의 나머지를 버퍼링하지 않고 그것의 내용에 기반한 비즈니스 로직을 적용하기 전에 나머지를 버퍼링할 필요가 없도록 하기 위해서예요.

Exposer는 특정 수집기를 위해 명시적으로 구성되지 않는 한 exposition의 모든 메트릭에 타깃 메타데이터 레이블을 추가해서는 안(MUST NOT) 돼요. Exposer는 타깃 메타데이터에 기반해 MetricFamily 이름에 접두사를 붙이거나 그 외에 MetricFamily 이름을 다르게 해서는 안(MUST NOT) 돼요. 일반적으로 같은 레이블이 exposition의 모든 메트릭에 나타나서는 안 되지만, 이것이 창발적 행동의 결과일 수 있는 드문 경우가 있어요. 유사하게 exposer의 모든 MetricFamily 이름이 매우 작은 exposition에서 접두사를 공유할 수 있어요. 예를 들어 A Company Manufacturing Everything이 Go 언어로 작성한 애플리케이션은 acme_, go_, process_ 접두사와 사용 중인 제3자 라이브러리의 메트릭 접두사를 가진 메트릭을 포함할 가능성이 높아요.

Exposer는 exposer 메타데이터를 Info MetricFamily로 노출할 수 있어요.

위 논의는 개별 exposer의 맥락이에요. 범용 모니터링 시스템의 exposition은 많은 개별 타깃의 메트릭을 포함할 수 있으므로 여러 target info 메트릭을 노출할 수 있어요. 메트릭은 수집의 일부로 이미 타깃 메타데이터가 레이블로 추가되었을 수 있어요. 메트릭 이름은 타깃 메타데이터에 기반해 다르게 되어서는 안(MUST NOT) 돼요. 예를 들어 모든 메트릭이 스테이징 환경의 타깃에서 왔더라도 모두 staging_으로 접두사로 끝나는 것은 잘못된 것이에요.

클라이언트 계산과 파생 메트릭

Exposer는 어떤 수학이나 계산도 수집기에 맡겨야 해요. 주목할 만한 예외는 Summary quantile인데, 불행히도 역호환성을 위해 필요해요. Exposition은 임의의 시간 기간에 걸쳐 유용한 원시 값이어야 해요.

예를 들어 지난 5분 동안 카운터의 증가 평균 속도의 gauge를 노출해서는 안 돼요. 수집기가 exposition 간에 소비한 데이터 포인트에 대해 증가를 계산하게 하는 것이 더 나은 수학적 속성을 가지고 스크레이프 실패에 더 탄력적이에요.

또 다른 예는 히스토그램/summary의 평균 사건 크기예요. 애플리케이션이 시작된 이후 또는 메트릭이 생성된 이후 카운터의 증가 평균 속도를 노출하는 것은 앞선 예의 문제가 있고 집계도 막아요.

표준 편차도 이 범주에 들어가요. 제곱의 합을 카운터로 노출하는 것이 올바른 접근일 거예요. 히스토그램 값으로 이 표준에 포함되지 않았는데, 64비트 부동 소수점 정밀도가 실제로 이것이 작동하기에 충분하지 않기 때문이에요. 제곱 때문에 정밀도 측면에서 53비트 가수부의 절반만 사용할 수 있어요. 예를 들어 초당 1만 이벤트를 관측하는 히스토그램은 2시간 안에 정밀도를 잃을 거예요. 64비트 정수도 부동 소수점 소수점의 손실 때문에 더 나을 것이 없어요. 보통 초 길이의 이벤트를 추적하는 나노초 해상도 정수는 19회 관측 후 오버플로할 것이기 때문이에요. 이 설계 결정은 128비트 부동 소수점 숫자가 흔해지면 다시 검토될 수 있어요.

또 다른 예는 요청 실패 비율을 노출하는 것을 피하고, 대신 실패한 요청과 총 요청에 대해 분리된 카운터를 노출하는 것이에요.

숫자 타입

초당 백만 번 증가하는 카운터는 53비트 가수부가 있으므로 float64로 정밀도를 잃기 시작하는 데 한 세기가 넘게 걸릴 거예요. 하지만 100Gbps 네트워크 인터페이스의 옥텟 처리량 정밀도는 약 20시간 안에 float64로 잃기 시작할 수 있어요. 100Gbps 네트워크 인터페이스에 대해 수년에 걸쳐 1KB의 정밀도를 잃는 것이 실제로 문제가 될 가능성은 낮지만, 이렇게 높은 처리량의 정수 데이터에는 int64가 선택지예요.

Summary quantile은 float64여야 해요. 그것들은 추정치라서 근본적으로 부정확하기 때문이에요.

타임스탬프 노출

OpenMetrics의 핵심 가정 중 하나는 exposer가 노출하는 것의 가장 최신 스냅샷을 노출한다는 것이에요.

노출된 데이터에 타임스탬프를 붙이는 제한된 사용 사례가 있지만, 이것은 매우 드물어요. 이전에 타임스탬프가 붙었던 데이터, 특히 범용 모니터링 시스템에 수집된 데이터는 타임스탬프를 가질 수 있어요. 라이브나 원시 데이터는 타임스탬프를 가져서는 안 돼요. exposition 간에 같은 메트릭 MetricPoint 값을 같은 타임스탬프로 노출하는 것은 유효하지만, 밑바탕의 메트릭이 이제 없어졌다면 그렇게 하는 것은 유효하지 않아요.

시간 동기화는 어려운 문제이고 데이터는 각 시스템에서 내부적으로 일관되어야 해요. 따라서 수집기는 exposer 장치의 시스템 시간에 기반하기보다 자신의 관점에서 현재 타임스탬프를 데이터에 붙일 수 있어야 해요.

타임스탬프가 있는 메트릭으로는 일반적으로 exposition 간에 메트릭이 언제 사라졌는지 감지할 수 없어요. 하지만 타임스탬프가 없는 메트릭으로는 수집기가 메트릭이 더 이상 없는 exposition으로부터 자신의 타임스탬프를 사용할 수 있어요.

이 모든 것은 일반적으로 MetricPoint 타임스탬프를 노출해서는 안 된다는 것이고, 수집기에 자신이 수집하는 샘플에 자신의 타임스탬프를 적용하는 것이 맡겨져야 한다는 것이에요.

메트릭이 마지막으로 변경된 시점 추적

초기화된 다음 나중에 시간 123에 1씩 증가된 my_counter라는 카운터가 있다고 가정해요. 텍스트 형식으로 올바르게 노출하는 방법:

# HELP my_counter Good increment example
# TYPE my_counter counter
my_counter_total 1

부모 섹션에 따라 수집기는 자신의 타임스탬프를 붙이는 것이 자유로워야 하므로, 이는 잘못된 것이에요:

# HELP my_counter Bad increment example
# TYPE my_counter counter
my_counter_total 1 123

카운터의 마지막 변경의 특정 시간이 중요해지는 경우, 이렇게 하는 것이 올바른 방법이에요:

# HELP my_counter Good increment example
# TYPE my_counter counter
my_counter_total 1
# HELP my_counter_last_increment_timestamp_seconds When my_counter was last incremented
# TYPE my_counter_last_increment_timestamp_seconds gauge
# UNIT my_counter_last_increment_timestamp_seconds seconds
my_counter_last_increment_timestamp_seconds 123

마지막 변경의 타임스탬프를 값으로 자신의 Gauge에 넣으면 수집기는 두 메트릭 모두에 자신의 타임스탬프를 붙이는 것이 자유로워요.

경험상 절대 타임스탬프(epoch도 여기서는 절대로 간주됨)를 노출하는 것이 경과 시간, 이후 초 등보다 더 견고함을 보여줐어요. 어느 경우든 게이지가 될 거예요. 예를 들어:

# TYPE my_boot_time_seconds gauge
# HELP my_boot_time_seconds Boot time of the machine
# UNIT my_boot_time_seconds seconds
my_boot_time_seconds 1256060124

가 아래보다 낫습니다:

# TYPE my_time_since_boot_seconds gauge
# HELP my_time_since_boot_seconds Time elapsed since machine booted
# UNIT my_time_since_boot_seconds seconds
my_time_since_boot_seconds 123

반대로 exemplar 타임스탬프에 대한 모범 사례 제한은 없어요. 경쟁 조건이나 장치 간의 완벽하게 동기화되지 않은 시간 때문에 exemplar 타임스탬프가 수집기의 시스템 시계나 같은 exposition의 다른 메트릭에 비해 약간 미래로 보일 수 있다는 점을 염두에 두세요. 유사하게 MetricPoint에 대한 "_created"가 같은 MetricPoint에 대한 exemplar나 샘플 타임스탬프보다 약간 뒤에 나타나는 것처럼 보일 수 있어요.

나노초에서 초 해상도까지 전부를 지원하는 흔히 사용되는 모니터링 시스템이 있으므로, 초 해상도로 잘렸을 때 같은 타임스탬프를 가진 두 MetricPoint가 수집기에서 명백한 중복으로 보일 수 있다는 점을 염두에 두세요. 이 경우 가장 이른 타임스탬프를 가진 MetricPoint를 사용해야(MUST) 해요.

임계값

시스템에 대한 원하는 경계를 노출하는 것이 말이 될 수 있지만, 적절한 주의가 필요해요. 보편적으로 참인 값의 경우 그런 임계값에 대해 Gauge 메트릭을 내보내는 것이 말이 될 수 있어요. 예를 들어 데이터센터 HVAC 시스템은 현재 측정값, 설정점, 경고 설정점을 알고 있어요. 그것은 원하는 시스템 상태에 대해 전역적으로 유효하고 정확한 관점을 가져요. 반대로 일부 임계값은 규모, 배포 모델, 또는 시간이 지나면서 변할 수 있어요. 일정량의 CPU 사용량이 한 환경에서는 허용 가능하고 다른 환경에서는 바람직하지 않을 수 있어요. 값의 집계는 허용 가능한 값을 더 바꿀 수 있어요. 그런 시스템에서 경계를 노출하는 것은 역효과가 될 수 있어요.

예를 들어 큐의 최대 크기가 현재 큐의 항목 수와 함께 노출될 수 있어요:

# HELP acme_notifications_queue_capacity The capacity of the notifications queue.
# TYPE acme_notifications_queue_capacity gauge
acme_notifications_queue_capacity 10000
# HELP acme_notifications_queue_length The number of notifications in the queue.
# TYPE acme_notifications_queue_length gauge
acme_notifications_queue_length 42

크기 제한

이 표준은 단일 exposition이 노출하는 샘플 수, 존재할 수 있는 레이블 수, stateset이 가질 수 있는 상태 수, info 값의 레이블 수, 메트릭 이름/레이블 이름/레이블 값/help 문자 제한에 대한 특정 제한을 규정하지 않아요.

특정 제한은 합리적인 사용 사례를 막을 위험이 있어요. 예를 들어 주어진 exposition이 범용 모니터링 시스템을 통과한 후 적절한 수의 레이블을 가질 수 있지만, 몇 개의 타깃 레이블이 추가되어 제한을 넘어설 수 있어요. 이와 같은 숫자에 대한 특정 제한은 범용 모니터링 시스템의 실제 비용이 어디에 있는지도 포착하지 못해요. 따라서 이 지침은 exposer와 수집기 모두 무엇이 합리적인지 이해하는 것을 돕기 위한 것이에요.

반면 어떤 차원에서 너무 큰 exposition은 노출된 메트릭의 이득에 비해 상당한 성능 문제를 일으킬 수 있어요. 따라서 단일 exposition의 크기에 대한 몇 가지 지침이 유용할 거예요.

수집기는 특히 공격이나 중단을 막기 위해 스스로 제한을 부과할 수(MAY) 있어요. 그래도 수집기는 합리적인 사용 사례를 고려해야 하고 그것들에 불균형하게 영향을 주지 않도록 노력해야 해요. 단일 값/메트릭/exposition이 그런 제한을 초과하면 전체 exposition을 거부해야 해요.

일반적으로 범용 모니터링 시스템의 시계열 데이터 수집 성능에 영향을 주는 세 가지가 있어요: 고유 시계열 수, 그 시리즈의 시간에 따른 샘플 수, 메트릭 이름, 레이블 이름, 레이블 값, HELP 같은 고유 문자열 수. 수집기는 얼마나 자주 수집할지 제어할 수 있으므로 그 측면은 더 고려할 필요가 없어요.

고유 시계열 수는 텍스트 형식의 비주석 라인의 수와 대략 같아요. 2020년 기준으로 총 1000만 시계열은 큰 양으로 간주되고 단일 인스턴스 수집기의 상한의 크기 순서로 흔히 여겨져요. 단일 exposition은 실사 없이 1만 시계열을 넘어서는 안 돼요. 한 가지 일반적인 고려사항은 수평 확장이에요: 인스턴스 수를 1-2 자릿수로 확장하면 어떻게 되나요? 단일 배포에 천 개의 top-of-rack 스위치를 가진 것은 30년 전에는 상상하기 어려웠어요. 타깃이 싱글턴이라면(예: 전체 클러스터와 관련된 메트릭 노출) 수십만 시계열이 합리적일 수 있어요. 중요한 것은 고유 MetricFamily 수나 개별 레이블/버킷/stateset의 카디널리티가 아니라 시계열의 총 크기 순서예요. 각각 메트릭 하나를 가진 게이지 1000개는 메트릭 1000개를 가진 단일 게이지만큼 비싸요.

특정 타입의 모든 타깃이 같은 시계열 세트를 노출한다면, 각 추가 타깃의 문자열은 대부분의 합리적으로 현대적인 모니터링 시스템에 증분 비용을 부과하지 않아요. 하지만 각 타깃이 고유한 문자열을 가지면 그런 비용이 있어요. 극단적인 예로, 많은 타깃이 사용하는 단일 10k 문자 메트릭 이름은 그 자체만으로 실제로 문제가 될 가능성이 매우 낮아요. 반대로 각각 고유한 36자 UUID를 노출하는 천 개의 타깃은 현대적 접근을 가정했을 때 저장할 문자열 측면에서 그 단일 10k 문자 메트릭 이름보다 3배 이상 비싸요. 추가로 이 문자열이 시간에 따라 변하면 이전 문자열을 적어도 잠시 동안 저장해야 하므로 추가 비용이 발생해요. 마지막 문단의 1000만 시계열을 가정하면, 시간당 100MB의 고유 문자열은 이벤트 로깅에 더 가까운 사용 사례일 수 있음을 나타낼 수 있는데, 그 경우 사용 사례는 메트릭 시계열보다는 이벤트 로깅에 더 가깝게 될 거예요.

트레이스 스팬 데이터와 다른 이벤트 로깅을 위한 기능의 오용을 막기 위해 exemplar 길이에 하드 128 UTF-8 문자 제한이 있어요.

보안

구현자는 인증, 권한 부여, 회계를 제공하는 것을 선택할 수(MAY) 있어요. 제공하기로 선택한다면 이것은 OpenMetrics 밖에서 처리해야(SHOULD) 해요.

모든 exposer 구현은 TLS 1.2 이상으로 HTTP 트래픽을 보호할 수 있어야(SHOULD) 해요. exposer 구현이 암호화를 지원하지 않으면 운영자는 가능한 한 역방향 프록시, 방화벽, 및/또는 ACL을 사용해야(SHOULD) 해요.

메트릭 exposition은 최종 사용자에게 노출되는 프로덕션 서비스와 독립적이어야 해요. 따라서 OpenMetrics를 사용하는 공개적으로 노출된 서비스에 TCP/80, TCP/443, TCP/8080, TCP/8443 같은 포트에 /metrics 엔드포인트를 두는 것은 일반적으로 권장되지 않아요.

IANA

현재 대부분의 프로메테우스 exposition 형식 구현이 {{PrometheusPorts}}의 비공식 레지스트리의 IANA 비등록 포트를 사용하지만, OpenMetrics는 잘 정의된 포트에서 찾을 수 있어요.

데이터를 노출하는 클라이언트를 위해 IANA가 할당한 포트는 <9099는 역사적 일관성을 위해 요청됨>이에요.

공통 IP 주소와 포트에서 둘 이상의 메트릭 엔드포인트에 도달 가능해야 한다면 운영자는 localhost 주소 위에서 exposer와 통신하는 역방향 프록시 사용을 고려할 수 있어요. 멀티플렉싱을 쉽게 하려면 엔드포인트는 경로에 자신의 이름을 담아야(SHOULD) 해요. 즉 /node_exporter/metrics요. Exposition은 "푸시 기반과 풀 기반 시스템 모두에서 타깃 메타데이터 지원"에서 다루는 이유와 단일 실패 지점 없이 독립적인 수집을 허용하기 위해 하나의 exposition으로 결합해서는 안(SHOULD NOT) 돼요.

OpenMetrics는 application/openmetrics-textapplication/openmetrics-proto 두 가지 MIME 타입을 등록하고 싶어요.

더 알아보기 (Learn more)