OpenMetrics 2.0

  • バージョン: 2.0
  • ステータス: ドラフト
  • 日付: 未定
  • 著者: リチャード・ハートマン、ベン・コキー、ブライアン・ブラジル、ロブ・スキリングトン

2012年に誕生したPrometheusは、2015年以来クラウドネイティブオブザーバビリティのデファクトスタンダードとなっています。Prometheusの設計の中心的な要素は、2014年以来安定している「Prometheus exposition format 0.0.4」と呼ばれるテキストメトリックのエクスポート形式です。この形式では、生成しやすく、取り込みやすく、人間が理解しやすいように特別な配慮がされています。2020年現在、700を超える公開されているエクスポーター、数えきれないほどの非公開のエクスポーター、そして数千のネイティブライブラリ統合がこの形式を使用しています。さまざまなプロジェクトや企業の数十のインジェスターが、この形式の消費をサポートしています。

OpenMetricsでは、IETFに導入することを明確な目的として、仕様を整理し厳格化しています。私たちは、最小限の、ほとんど後方互換性のある、十分に検討された変更を導入しつつ、広く自然に採用されている既存の標準を文書化しています。2020年現在、数十のエクスポーター、統合、インジェスターが既にOpenMetricsを使用しており、優先的にOpenMetricsをネゴシエートしています。

エコシステムにおける幅広い採用と大幅な調整の必要性を考慮すると、Prometheus exposition format 0.0.4またはOpenMetrics 1.0への大幅な変更は範囲外と見なされます。

このドキュメントは初期ドラフトであり、大幅な変更が予想されます。Prometheus OM 2.0作業グループへの参加方法については、こちらをお読みください。

概要

メトリックは特定の種類のテレメトリーデータです。これらは一連のデータの現在の状態のスナップショットを表します。個々のイベントに関する記録や情報に焦点を当てるログやイベントとは異なります。

OpenMetricsは主にワイヤーフォーマットであり、そのフォーマットのための特定のトランスポートには依存しません。このフォーマットは定期的に消費され、連続した公開で意味を持つことが期待されます。

実装者は、特定のプロセスまたはデバイスの文書化されたURLへのシンプルなHTTP GETリクエストに応答して、OpenMetricsテキスト形式でメトリックを公開**しなければなりません**。このエンドポイントは「/metrics」と**すべきです**。実装者は、HTTP経由でオペレーターが構成したエンドポイントにメトリックセットを定期的にプッシュするなど、他の方法でOpenMetrics形式のメトリックを公開**することもできます**。

メトリックと時系列

この標準は、すべてのシステム状態を数値として表現します。一般的な例としては、カウント、現在の値、列挙、ブール状態などがあります。メトリックとは対照的に、単一のイベントは特定の時間に発生します。メトリックは時間的にデータを集計する傾向があります。これにより情報が失われる可能性がありますが、オーバーヘッドの削減は、多くの最新の監視システムで一般的に選択されるエンジニアリング上のトレードオフです。

時系列とは、時間の経過とともに変化する情報の記録です。時系列は任意の文字列やバイナリデータをサポートできますが、このRFCの範囲は数値データのみです。

メトリック時系列の一般的な例としては、ネットワークインターフェースカウンター、デバイス温度、BGP接続状態、アラート状態などがあります。

データモデル

このセクションはABNFセクションと合わせて読む**必要があります**。両者の間に不一致がある場合、ABNFの制限が優先され**なければなりません**。これにより、テキストワイヤーフォーマットがサポートされる必要があるため、繰り返しが減ります。

データ型

OpenMetricsのメトリック値は、浮動小数点数または整数で**なければなりません**。この形式のインジェスターがfloat64のみをサポートする**可能性がある**ことに注意してください。非実数値NaN、+Inf、-Infはサポートされ**なければなりません**。NaNは欠落値と見なされ**てはなりません**が、ゼロによる除算を示すために使用**してもよい**です。

ブール値

ブール値は`1==true`、`0==false`に従わ**なければなりません**。

タイムスタンプ

タイムスタンプはUnix Epoch秒で**なければなりません**。負のタイムスタンプを使用**してもよい**です。

文字列

文字列は有効なUTF-8文字のみで構成され**なければならず**、長さはゼロで**あってもよい**です。NULL(ASCII 0x0)はサポートされ**なければなりません**。

ラベル

ラベルは文字列で構成されるキーと値のペアです。

アンダースコアで始まるラベル名は予約済みであり、この標準で指定されていない限り使用**してはなりません**。ラベル名はABNFセクションの制限に従わ**なければなりません**。

空のラベル値は、ラベルが存在しないかのように扱われる**べきです**。

ラベルセット

LabelSetはLabelで構成され**なければならず**、空で**あってもよい**です。LabelSet内ではラベル名は一意で**なければなりません**。

メトリックポイント

各MetricPointは、MetricFamilyのタイプに応じて、値のセットで構成されます。

エグザンプラー

ExemplarsはMetricSetの外部のデータへの参照です。一般的な使用例は、プログラムトレースのIDです。

ExemplarsはLabelSetと値で構成され**なければならず**、タイムスタンプを持た**なくてもよい**です。それらはMetricPointsのLabelSetおよびタイムスタンプとは異なっ**てもよい**です。

ExemplarのLabelSetのラベル名と値の合計の長さは、128 UTF-8文字コードポイントを超えては**なりません**。エクザンプラのテキストレンダリングにおけるその他の文字(`",=`など)は、実装の簡素化とテキスト形式とプロトコル形式間の一貫性のために、この制限には含まれません。

インジェスターはエクザンプラを破棄**してもよい**です。

メトリック

メトリックは、MetricFamily内のユニークなLabelSetによって定義されます。メトリックは1つ以上のMetricPointのリストを含ま**なければなりません**。特定のMetricFamilyで同じ名前を持つメトリックは、そのLabelSetに同じラベル名のセットを持つ**べきです**。

MetricPointは明示的なタイムスタンプを持つ**べきではありません**。

メトリックに複数のMetricPointが公開されている場合、そのMetricPointのタイムスタンプは単調増加で**なければなりません**。

MetricFamily

MetricFamilyは0個以上のメトリックを持つ**こともできます**。MetricFamilyは名前、HELP、TYPE、およびUNITのメタデータを持た**なければなりません**。MetricFamily内のすべてのメトリックは一意のLabelSetを持た**なければなりません**。

名前

MetricFamily名は文字列であり、MetricSet内で一意で**なければなりません**。名前はsnake_caseで**あるべきです**。メトリック名はABNFセクションの制限に従わ**なければなりません**。

MetricFamily名内のコロンは、MetricFamilyが汎用監視システムの計算または集計の結果であることを示すために予約済みです。

アンダースコアで始まるMetricFamily名は予約済みであり、この標準で指定されていない限り使用**してはなりません**。

サフィックス

MetricFamilyの名前は、MetricSet内のテキスト形式で別のMetricFamilyとのサンプルメトリック名の潜在的な衝突を引き起こして**はなりません**(ABNFに従って)。例えば、「foo」というカウンターがある場合、「foo_total」というゲージはテキスト形式で「foo_total」を作成する可能性があります。

エクスポーターは、テキスト形式のサンプルメトリック名が使用するサフィックスと混同される可能性のある名前を避ける**べきです**。

  • 各タイプのサフィックスは次のとおりです。
  • カウンター: _total
  • サマリー: _count_sum、`` (空)
  • ヒストグラム: _count_sum_bucket
  • ゲージヒストグラム: _gcount_gsum_bucket
  • 情報: _info
  • ゲージ: `` (空)
  • ステートセット: `` (空)
  • 不明: `` (空)
タイプ

TypeはMetricFamilyのタイプを指定します。有効な値は「unknown」、「gauge」、「counter」、「stateset」、「info」、「histogram」、「gaugehistogram」、「summary」です。

単位

UnitはMetricFamilyの単位を指定します。空でない場合、それはMetricFamily名にアンダースコアで区切られた接尾辞で**なければなりません**。さらなる生成ルールによって、テキスト形式では中置になる可能性があることに注意してください。

ヘルプ

ヘルプは文字列であり、空で**ないべきです**。これは、人間が理解できるようにMetricFamilyの簡単な説明を提供するために使用され、ツールチップとして使用できるくらい短く**あるべきです**。

メトリックセット

MetricSetはOpenMetricsによって公開される最上位オブジェクトです。MetricFamilyで構成され**なければならず**、空で**あってもよい**です。

各MetricFamily名は一意で**なければなりません**。同じラベル名と値がMetricSet内のすべてのメトリックに現れる**べきではありません**。

MetricSet内のMetricFamilyに特定の順序は必要ありません。例えば、パフォーマンス上のトレードオフが意味をなす場合、エクスポージャーは人間が読みやすいように、アルファベット順にソート**してもよい**です。

存在する場合、以下の「プッシュベースおよびプルベースシステムの両方でターゲットメタデータをサポートする」セクションに従って、「target」というInfo MetricFamilyが最初に**あるべきです**。

メトリックタイプ

ゲージ

ゲージは、現在使用中のメモリバイト数やキュー内の項目数など、現在の測定値です。ゲージの場合、絶対値がユーザーにとって関心のあるものです。

タイプがゲージのメトリックのMetricPointは、単一の値を持た**なければなりません**。

ゲージは、時間の経過とともに増加、減少、または一定のまま**であることもあります**。たとえ常に一方向にしか進まないとしても、それらはゲージでありカウンターではない**可能性もあります**。ログファイルのサイズは通常増加するのみで、リソースは減少する**可能性もあり**、キューサイズの制限は一定である**こともあります**。

ゲージは、多くの状態を持ち、時間の経過とともに変化するenumをエンコードするために使用**することもできます**。これは最も効率的ですが、ユーザーフレンドリーではありません。

カウンター

カウンターは離散イベントを測定します。一般的な例としては、受信したHTTPリクエストの数、費やされたCPU秒数、送信されたバイト数などがあります。カウンターの場合、時間の経過とともにどれだけ早く増加しているかがユーザーにとって関心のあるものです。

タイプがカウンターのメトリックのMetricPointは、Totalと呼ばれる1つの値を持た**なければなりません**。Totalは非NaNであり、0から始まり、時間の経過とともに単調に非減少で**なければなりません**。

タイプがカウンターのメトリックのMetricPointは、Created Timestampと呼ばれるTimestamp値を持つ**べきです**。これは、インジェスターが新しいメトリックと以前に認識していなかった長期実行中のメトリックを区別するのに役立ちます。

メトリックのカウンターの合計のMetricPointは0にリセット**してもよい**です。存在する場合、対応する作成タイムスタンプもリセットのタイムスタンプに設定**しなければなりません**。

メトリックのカウンターの合計のMetricPointはエクザンプラを持つ**こともできます**。

ステートセット

StateSetsは、ビットセットとも呼ばれる一連の関連するブール値を表します。ENUMをエンコードする必要がある場合、これはStateSetを介して行う**こともできます**。

StateSetメトリックのポイントは複数の状態を含み**こともでき**、各状態に対して1つのブール値を含ま**なければなりません**。状態は文字列である名前を持ちます。

StateSetメトリックのLabelSetは、そのMetricFamilyの名前と同じラベル名を持って**はなりません**。

StateSetとしてエンコードされている場合、ENUMsはMetricPoint内にちょうど1つのTrueのブール値を持た**なければなりません**。

これは、enum値が時間の経過とともに変化し、Stateの数が数個程度を超えない場合に適しています。

StateSetsタイプのMetricFamiliesは、空のUnit文字列を持た**なければなりません**。

情報

情報メトリックは、プロセスのライフタイム中に変化**すべきではない**テキスト情報を公開するために使用されます。一般的な例としては、アプリケーションのバージョン、リビジョン管理のコミット、コンパイラのバージョンなどがあります。

InfoメトリックのMetricPointはLabelSetを含みます。Info MetricPointのLabelSetは、そのメトリックのLabelSetのラベル名と同じラベル名を持って**はなりません**。

Infoは、ネットワークインターフェースのタイプなど、時間の経過とともに値が変化しないENUMをエンコードするために使用**することもできます**。

InfoタイプのMetricFamiliesは、空のUnit文字列を持た**なければなりません**。

ヒストグラム

ヒストグラムは、HTTPリクエストの遅延、関数の実行時間、I/Oリクエストサイズなど、離散イベントの分布を測定します。

Histogram MetricPointは少なくとも1つのバケットを含ま**なければならず**、SumとCreated Timestampの値を持つ**べきです**。すべてのバケットはしきい値と値を持た**なければなりません**。

ヒストグラムのメトリックポイントは、+Infのしきい値を持つ1つのバケットを持た**なければなりません**。バケットは累積的で**なければなりません**。例えば、秒単位の要求遅延を表すメトリックの場合、しきい値1、2、3、+Infを持つバケットの値はvalue_1 <= value_2 <= value_3 <= value_+Infに従わ**なければなりません**。10個の要求がそれぞれ1秒かかった場合、1、2、3、+Infのバケットの値は10と等しく**なければなりません**。

+Infバケットはすべてのリクエストをカウントします。存在する場合、Sumの値は測定されたすべてのイベント値の合計と等しく**なければなりません**。MetricPoint内のバケットのしきい値は一意で**なければなりません**。

意味的には、Sumとバケットの値はカウンターであるため、NaNまたは負の値で**あってはなりません**。負のしきい値バケットを使用**してもよい**ですが、その場合、ヒストグラムのメトリックポイントは意味的にカウンターではなくなるため、合計値を含んで**はなりません**。バケットのしきい値はNaNと等しく**あってはなりません**。カウントとバケットの値は整数で**なければなりません**。

Histogram MetricPointはCreated Timestampと呼ばれるTimestamp値を持つ**べきです**。これは、インジェスターが新しいメトリックと以前に認識していなかった長期実行中のメトリックを区別するのに役立ちます。

ヒストグラムのメトリックのLabelSetは「le」ラベル名を持って**はなりません**。

バケット値はエクザンプラを持つ**こともできます**。バケットは累積的であり、監視システムがパフォーマンス/サービス拒否対策のために+Inf以外のバケットを破棄できるようにします。これにより粒度は失われますが、有効なヒストグラムであることには変わりありません。

各バケットはそれ以下またはそれと等しい値をカバーし、エクザンプラの値はこの範囲内になければ**なりません**。エクザンプラは最高値のバケットに配置される**べきです**。バケットは複数のエクザンプラを持って**はなりません**。

ゲージヒストグラム

GaugeHistogramsは現在の分布を測定します。一般的な例としては、キュー内のアイテムが待機している時間や、キュー内のリクエストのサイズなどがあります。

GaugeHistogram MetricPointは+Infのしきい値を持つ1つのバケットを持た**なければならず**、Gsum値を持つ**べきです**。すべてのバケットはしきい値と値を持た**なければなりません**。

GaugeHistogramのバケットは、Histogramのすべてのルールに従います。

GaugeHistogramのバケットとGsumは概念的にはゲージですが、バケット値は負の値またはNaNで**あってはなりません**。負のしきい値バケットが存在する場合、sumは負の値で**あってもよい**です。GsumはNaNで**あってはなりません**。バケット値は整数で**なければなりません**。

GaugeHistogramのメトリックのLabelSetは「le」ラベル名を持って**はなりません**。

バケット値はエクザンプラを持つことができます。

各バケットはそれ以下またはそれと等しい値をカバーし、エクザンプラの値はこの範囲内になければ**なりません**。エクザンプラは最高値のバケットに配置される**べきです**。バケットは複数のエクザンプラを持って**はなりません**。

サマリー

サマリーも離散イベントの分布を測定し、ヒストグラムが高価すぎる場合や、平均イベントサイズで十分な場合に使用**することもできます**。

既存の計測ライブラリには事前に計算されたパーセンタイルを公開し、ヒストグラムをサポートしないものがあるため、後方互換性のために使用**することもできます**。事前に計算されたパーセンタイルは集計可能ではなく、ユーザーはそれがカバーする時間枠を推測できないことが多いため、使用**すべきではありません**。

Summary MetricPointは、Count、Sum、Created Timestamp、および一連のパーセンタイルで構成**されることもあります**。

意味的には、CountとSumの値はカウンターであるため、NaNまたは負の値で**あってはなりません**。Countは整数で**なければなりません**。

CountまたはSum値を含むSummaryタイプのメトリックのMetricPointは、Created Timestampと呼ばれるTimestamp値を持つ**べきです**。これは、インジェスターが新しいメトリックと以前に認識していなかった長期実行中のメトリックを区別するのに役立ちます。Created Timestampはパーセンタイル値の収集期間に関連して**はなりません**。

Quantileは、Quantileを値にマップするものです。例えば、`myapp_http_request_duration_seconds`というメトリックのQuantile 0.95は0.2という値を持ち、これは95パーセンタイルのレイテンシが不明な時間枠で200msであることを意味します。関連する時間枠にイベントがない場合、Quantileの値はNaNでなければなりません(MUST)。QuantileのメトリックのLabelSetは、「quantile」というラベル名を持ってはなりません(MUST NOT)。Quantileは0から1の範囲(両端を含む)でなければなりません(MUST)。Quantileの値は負であってはなりません(MUST NOT)。Quantileの値は最近の値を表すべきです(SHOULD)。一般的には、過去5~10分間の値が該当します。

不明

Unknownは使用すべきではありません(SHOULD NOT)。Unknownは、サードパーティシステムからの個々のメトリックのタイプを決定できない場合に使用してもよいです(MAY)。

Unknownタイプのメトリックのポイントは、単一の値を持たなければなりません(MUST)。

データ転送とワイヤフォーマット

テキストワイヤフォーマットはサポートされなければならず(MUST)、デフォルトです。protobufワイヤフォーマットはサポートされてもよく(MAY)、ネゴシエーション後にのみ使用されなければなりません(MUST ONLY)。

OpenMetricsフォーマットは正規チョムスキー文法であり、高速で小型のパーサーを記述することを可能にします。テキストフォーマットは良好に圧縮され、protobufはすでにバイナリで効率的にエンコードされています。

部分的な、または無効なエクスポジションは、全体としてエラーと見なされなければなりません(MUST)。

プロトコルネゴシエーション

すべてのインジェスター実装は、TLS 1.2以降で保護されたデータを取り込むことができなければなりません(MUST)。すべてのエクスポージャーは、TLS 1.2以降で保護されたデータを出力できるべきです(SHOULD)。インジェスター実装は、TLSなしでHTTPからデータを取り込むことができるべきです(SHOULD)。すべての実装は、データを転送するためにTLSを使用すべきです(SHOULD)。

使用するOpenMetricsフォーマットのバージョンに関するネゴシエーションは帯域外で行われます。例えば、HTTP経由のプルベースのエクスポジションでは、標準のHTTPコンテンツタイプネゴシエーションが使用され、新しいバージョンが要求されない限り、標準の最も古いバージョン(つまり1.0.0)がデフォルトでなければなりません(MUST)。

プッシュベースのネゴシエーションは、通常、エクスポージャーが接続を開始するため、本質的に複雑です。プロデューサーは、インジェスターから別の要求がない限り、標準の最も古いバージョン(つまり1.0.0)を使用しなければなりません(MUST)。

テキスト形式

ABNF

RFC 5234 に基づく ABNF

「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] [SP created] [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

; Lowercase ct @ timestamp
created = %d99.116 "@" timestamp

全体構造

UTF-8を使用しなければなりません(MUST)。バイトオーダーマーカー(BOM)を使用してはなりません(MUST NOT)。実装者への重要な注意点として、バイト0は有効なUTF-8ですが、例えばバイト255は有効ではありません。

コンテンツタイプは、

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

改行はラインフィード(\n)で示されなければならず(MUST)、キャリッジリターン(\r)を含んではなりません(MUST NOT)。エクスポジションはEOFで終了しなければならず(MUST)、`EOF\n`で終了すべきです(SHOULD)。

完全なエクスポジションの例

# 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 [email protected]
acme_http_router_request_seconds_count{path="/api/v1",method="GET"} 807283.0 [email protected]
acme_http_router_request_seconds_sum{path="/api/v2",method="POST"} 479.3 [email protected]
acme_http_router_request_seconds_count{path="/api/v2",method="POST"} 34.0 [email protected]
# 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.123421`と`1.89e-7`があります。浮動小数点数は、IEEE 754で定義された64ビット浮動小数点値の範囲内に収まらなければなりませんが(MUST)、仮数部に多くのビットが必要となり、精度が失われる可能性があります(MAY)。これは、ナノ秒分解能のタイムスタンプをエンコードするために使用してもよいです(MAY)。

「Canonical Numbers」のセクションにあるように、「quantile」と「le」のラベル値に任意の整数および浮動小数点数のレンダリングを使用することはできません(MUST NOT)。他の数値が使用される場所ではどこでも使用してもよいです(MAY)。

考慮事項:正規表現

ヒストグラムの「le」ラベル値とサマリーメトリックの「quantile」ラベル値の数値は、ラベル値であるという点で特殊であり、ラベル値は不透明であることを意図しています。エンドユーザーがこれらの文字列値と直接対話する可能性が高く、多くのモニタリングシステムがこれらをファーストクラスの数値として扱う能力に欠けているため、特定の数値がまったく同じテキスト表現を持つことが有益でしょう。

一貫性は非常に望ましいですが、言語とそのランタイムの実際の動作を強制することは非現実的です。最も重要な一般的なquantileは0.5、0.95、0.9、0.99、0.999であり、バケット値はミリ秒から10.0秒までの値を表します。これは、典型的なWebサービスにおけるレイテンシSLAやApdexなどのケースをカバーするためです。固定小数点と指数表記の切り替えがランタイム間で一貫していることを確認するために、10の累乗が考慮されています。目標とするレンダリングは、Goのfloat64値のデフォルトレンダリング(つまり%g)と同等であり、小数点や指数がない場合は、それらが浮動小数点であることを明確にするために.0が付加されます。

エクスポージャーは、正の無限大に対して+Infとして出力を生成しなければなりません(MUST)。

エクスポージャーは、以下の例に沿って、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

エクスポージャーは、以下の例に沿って、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の一貫したレンダリングではありませんが、拒否されてはなりません。

エクスポージャーは非正規数に対してもこれらのパターンに従うべきであり(SHOULD)、レンダリングアルゴリズムをこれらの値に対して一貫させることで、他のほとんどの値も一貫したレンダリングを持つという意図があります。ごく一部の特定のle/quantile値のみを使用するエクスポージャーは、ハードコーディングすることも可能です。Grisu3のような最小限の浮動小数点レンダリングアルゴリズムが容易に利用できないCのような言語では、エクスポージャーは異なるレンダリングを使用してもよいです(MAY)。

Cやそのprintf実装を共有する他の言語の実装者への警告:`%f`、`%e`、`%g`の標準精度は6桁の有効数字しかありません。完全な精度には17桁の有効数字が必要です。例えば、`printf("%.17g", d)`。

タイムスタンプ

ナノ秒精度が必要な場合、タイムスタンプに指数浮動小数点レンダリングを使用すべきではありません(SHOULD NOT)。float64のレンダリングでは十分な精度がないためです(例: `1604676851.123456789`)。

MetricFamily

MetricFamily間に明示的なセパレータがあってはなりません(MUST NOT)。次のMetricFamilyは、メタデータまたは前のMetricFamilyの一部ではありえない新しいサンプルメトリック名によって示されなければなりません(MUST)。

MetricFamilyはインターリーブされてはなりません(MUST NOT)。

MetricFamilyメタデータ

メタデータには、MetricFamily名、TYPE、UNIT、HELPの4つがあります。fooというカウンタメトリックのメタデータの例は以下の通りです。

# 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ごとに、各タイプのメタデータ行は1つ以上存在してはなりません(MUST NOT)。順序はTYPE、UNIT、HELPであるべきです(SHOULD)。

このメタデータとメッセージ末尾のEOF行を除き、#で始まる行を公開してはなりません(MUST NOT)。

メトリック

メトリックはインターリーブされてはなりません(MUST NOT)。

「テキスト形式 -> MetricPoint」の例を参照してください。ラベル ラベルやタイムスタンプがなく、値が0のサンプルは、以下のようにレンダリングされなければなりません。

bar_seconds_count 0

または

bar_seconds_count{} 0

ラベル値は任意の有効なUTF-8値であってもよいので(MAY)、ABNFに従ってエスケープを適用しなければなりません(MUST)。2つのラベルを持つ有効な例を以下に示します。

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

MetricPointの値をレンダリングする際には、追加のラベル(例えばHistogramタイプの「le」ラベル)を含めることができ、これはMetric自身の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タイプのMetricFamilyのMetricPointの値のサンプルメトリック名にはサフィックスがあってはなりません(MUST NOT)。

ラベルなしのメトリックとタイムスタンプなしのMetricPointを持つMetricFamilyの例

# TYPE foo gauge
foo 17.0

ラベルとタイムスタンプなしのMetricPointを持つ2つのメトリックを持つMetricFamilyの例

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

メトリックなしのMetricFamilyの例

# TYPE foo gauge

ラベルとタイムスタンプ付きのMetricPointを持つメトリックの例

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

ラベルなしのMetricとタイムスタンプ付きのMetricPointの例

# TYPE foo gauge
foo 17.0 1520879607.789

ラベルなしのMetricとタイムスタンプ付きの2つのMetricPointの例

# TYPE foo gauge
foo 17.0 123
foo 18.0 456
カウンタ

MetricPointの合計値サンプルメトリック名には、サフィックス`_total`が付かなければなりません(MUST)。存在する場合、MetricPointの作成タイムスタンプは、`ct@`プレフィックス付きでメトリックポイントにインライン化されなければなりません(MUST)。値のタイムスタンプが存在する場合、作成タイムスタンプは直後に追加されなければなりません(MUST)。Exemplarが存在する場合、作成タイムスタンプはExemplarの前に追加されなければなりません(MUST)。

ラベルなしのMetric、タイムスタンプなしのMetricPoint、Created Timestampなしの例

# TYPE foo counter
foo_total 17.0

ラベルなしのMetric、タイムスタンプありのMetricPoint、Created Timestampなしの例

# TYPE foo counter
foo_total 17.0 1520879607.789

ラベルなしのMetric、タイムスタンプなしのMetricPoint、Created Timestampありの例

# TYPE foo counter
foo_total 17.0 [email protected]

ラベルなしのMetric、タイムスタンプありのMetricPoint、Created Timestampありの例

# TYPE foo counter
foo_total 17.0 1520879607.789 [email protected]

ExemplarはMetricPointのTotalサンプルに付加されてもよいです(MAY)。

ラベルなしのMetric、タイムスタンプありのMetricPoint、Created Timestampあり、Exemplarありの例

# TYPE foo counter
foo_total 17.0 1520879607.789 [email protected] # {trace_id="KOO5S4vxi0o"} 0.67
StateSet

StateSetタイプのMetricFamilyのMetricPointの値のサンプルメトリック名には、サフィックスがあってはなりません(MUST NOT)。

StateSetは、MetricPoint内のStateごとに1つのサンプルを持たなければなりません(MUST)。各Stateのサンプルは、MetricFamily名をラベル名とし、State名をラベル値とするラベルを持たなければなりません(MUST)。Stateサンプルの値は、Stateが真の場合には1でなければならず、Stateが偽の場合には0でなければなりません(MUST)。

状態「a」、「bb」、「ccc」があり、bbのみが有効で、メトリック名がfooである例

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

メトリックの「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タイプのMetricFamilyのMetricPointの値のサンプルメトリック名には、サフィックス`_info`が付かなければなりません(MUST)。サンプル値は常に1でなければなりません(MUST)。

ラベルなしのMetricと、「name」および「version」ラベルを持つ1つのMetricPoint値の例

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

「entity」ラベルを持つMetricと、「name」および「version」ラベルを持つ1つの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)。

要約

存在する場合、MetricPointのSum値サンプルメトリック名には`_sum`サフィックスが付かなければなりません(MUST)。存在する場合、MetricPointのCount値メトリック名には`_count`サフィックスが付かなければなりません(MUST)。存在する場合、MetricPointのQuantile値は、ラベル名「quantile」と測定されたquantileのラベル値を持つラベルを使用して、測定されたquantileを指定しなければなりません(MUST)。

存在する場合、MetricPointのCreated Timestampは、`ct@`プレフィックス付きでメトリックポイントにインライン化されなければなりません(MUST)。値のタイムスタンプが存在する場合、Created Timestampは直後に追加されなければなりません(MUST)。Exemplarが存在する場合、Created TimestampはExemplarの前に追加されなければなりません(MUST)。Created Timestampは、すべてのQuantile値、MetricPointのSum、およびMetricPointのCountに付加されなければなりません(MUST)。

ラベルなしのMetricと、Sum、Count、Created Timestamp値を持つMetricPointの例

# TYPE foo summary
foo_count 17.0 [email protected]
foo_sum 324789.3 [email protected]

ラベルなしのMetricと、2つのQuantile、Created Timestamp値を持つMetricPointの例

# TYPE foo summary
foo{quantile="0.95"} 123.7 [email protected]
foo{quantile="0.99"} 150.0 [email protected]

Quantileは任意の順序であってよいです(MAY)。

ヒストグラム

MetricPointのバケット値サンプルメトリック名には`_bucket`サフィックスが付かなければなりません(MUST)。存在する場合、MetricPointのSum値サンプルメトリック名には`_sum`サフィックスが付かなければなりません(MUST)。

存在する場合、MetricPointのCreated Timestampは、`ct@`プレフィックス付きでメトリックポイントにインライン化されなければなりません(MUST)。値のタイムスタンプが存在する場合、Created Timestampは直後に追加されなければなりません(MUST)。Exemplarが存在する場合、Created TimestampはExemplarの前に追加されなければなりません(MUST)。Created Timestampは、すべてのBucket値、MetricPointのSum、およびMetricPointのCountに付加されなければなりません(MUST)。

MetricPointにSum値が存在する場合に限り(If and only if)、MetricPointの+Infバケット値も、サフィックス「_count」を持つメトリック名のサンプルに表示されなければなりません(MUST)。

バケットは「le」の昇順にソートされなければならず(MUST)、「le」ラベルの値は正規表現の規則に従わなければなりません(MUST)。

ラベルなしのMetricと、Sum、Count、Created Timestamp値、および12個のバケットを持つMetricPointの例。「le」値の広範で atypicalだが有効な種類が意図的に示されています。

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

ラベルなしのExemplarは、空のLabelSetを`{}`として表現しなければなりません(MUST)。

いくつかの有効なケースを示すExemplarの例:「0.01」バケットにはExemplarがありません。0.1バケットにはラベルなしのExemplarがあります。1バケットには1つのラベルを持つExemplarがあります。10バケットにはラベルとタイムスタンプを持つExemplarがあります。実際には、すべてのバケットは同じ形式のExemplarを持つべきです(SHOULD)。

# TYPE foo histogram
foo_bucket{le="0.01"} 0 [email protected]
foo_bucket{le="0.1"} 8 [email protected] # {} 0.054
foo_bucket{le="1"} 11 [email protected] # {trace_id="KOO5S4vxi0o"} 0.67
foo_bucket{le="10"} 17 [email protected] # {trace_id="oHg5SJYRHA0"} 9.8 1520879607.789
foo_bucket{le="+Inf"} 17 [email protected]
foo_count 17 [email protected]
foo_sum 324789.3 [email protected]
ゲージヒストグラム

MetricPointのBucket値サンプルメトリック名には`_bucket`サフィックスが付かなければなりません(MUST)。存在する場合、MetricPointのSum値サンプルメトリック名には`_gsum`サフィックスが付かなければなりません(MUST)。MetricPointにSum値が存在する場合に限り(If and only if)、MetricPointの+Inf Bucket値も、`_gcount`サフィックスを持つMetricNameのサンプルに表示されなければなりません(MUST)。

バケットは「le」の昇順にソートされなければならず(MUST)、「le」ラベルの値は正規表現の規則に従わなければなりません(MUST)。

ラベルなしのMetricと、バケットにExemplarがない、Exemplarなしの1つの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タイプのMetricFamilyのMetricPointの値のサンプルメトリック名にはサフィックスがあってはなりません(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形式は、プロトコルバッファ言語のproto3バージョンに従わなければなりません(MUST)。

文字列

すべての文字列フィールドはUTF-8でエンコードされなければなりません(MUST)。

タイムスタンプ

OpenMetrics protobufスキーマにおけるタイムスタンプ表現は、公開されているgoogle.protobuf.Timestamp [timestamp] メッセージに従わなければなりません(MUST)。タイムスタンプメッセージは、int64としてのUnixエポック秒と、秒タイムスタンプコンポーネントから前方にカウントされるナノ秒分解能の非負の秒の分数(int32として)でなければなりません(MUST)。0から999,999,999の範囲(両端を含む)でなければなりません(MUST)。

Protobufスキーマ

Protobufスキーマは現在、こちらで利用可能です。

PrometheusおよびエコシステムはOpenMetrics protobufスキーマをサポートしていません。代わりに、類似の`io.prometheus.client` 形式を使用しています。OpenMetrics 2.0におけるprotobufスキーマの将来についての議論は進行中です。

設計上の考慮事項

スコープ

OpenMetricsはオンラインシステムのテレメトリを提供することを目的としています。ハードまたはソフトなリアルタイム保証を提供しないプロトコル上で動作するため、それ自体ではリアルタイム保証を行うことはできません。OpenMetricsのレイテンシとジッターの特性は、基盤となるネットワーク、オペレーティングシステム、CPUなどと同じくらい不正確です。意思決定の基礎として集計を使用するのに十分な精度はありますが、個々のイベントを反映するものではありません。

1時間に数リクエストしか受け付けないアプリケーションから、400Gbネットワークポートの帯域幅使用量を監視するアプリケーションまで、あらゆる規模のシステムがサポートされるべきです。送信されたテレメトリの集計と分析は、任意の期間にわたって可能でなければなりません。

定期的にデータ送信時の状態のスナップショットを転送することを意図しています。

スコープ外

インジェスターがどのエクスポージャーが存在するかを発見する方法、およびその逆については、この標準の範囲外であり、したがって定義されていません。

拡張と改善

OpenMetricsのこの最初のバージョンは、確立されたデファクトスタンダードであるPrometheusテキストフォーマット0.0.4に基づいており、主要な構文的または意味的な拡張、あるいは最適化を意図的に追加していません。例えば、Histogramバケットのテキスト表現をよりコンパクトにする試みは行われず、反復的な性質に対処するために基盤となるスタックの圧縮に依存しています。

これは、標準が既存のユーザーベースの採用と勢いを活用できるようにするための意図的な選択です。これにより、Prometheusテキストフォーマット0.0.4からの比較的容易な移行が保証されます。

また、実装が容易な基本的な標準があることも保証されます。これは、標準の将来のバージョンで構築することができます。将来の標準バージョンは、構文的にも意味的にも常にこの1.0バージョンをサポートする必要があることを意図しています。

モニタリングシステムが、過度な負担なしにOpenMetricsエクスポジションから有用な情報を取得できるようにしたいと考えています。すべてのメタデータと構造を剥ぎ取り、OpenMetricsエクスポジションをサンプル の順不同セットとしてのみ見た場合でも、それ自体で有用であるべきです。そのため、スケッチやt-ダイジェストのような不透明なバイナリタイプも存在しません。これらは、カスタムの解析と処理を必要とするため、ゲージとカウンタの組み合わせとして表現することはできません。

この原則は、標準全体にわたって一貫して適用されています。例えば、MetricFamilyの単位は名前に重複して含まれており、単位メタデータを理解しないシステムでも単位を利用できるようにしています。「le」ラベルは、独自の特殊な構文を持つのではなく、通常のラベル値として扱われます。これにより、インジェスターがヒストグラムの特殊な処理コードを追加する必要がありません。さらに、複合データ型は存在しません。例えば、緯度/経度のような地理位置情報型は存在しません。これは、個別のゲージメトリックで実現できるためです。

単位と基本単位

システム間の一貫性を保ち、混乱を避けるため、単位は主にSI基本単位に基づいています。基本単位には、秒、バイト、ジュール、グラム、メートル、比率、ボルト、アンペア、セルシウスが含まれます。単位は適用可能な場合に提供すべきです。

例えば、すべての継続時間メトリックを秒で表現することで、特定のメトリックがナノ秒、マイクロ秒、ミリ秒、秒、分、時間、日、週のどれであるかを推測したり、混在する単位を扱う必要がなくなります。接頭辞のない単位を選択することで、キロミリ秒が複雑なシステムの発生的な振る舞いの結果となるような状況を避けることができます。

値は浮動小数点数にできるため、サブ基本単位の精度が標準に組み込まれています。

同様に、ビットとバイトを混在させると混乱を招くため、バイトが基本として選ばれています。ケルビンは理論的にはより良い基本単位ですが、実際には既存のハードウェアのほとんどが摂氏を公開しています。キログラムはSI基本単位ですが、キロ接頭辞は問題があるため、グラムが基本単位として選ばれています。

基本単位は可能な限りすべての場合で使用すべきですが(SHOULD)、ケルビンは広く確立された単位であり、色や黒体温度など、摂氏とケルビンのメトリックを比較する可能性が低いユースケースでは、摂氏の代わりにケルビンを使用してもよいです(MAY)。

比率は基本単位であり、パーセンテージではありません。可能な限り、与えられた分子と分母のゲージまたはカウンタの形式の生データが公開されるべきです。これにより、インジェスターでの分析と集計のためのより良い数学的特性が得られます。

デシベルは基本単位ではありません。第一に、デシはSI接頭辞であり、第二に、ベルは対数であるからです。信号/エネルギー/電力比を公開するには、比率を直接公開する方が良いでしょう。可能であれば、生の電力/エネルギーを公開する方がさらに良いです。浮動小数点の指数は、極端な科学的用途でも十分にカバーできます。電子ボルト(約1e-19 J)から超新星が放出するエネルギー(約1e44 J)まで、63桁の桁数がありますが、64ビット浮動小数点数は2000桁以上の桁数をカバーできます。

基本単位以外の単位を避けられず、変換が不可能である場合は、明確にするために実際の単位をメトリック名に含めるべきです。例えば、ジュールはエネルギーと電力の両方の基本単位であり、ワットはジュール単位のカウンタとして表現できます。実際には、特定のサードパーティシステムがワットのみを公開する場合があり、その場合はワットで表現されたゲージが唯一現実的な選択肢となります。

すべてのMetricFamilyが単位を持つわけではありません。例えば、HTTPリクエストの数には単位がありません。技術的には単位はHTTPリクエストですが、その意味ではMetricFamily名全体が単位です。そこまで極端に考えると有用ではありません。ダウンストリームシステムで人間が利用するためのグラフの良好な軸の可能性は常に念頭に置かれるべきです。

ステートレス性

OpenMetricsで定義されたワイヤフォーマットは、エクスポジション間でステートレスです。以前にどのような情報が公開されたかは、将来のエクスポジションに影響を与えてはなりません(MUST)。各エクスポジションは、エクスポージャーの現在の状態の自己完結型スナップショットです。

既存および新規のインジェスターには、同じ自己完結型のエクスポジションが提供されなければなりません(MUST)。

中核的な設計上の選択肢として、エクスポージャーは、最近変更や観測がなかったという理由だけで、メトリックを除外してはなりません(MUST NOT)。エクスポージャーは、インジェスターがエクスポジションをどれくらいの頻度で消費しているかについて、いかなる仮定もしてはなりません。

時間とメトリックの進化にわたる公開

メトリックは、時間の経過に伴うその進化が分析できる場合に最も有用であるため、それに応じてエクスポジションは時間の経過とともに意味をなさなければなりません。したがって、単一のエクスポジションだけでは有用で有効であるには不十分です。メトリックセマンティクスに対する一部の変更は、ダウンストリームのユーザーを壊す可能性もあります。

パーサーは通常、以前の結果をキャッシュすることで最適化します。したがって、技術的には破壊的ではないものの、エクスポジション間でラベルが公開される順序を変更することは避けるべきです(SHOULD)。これにより、エクスポジションの単体テストの作成も容易になる傾向があります。

メトリックとサンプルは、エクスポジションごとに現れたり消えたりすべきではありません(SHOULD NOT)。例えば、カウンターは履歴があって初めて有用です。原則として、特定のメトリックは、プロセスが開始されてから終了するまで、エクスポジションに存在すべきです。特定のプロセスのライフタイムにわたってMetricFamilyがどのようなMetricを持つかを事前に知ることはしばしば不可能です(例えば、レイテンシーヒストグラムのラベル値がHTTPパスであり、これは実行時にエンドユーザーによって提供されるものなど)が、カウンターのようなMetricが一度公開されたら、プロセスが終了するまで公開され続けるべきです。カウンターが増分されないからといって、現在の値が無効になるわけではありません。特定のMetricの公開を停止することが理にかなっているケースもあります。欠落データに関するセクションを参照してください。

一般に、MetricFamilyの型を変更したり、そのMetricにラベルを追加または削除したりすることは、インジェスターにとって破壊的となります。

注目すべき例外として、Info MetricPointの値にラベルを追加しても破壊的ではありません。これにより、既存のInfo MetricFamilyに、意味のある追加情報を追加できるようになり、追加のラベル値を持つまったく新しいInfoメトリックを作成する必要がなくなります。インジェスターシステムは、このような追加に対して堅牢であることを確認すべきです。

MetricFamilyのヘルプを変更しても破壊的ではありません。可能な値の場合、浮動小数点数と整数の切り替えは破壊的ではありません。StateSetに新しい状態を追加しても破壊的ではありません。メトリック名を変更しない単位メタデータを追加しても破壊的ではありません。

ヒストグラムのバケットは、エクスポジションごとに変更すべきではありません(SHOULD NOT)。これは、パフォーマンスの問題を引き起こし、インジェスターを破壊する可能性が高いからです。同様に、アプリケーションのあらゆる一貫したバイナリと環境からのすべてのエクスポジションは、特定のヒストグラムMetricFamilyに対して同じバケットを持つべきです。これにより、インジェスターは異なるバケットに対してヒストグラムのマージロジックを実装することなく、すべてのインジェスターによって集計できます。例外として、新しいソフトウェアリリースによってパフォーマンス特性が変化した場合に、破壊的とみなされる occasionalな手動変更が、有効なトレードオフとなる場合があります。

変更が技術的に破壊的でなくても、コストがかかります。例えば、頻繁な変更はインジェスターのパフォーマンス問題を引き起こす可能性があります。エクスポジションごとに異なるヘルプ文字列は、各ヘルプ値が保存される原因となる可能性があります。intとfloat値の間で頻繁に切り替えると、効率的な圧縮が妨げられる可能性があります。

NaN

NaNはOpenMetricsにおいて他の数値と同様の数値であり、通常、最近観測がなかった場合の要約Quantileのように、ゼロ除算の結果として生じます。NaNはOpenMetricsにおいて特別な意味を持たず、特に欠損データやその他の不良データのマーカーとして使用してはなりません(MUST NOT)。

データ不足

データが存在しなくなる有効なケースがあります。例えば、ファイルシステムがアンマウントされ、それによって空きディスク領域を示すゲージメトリックが存在しなくなることがあります。この状況に対して特別なマーカーや信号はありません。その後のエクスポジションには、このメトリックは含まれません。

Expositionパフォーマンス

メトリックは、妥当な時間枠で収集できる場合にのみ有用です。公開に数分かかるメトリックは有用とは見なされません。

目安として、エクスポジションは1秒を超えないようにすべきです(SHOULD)。

OpenMetricsを介してシリアル化されたレガシーシステムからのメトリックは、より時間がかかる場合があります。このため、厳密なパフォーマンスの仮定をすることはできません。

エクスポジションは最新の状態のものであるべきです(SHOULD)。例えば、エクスポジションリクエストを処理するスレッドは、キャッシュされた値に依存すべきではありません(SHOULD NOT)。その範囲内で、そのようなキャッシュをバイパスできる限り。

同時実行性

高可用性とアドホックアクセスには、複数のインジェスターを持つのが一般的なアプローチです。これをサポートするために、同時エクスポジションがサポートされなければなりません(MUST)。同時システムに関するすべてのBCPに従うべきです(SHOULD)。一般的な落とし穴には、デッドロック、競合状態、および同時エクスポジションの進行を妨げる過度に粗い粒度のロックが含まれます。

メトリックの命名と名前空間

メトリック名とラベル名の命名において、理解しやすさ、衝突回避、簡潔さのバランスを目指しています。名前はアンダースコアで区切られているため、メトリック名は「snake_case」になります。

例えば「http_request_seconds」は簡潔ですが、多数のアプリケーション間で衝突する可能性があり、このメトリックが何を測定しているのかも正確には不明確です。例えば、複雑なシステムでは認証ミドルウェアの前か後かといった具合です。

メトリック名は、それらがどのコードから来ているかを示すべきです。例えば、「A Company Manufacturing Everything」という会社は、コード内のすべてのメトリックに「acme_」というプレフィックスを付けるかもしれません。そして、レイテンシを測定するHTTPルーターライブラリがある場合、「acme_http_router_request_seconds」のようなメトリックを持ち、それが全体的なレイテンシであることを示すヘルプ文字列を持つかもしれません。

すべてのアプリケーション間で発生しうるすべての衝突を防止することを目的としているわけではありません。そのためには、メトリック名前空間のグローバルレジストリやDNSに基づく非常に長い名前空間のような強硬な解決策が必要になります。むしろ、軽量な非公式なアプローチを維持することを目的としており、特定のアプリケーションにとって、その構成ライブラリ間で衝突が発生する可能性が非常に低いようにしています。

モニタリングシステム全体としての特定のデプロイメントにおいて、同じメトリック名が異なる意味を持つような衝突が稀であることを目指しています。例えば、`acme_http_router_request_seconds`は「A Company Manufacturing Everything」によって開発された何百もの異なるアプリケーションで使われることになるかもしれませんが、これは通常のことです。もし「Another Corporation Making Entities」もHTTPルーターで`acme_http_router_request_seconds`というメトリック名を使っていたとしても問題ありません。もし両社のアプリケーションが同じモニタリングシステムによって監視されていた場合、衝突は望ましくありませんが、許容されます。なぜなら、どちらのアプリケーションも両方の名前を公開しようとしておらず、どのターゲットも同じメトリック名を2回(誤って)公開しようとしていないからです。もしあるアプリケーションが「My Example Company」と「Mega Exciting Company」両方のHTTPルーターライブラリを含みたい場合、それは問題となり、どちらかのメトリック名を何らかの形で変更する必要があります。

結果として、ライブラリがより公開されているほど、そのようなシナリオが発生するリスクを減らすために、そのメトリック名はより適切に名前空間化されるべきです。acme_は社内での使用には悪くない選択ですが、これらの企業は例えば、社外で共有されるコードにはacmeverything_やacorpme_のような接頭辞を選択するかもしれません。

企業や組織による名前空間化の後、上記のようなhttp_routerライブラリのように、必要に応じてライブラリ/サブシステム/アプリケーションによってフラクタルに名前空間化と命名を続けるべきです。目標は、コードベースの全体構造に精通していれば、与えられたメトリック名から、そのメトリックの計測がどこにあるかをうまく推測できることです。

非常に有名で既存のソフトウェアの場合、ソフトウェア自体の名前で十分に区別できるかもしれません。例えば、bind_はDNSソフトウェアにとっておそらく十分ですが、isc_bind_の方がより一般的な命名でしょう。

`scrape_`で始まるメトリック名は、インジェスターが個々のエクスポジションに関連する情報を付加するために使用するため、アプリケーションによって直接公開すべきではありません。すでに汎用モニタリングシステムによって消費され、処理されたメトリックは、その後のエクスポジションでそのようなメトリック名を含むことがあります。エクスポージャーが個々のエクスポジションに関する情報を提供したい場合、`myexposer_scrape_`のようなメトリックプレフィックスを使用してもよいです。一般的な例は、エクスポージャーの観点からエクスポジションにかかった時間を示すゲージ`myexposer_scrape_duration_seconds`です。

Prometheusエコシステム内では、`process_`というプレフィックスを持つ、すべての実装で一貫したプロセスごとのメトリックセットが登場しました。例えば、オープンファイル制限の場合、MetricFamily `process_open_fds`と`process_max_fds`ゲージは、現在の値と最大値の両方を提供します。(これらの名前はレガシーであり、もし今日定義されたとすれば、`process_fds_open`と`process_fds_limit`と呼ばれる可能性が高いでしょう。)一般的に、このように意味的に同一の名前を得ることは非常に困難であり、それが異なる計測では異なる名前を使用すべき理由です。

メトリック名における冗長性を避けてください。`metric`、`timer`、`stats`、`counter`、`total`、`float64`などの部分文字列は避けてください。OpenMetricsを介して公開される特定のタイプ(およびおそらく単位)を持つメトリックであるという事実により、このような情報はすでに暗黙的に示されているため、明示的に含めるべきではありません。同じ理由で、メトリックのラベル名をメトリック名に含めるべきではありません。さらに、モニタリングシステムによるその後のメトリックの集計により、そのような情報が不正確になる可能性があります。

計測に含まれるメトリック名に、モニタリングシステムの他の層からの実装詳細を含めないでください。例えば、MetricFamily名には、たまたま現在OpenMetricsを介してどこかで公開されているからといって「openmetrics」という文字列を含めるべきではありません。また、現在のモニタリングシステムがPrometheusであるからといって「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のようなメトリック名に関するメタデータはありますが、ラベル名に関するメタデータはありません。これは、わずかな利点のためにフォーマットを肥大化させることになるためです。帯域外のドキュメントは、エクスポージャーがインジェスターにこれを提示する方法の1つです。

メトリック名とラベル

MetricFamily内に複数のMetricsを使用するか、複数のMetricFamiliesを使用するかの両方が理にかなっているように見える状況があります。MetricFamilyを合計したり平均したりすることは、常に有用でなくても意味があるべきです。例えば、電圧とファン速度を混ぜるのは意味がありません。

改めて申し上げますが、OpenMetricsは、インジェスターがデータを処理し、集計を実行できるという前提で構築されています。

合計値を他のメトリックと並べて公開するのは誤りです。これにより、ダウンストリームのインジェスターで集計する際に二重カウントが発生するためです。

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

メトリックのラベルは、一意性を確保するために必要な最小限の数にすべきです。ラベルが1つ増えるごとに、ユーザーがダウンストリームでどのラベルを扱うかを決定する際に考慮する必要があるラベルが1つ増えることになります。多くのMetricFamilyに適用できるラベルは、データベースの{{normalization}}と同様に_infoメトリックに移動させる候補です。メトリックのほぼすべてのユーザーが追加ラベルを必要とすると予想される場合、それをすべてのMetricFamilyに追加する方が良いトレードオフかもしれません。例えば、複数のSQLステートメントに関連するMetricFamilyがあり、一意性が完全なSQLステートメントのハッシュを含むラベルによって提供されている場合、人間が読みやすいようにSQLステートメントの最初の500文字を含む別のラベルを持つことは問題ありません。

経験上、ダウンストリームのインジェスターは、1つのMetricFamily内で`{result="success"}`と`{result="failure"}`のラベルを使用するよりも、個別の合計および失敗のMetricFamilyを扱う方が容易であると感じています。また、全二重システムが一般的であり、ダウンストリームのインジェスターはこれらの値を集約ではなく個別に気にかける可能性が高いため、個別の読み書きと送受信のMetricFamilyを公開する方が通常は優れています。

これらすべては、聞こえるほど簡単ではありません。これは、エクスポジションと公開されるシステムの両方におけるドメイン固有のエキスパートによる経験とエンジニアリングのトレードオフが必要とされる分野であり、適切なバランスを見つける必要があります。メトリックとラベルの文字名

OpenMetricsは、既存の広く採用されているPrometheusテキストエクスポジション形式と、その周りに形成されたエコシステムに基づいています。後方互換性は、主要な設計目標です。Prometheusテキスト形式でサポートされている文字セットを拡張または縮小することは、その目標に反するでしょう。後方互換性を破壊することは、ワイヤ形式だけでなく、より広範な影響を及ぼすでしょう。特に、Prometheusエコシステム内で送信されるデータと連携するために作成または採用されたクエリ言語は、これらの正確な文字セットに依存しています。ラベル値は完全なUTF-8をサポートしているため、形式は多言語メトリックを表現できます。

メタデータの種類

メタデータはさまざまなソースから来る可能性があります。長年にわたり、2つの主要なソースが登場しました。これらは機能的には同じであることが多いですが、概念的な違いについて話すことで理解が深まります。

「ターゲットメタデータ」は、通常、エクスポージャーの外部にあるメタデータです。一般的な例としては、サービスディスカバリ、CMDB、または類似のシステムから来るデータで、データセンターの地域、サービスが特定のデプロイメントの一部であるか、または本番環境かテスト環境かといった情報が含まれます。これは、エクスポージャーまたはインジェスターが、このメタデータをキャプチャするすべてのメトリックにラベルを追加することで実現できます。インジェスターを通じてこれを行うことが、より柔軟でオーバーヘッドが少ないため推奨されます。柔軟性に関しては、ハードウェア保守チームはマシンのサーバーラックの場所に関心があるかもしれませんが、その同じマシンを使用するデータベースチームは、それが本番データベースのレプリカ番号2を含んでいることに関心があるかもしれません。オーバーヘッドに関しては、この情報をハードコーディングまたは設定するには、追加の配布パスが必要です。

「エクスポージャーメタデータ」は、エクスポージャー内部から来るものです。一般的な例としては、ソフトウェアバージョン、コンパイラバージョン、GitコミットSHAなどがあります。

プッシュベースとプルベースの両方のシステムでのターゲットメタデータのサポート

プッシュベースの消費では、エクスポージャーが関連するターゲットメタデータをインジェスターに提供するのが一般的です。プルベースの消費では、プッシュベースのアプローチを採用することもできますが、より一般的には、インジェスターは機械データベースやサービスディスカバリシステムなどからターゲットのメタデータを事前に把握しており、エクスポジションを消費する際にそれをメトリックと関連付けます。

OpenMetricsはステートレスであり、すべてのインジェスターに同じエクスポジションを提供します。これは、プッシュ型のアプローチと矛盾します。さらに、プッシュ型のアプローチは、望ましくないメタデータが公開されるため、プル型インジェスターを壊すことになります。

1つのアプローチは、プッシュ型インジェスターがオペレーターの設定に基づいて帯域外でターゲットメタデータを提供することです。例えば、HTTPヘッダーとして提供するなどが考えられます。これはプッシュ型インジェスターにターゲットメタデータを転送し、この標準によって禁止されていませんが、プル型インジェスターが独自のターゲットメタデータを使用すべきであるにもかかわらず、エクスポージャー自体が認識しているメタデータにアクセスできることが依然として有用であるという欠点があります。

好ましい解決策は、このターゲットメタデータをエクスポジションの一部として提供することですが、エクスポジション全体に影響を与えない方法で提供することです。Info MetricFamiliesは、この目的のために設計されています。エクスポージャーは、「target」というInfo MetricFamilyを含めることができ、その中にラベルなしの単一のメトリックでメタデータを提供します。テキスト形式の例は以下のようになるでしょう。

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

エクスポージャーがこの目的のためにこのメトリックを提供する際には、エクスポジションの最初に配置すべきです(SHOULD)。これは効率のためであり、ターゲットメタデータに依存するインジェスターが、その内容に基づくビジネスロジックを適用する前に、エクスポジションの残りをバッファリングする必要がないようにするためです。

エクスポージャーは、特定のインジェスター向けに明示的に設定されていない限り、エクスポジションからのすべてのメトリックにターゲットメタデータラベルを追加してはなりません(MUST NOT)。エクスポージャーは、MetricFamily名にプレフィックスを付けたり、ターゲットメタデータに基づいてMetricFamily名を変更したりしてはなりません(MUST NOT)。一般的に、同じラベルがエクスポジションのすべてのメトリックに現れるべきではありませんが、ごくまれにこれが緊急的な振る舞いの結果となる場合があります。同様に、非常に小さなエクスポジションでは、エクスポージャーからのすべてのMetricFamily名がたまたま同じプレフィックスを共有することもあります。例えば、「A Company Manufacturing Everything」という会社がGo言語で書いたアプリケーションは、acme_、go_、process_、および使用中のサードパーティライブラリからのメトリックプレフィックスを含むメトリックを含む可能性が高いでしょう。

エクスポージャーは、エクスポージャーメタデータをInfo MetricFamilyとして公開できます。

上記の議論は個々のエクスポージャーの文脈におけるものです。汎用モニタリングシステムからのエクスポジションは、多くの個別のターゲットからのメトリックを含むことがあり、したがって複数のターゲット情報メトリックを公開する可能性があります。メトリックは、取り込みの一環として、すでにターゲットメタデータがラベルとして追加されている場合があります。メトリック名はターゲットメタデータに基づいて変更されてはなりません(MUST NOT)。例えば、すべてのメトリックがステージング環境のターゲットから発生したとしても、すべてのメトリックが`staging_`というプレフィックスで終わることは誤りでしょう。

クライアント計算と派生メトリック

エクスポージャーは、数学的または計算的な処理をすべてインジェスターに任せるべきです。特筆すべき例外は、後方互換性のために残念ながら必要とされるサマリー分位数です。エクスポジションは、任意の期間にわたって有用な生の値を扱うべきです。

例えば、過去5分間のカウンタの平均増加率を示すゲージを公開すべきではありません。インジェスターがエクスポジションを通じて消費したデータポイントの増加を計算するようにすることで、より良い数学的特性が得られ、スクレイプの失敗に対する回復力も高まります。

もう一つの例は、ヒストグラム/サマリーの平均イベントサイズです。アプリケーションの起動後またはメトリックの作成後のカウンタの平均増加率を公開することは、先の例の問題を抱えるだけでなく、集計を妨げます。

標準偏差もこのカテゴリに属します。二乗和をカウンタとして公開するのが正しいアプローチでしょう。しかし、64ビット浮動小数点精度では実用的に機能しないため、ヒストグラム値としてはこの標準には含まれませんでした。二乗するため、精度という点で53ビット仮数の半分しか利用できません。例えば、1秒あたり1万イベントを観測するヒストグラムは、2時間以内に精度を失うでしょう。64ビット整数を使用しても、浮動小数点が失われるため、より良いわけではありません。ナノ秒分解能の整数で通常1秒間のイベントを追跡する場合、19回の観測でオーバーフローするでしょう。この設計上の決定は、128ビット浮動小数点数が一般的になったときに再検討される可能性があります。

もう一つの例は、リクエスト失敗率を公開することを避け、代わりに失敗したリクエストと合計リクエストの個別のカウンタを公開することです。

数値型

1秒あたり100万回増分されるカウンタの場合、float64の仮数が53ビットであるため、精度が失われ始めるまでに1世紀以上かかります。しかし、100Gbpsのネットワークインターフェースのオクテットスループットの精度は、float64では約20時間以内に失われ始める可能性があります。100Gbpsのネットワークインターフェースで数年間にわたって1KBの精度が失われることは実際には問題にならない可能性が高いですが、このような高スループットの整数データにはint64が選択肢となります。

サマリーの分位数はfloat64でなければなりません。これらは推定値であり、根本的に不正確だからです。

タイムスタンプの公開

OpenMetricsのコアな前提の1つは、エクスポージャーが公開しているものの最新のスナップショットを公開することです。

公開データにタイムスタンプを付加する使用例は限られていますが、これらは非常に一般的ではありません。タイムスタンプが以前に付加されていたデータ、特に汎用モニタリングシステムに取り込まれたデータはタイムスタンプを保持する場合があります。ライブデータや生データはタイムスタンプを保持すべきではありません。同じメトリックのMetricPoint値を複数のエクスポジションで同じタイムスタンプで公開することは有効ですが、基盤となるメトリックが現在存在しない場合には無効です。

時間同期は難しい問題であり、データは各システム内で内部的に一貫していなければなりません。そのため、インジェスターは、エクスポージャーデバイスのシステム時間に基づかず、自身の視点から現在のタイムスタンプをデータに付加できるべきです。

タイムスタンプ付きメトリックの場合、エクスポジション間でメトリックが欠落した時間を一般的に検出することはできません。しかし、タイムスタンプなしメトリックの場合、インジェスターはメトリックが存在しなくなったエクスポジションから自身のタイムスタンプを使用できます。

これらすべては、一般的にMetricPointのタイムスタンプは公開すべきではないことを意味します。なぜなら、インジェスターが取り込むサンプルに独自のタイムスタンプを適用するのはインジェスター次第であるべきだからです。

メトリックの最終変更時刻を追跡する

`my_counter`というカウンターが初期化され、その後、時刻123に1だけ増分されたと仮定します。これをテキスト形式で公開する正しい方法は以下の通りです。

# 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

最終変更のタイムスタンプをそれ自身のゲージの値として置くことで、インジェスターは両方のメトリックに独自のタイムスタンプを自由に付加できます。

絶対タイムスタンプ(ここではエポックが絶対と見なされます)を公開する方が、経過時間、秒数などよりも堅牢であることが経験によって示されています。どちらの場合も、それらはゲージとなります。例えば、

# 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のタイムスタンプがインジェスターのシステムクロックや同じエクスポジションからの他のメトリックと比較してわずかに未来に見える可能性があることに留意してください。同様に、MetricPointの「ct@」が同じMetricPointのExemplarまたはサンプルタイムスタンプよりわずかに後に見える可能性もあります。

ナノ秒から秒までの解像度をサポートする一般的なモニタリングシステムがあることに留意してください。そのため、秒解像度に切り詰められた場合に同じタイムスタンプを持つ2つのMetricPointが存在すると、インジェスターで重複が発生する可能性があります。この場合、最も早いタイムスタンプを持つMetricPointが使用されなければなりません(MUST)。

閾値

システムの望ましい境界を公開することは意味があるかもしれませんが、適切な注意が必要です。普遍的に真である値については、そのような閾値のゲージメトリックを発行することは意味があるかもしれません。例えば、データセンターの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

サイズ制限

この標準は、単一のエクスポジションによって公開されるサンプルの数、存在する可能性のあるラベルの数、StateSetが持つ可能性のある状態の数、情報値内のラベルの数、またはメトリック名/ラベル名/ラベル値/ヘルプの文字制限に関して、特定の制限を規定していません。

特定の制限を設けることは、合理的なユースケースを妨げるリスクがあります。例えば、あるエクスポジションが汎用モニタリングシステムを通過した後に適切な数のラベルを持っていても、数個のターゲットラベルが追加されただけでその制限を超えてしまう可能性があります。このような数に関する特定の制限は、汎用モニタリングシステムにおける真のコストを捉えることにもなりません。したがって、これらのガイドラインは、エクスポージャーとインジェスターが何が合理的であるかを理解するのに役立つものです。

一方で、ある側面で大きすぎるエクスポジションは、公開されるメトリクスのメリットに比べて、パフォーマンスに大きな問題を引き起こす可能性があります。そのため、単一のエクスポジションのサイズに関するいくつかのガイドラインは有用でしょう。

インジェスターは、特に攻撃や障害を防ぐために、自ら制限を課すことを選択する場合があります。それでも、インジェスターは合理的なユースケースを考慮し、それらに不均衡な影響を与えないように努める必要があります。いずれかの単一の値/メトリクス/エクスポジションがそのような制限を超えた場合、エクスポジション全体が拒否されなければなりません。

一般的に、汎用モニタリングシステムが時系列データを収集する際のパフォーマンスに影響を与えるものは3つあります。それは、ユニークな時系列の数、それらの時系列における時間経過に伴うサンプルの数、そしてメトリクス名、ラベル名、ラベル値、HELPなどのユニークな文字列の数です。インジェスターは取り込み頻度を制御できるため、その側面はこれ以上考慮する必要はありません。

ユニークな時系列の数は、テキスト形式のコメント以外の行数とほぼ同等です。2020年現在、合計1000万の時系列は大量と見なされており、一般的に単一インスタンスのインジェスターの上限のオーダーです。デューデリジェンスなしに、単一のエクスポジションが1万時系列を超えるべきではありません。一般的な考慮事項の1つは水平スケーリングです。インスタンス数を1〜2桁スケールアップした場合、何が起こるでしょうか?単一のデプロイメントで数千台のラックトップスイッチを持つことは、30年前には想像し難かったでしょう。ターゲットがシングルトン(例:クラスター全体に関するメトリクスを公開する)である場合、数十万の時系列は合理的であるかもしれません。重要なのはユニークなMetricFamiliesの数や個々のラベル/バケット/状態セットのカーディナリティではなく、時系列の総オーダーです。それぞれ1つのMetricを持つ1,000個のゲージは、1つのゲージが1,000個のMetricを持つ場合と同じくらいコストがかかります。

特定のタイプのすべてのターゲットが同じ時系列セットを公開している場合、追加のターゲットの文字列は、ほとんどの合理的にモダンなモニタリングシステムにとって増分コストを発生させません。しかし、各ターゲットがユニークな文字列を持つ場合、そのようなコストが発生します。極端な例として、多くのターゲットで使われる1万文字の単一のメトリック名は、それ自体では実際には問題にならない可能性が非常に高いです。それどころか、それぞれユニークな36文字のUUIDを公開する千のターゲットは、モダンなアプローチを仮定すると、保存される文字列という点で、その1万文字の単一のメトリック名よりも3倍以上高価です。さらに、これらの文字列が時間とともに変化する場合、古い文字列は少なくとも一定期間保存され続ける必要があり、追加のコストが発生します。前段落の1千万時系列を仮定すると、1時間あたり100MBのユニークな文字列は、ユースケースがメトリック時系列ではなくイベントログに近いことを示唆しているかもしれません。

エクゼンプラーの長さには、トレーシングスパンデータやその他のイベントログにこの機能を悪用することを防ぐため、128 UTF-8文字の厳密な制限があります。

セキュリティ

実装者は認証、認可、アカウンティングを提供してもよい(MAY)ものとします。もしそれらを選択するならば、OpenMetricsの外部で処理されるべきです(SHOULD)。

すべてのエクスポージャー実装は、TLS 1.2以降でHTTPトラフィックを保護できるべきです(SHOULD)。エクスポージャー実装が暗号化をサポートしない場合、オペレーターは可能な限りリバースプロキシ、ファイアウォール、および/またはACLを使用すべきです(SHOULD)。

メトリックのエクスポジションは、エンドユーザーに公開されるプロダクションサービスとは独立しているべきです。そのため、TCP/80、TCP/443、TCP/8080、TCP/8443のようなポートに/metricsエンドポイントを設けることは、OpenMetricsを使用する公開サービスでは一般的に推奨されません。

IANA

現在、Prometheusのエクスポジション形式のほとんどの実装は、{{PrometheusPorts}}の非公式レジストリにあるIANA未登録ポートを使用していますが、OpenMetricsは明確に定義されたポートで見つけることができます。

データを公開するクライアントのためにIANAによって割り当てられたポートは、歴史的な整合性のために要求された<9099>です。

共通のIPアドレスとポートで複数のメトリックエンドポイントに到達する必要がある場合、オペレーターはlocalhostアドレスを介してエクスポージャーと通信するリバースプロキシの使用を検討するかもしれません。多重化を容易にするため、エンドポイントはパスに独自の名前を持つべきです(SHOULD)。すなわち、/node_exporter/metrics のようにします。「プッシュベースおよびプルベースシステムの両方でのターゲットメタデータのサポート」で述べた理由、および単一障害点なしに独立した取り込みを可能にするために、エクスポジションは単一のエクスポジションに結合されるべきではありません(SHOULD NOT)。

OpenMetricsは、application/openmetrics-text および application/openmetrics-proto の2つのMIMEタイプを登録したいと考えています。

このページの内容