OTEP-0088: メトリクスインストゥルメント
メトリクスインストゥルメントのオプションのセマンティック宣言である Monotonic と Absolute を削除し、MeasureインストゥルメントとObserverインストゥルメントを 基礎的 なものとして宣言し、新しいインストゥルメントの 精緻化 を標準化するプロセスを導入します。
OTEP 93 には、インストゥルメントの集合に関する最終的な提案が含まれており、そこには7つのインストゥルメントがあることに注意してください。 OTEP 96 には、7つの標準インストゥルメントの名前に関する最終的な提案が含まれていることに注意してください。 これら3つのOTEPは、OTEP 96で確定した名前を使って、まとめて仕様に適用される予定です。
動機
仕様からGaugeインストゥルメントが削除され、Observerインストゥルメントが追加されたことで、既存の Monotonic と Absolute オプションが混乱を生むようになりました。
たとえば、Counterインストゥルメントは合計値の変化を捕捉するために使用され、非負の値を持つメトリクスイベントは、その合計が単調増加するという意味で単調増加なカウンターを定義していると言えます。
この場合の混乱は、Absolute が捕捉された値を指すのに対して、Monotonic はセマンティックな出力を指すことから生じています。
別の視点から見ると、Counterインストゥルメントは、Measureインストゥルメントの精緻化として扱うことができるかもしれません。
Measureインストゥルメントは汎用的な同期測定を捕捉するために使われるのに対し、Counterインストゥルメントは合計値の変化の測定を同期的に捕捉するために特化して使われます。
そのため、Record() の代わりに Add() を使用し、標準的な集約として Sum を指定します。
これが示しているのは、私たちがこの領域を貧弱にモデル化してきたということです。 本提案は、既存のいかなるメトリクスAPIも変更することを提案するものではなく、仕様に現在含まれる3つのインストゥルメント、すなわちMeasure、Observer、Counterについての私たちの理解のみを変更するものです。
解説
MeasureインストゥルメントとObserverインストゥルメントは、ここでは 基礎的 なものとして定義されます。 これは、あらゆる種類のメトリクスインストゥルメントが、これらのいずれかに還元されなければならないという意味です。 基礎的なインストゥルメントは制約を持たず、メトリクスイベントが正であれ負であれ、ゼロであれ無限大であれ、任意の数値をサポートするという意味です。
2つの基礎的なインストゥルメントの違いは、それらが同期的かどうかです。 Measureインストゥルメントはユーザーによって同期的に呼び出されるのに対し、Observerインストゥルメントは実装によって非同期的に呼び出されます。 同期的なインストゥルメント(Measureとその精緻化)は、測定値を捕捉するために3つの呼び出しパターン(Bound、Unbound、Batch)を持ちます。 非同期的なインストゥルメント(Observerとその精緻化)は、測定値を捕捉するためにコールバックを使用します。
すべての測定APIは、タイムスタンプ、インストゥルメント記述子、ラベルセット、数値からなるメトリクスイベントを生成します。 同期的なインストゥルメントのイベントは、さらに、関連するトレースと分散相関値のプロパティを記述する Context を持ちます。
用語: 集約の種類
集約 とは、多数の測定値やオブザベーションを、データの何らかの種類の要約にまとめるために使用される手法を指します。 メトリクスSDK仕様(TODO: 作業中) で詳しく説明されているように、一般的に関連する集約のモードは2つあります。
- 1つの収集間隔の中で、1つのラベルセットについて、SDKの
Aggregator.Add()インターフェースメソッドが1つの新しい測定値を現在の集約値に組み込みます。 これはランタイムで発生するため、時間的集約 と呼ばれます。 このモードはMeasureインストゥルメントにのみ適用されます。 - 1つの収集間隔の中で、複数のラベルセットを結合する際、SDKの
Aggregator.Merge()インターフェースメソッドが2つの集約値を1つの集約値に組み込みます。 これは 空間的集約 と呼ばれます。 このモードはMeasureインストゥルメントとObserverインストゥルメントの両方に適用されます。
以下で議論するように、私たちはとくにレート情報の集約に関心を持っており、これはときに時間的集約と空間的集約を別々に扱う必要があります。
Last-valueの関係
Observerインストゥルメントには、集約を定義する際に役立つ、インストゥルメントによって測定される明確に定義された Last Value(直近値) があります。 Observerインストゥルメントの直近値とは、直近に完了した収集間隔の間に捕捉された値のことであり、収集間隔のタイミングとは無関係に定義できるため、有用な関係です。 Observerの直近値は、直近に完了した単一の収集間隔によって決定されます。それより前の収集間隔を考慮する必要はありません。 Observerの直近値は、収集間隔中にオブザベーションが行われなかった場合には未定義になります。
このプロパティを維持するために、1つの制約を課します。
単一のObserverコールバック呼び出しの中で、同一のLabelSetを持つ2回以上の Observe() 呼び出しは互いの重複として扱われ、最後に呼び出された Observe() が勝ちます。
直近値の関係にもとづいて、「ある時点でのメトリクスの直近値の平均は何か」といった質問をし、それに答えることができます。 Observerインストゥルメントは、収集間隔を参照することなく、また曖昧さもなく、直近値の関係を定義します。
Last-valueとMeasureインストゥルメント
Measureインストゥルメントは直近値の関係を定義しません。 その理由の1つは、同期的なイベントが同時に発生し得るためです。
Measureインストゥルメントについては、収集間隔内で最後に捕捉された値を計算する集約を計算することは可能ですが、それは一意にならない可能性があり、結果は収集間隔のタイミングによって変わってしまいます。 たとえば、1分前に最後に発生した同期的なメトリクスイベントは、収集間隔が1分以上であれば直近値として現れますが、収集間隔が1分より短い場合、直近値は未定義になります。
合計への変化の集約: レート計算
旧来の Monotonic オプションは、レート計算が暗示されるような、現在の合計の報告をサポートするために導入されました。
ここで私たちは、Rate(レート) を、インストゥルメントの部分集合に対して定義される集約として定義します。これは、インストゥルメントがどのように定義されているかによって異なる方法で計算される場合があります。
レート集約は、量の変化量を時間の変化量で割った値を出力します。
レートは、ここで delta(差分) 報告と呼ぶ差分として報告される値、または cumulative(累積) 報告と呼ぶ合計として報告される値から計算できます。 本提案で導入されるインストゥルメントの精緻化の主な目的は、複数の方法でレート計算を容易にすることです。
delta報告の場合、レートは個々の測定値やオブザベーションを合計することによって計算されます。 cumulative報告の場合、レートは個々の値の間の差分を計算することによって計算されます。
cumulativeで報告されるメトリクスデータは、レートを計算する際に時間の次元を特別に扱う必要があることに注意してください。 時間の次元にわたって集約する場合には、差分を計算する必要があります。 空間の次元にわたって集約する場合には、合計を計算する必要があります。
MeasureとObserverの標準的な実装
OpenTelemetryは、インストゥルメントからデータをエクスポートするよう求められたとき、デフォルトのSDKがメトリクスイベントをデフォルトでどのように扱うべきかを規定します。
MeasureインストゥルメントとObserverインストゥルメントは、標準的な実装において、デフォルトで Sum と Count の集約を計算します。
このペアの測定値は、もちろん、平均値を定義します。
2つの基礎的なインストゥルメントには、イベント内の数値に対する制約は課されていません。
MeasureとObserverの精緻化
Monotonic オプションと Absolute オプションは、0.3の仕様で削除されました。
ここでは、インストゥルメントの精緻化を通じて、同等の効果を取り戻すことを提案します。
インストゥルメントの精緻化は基礎的なインストゥルメントに追加され、それが精緻化する基礎的なインストゥルメントと同じ呼び出しパターンを持つ新しいインストゥルメントを生み出します。
これらの精緻化は、異なる標準的な実装を追加すること、またはインストゥルメントへの入力ドメインを制限することのいずれかをサポートします。
言い換えれば、私たちはインストゥルメントのオプションを廃止し、オプションのメトリクスインストゥルメントを採用することにしました。 ここでは、4つの重要なインストゥルメントの精緻化について議論します。
非負
実際の量を測定するインストゥルメントなど、一部のインストゥルメントでは負の値に意味がありません。 たとえば、人が負の量の体重を持つことは不可能です。
非負のインストゥルメントの精緻化は、非負の値のみを受け入れます。 このプロパティを持つインストゥルメントでは、負の値は測定エラーとみなされます。 MeasureインストゥルメントとObserverインストゥルメントの両方が、非負の精緻化をサポートします。
合計のみ
合計のみのインストゥルメントとは、合計のみが関心の対象とみなされるインストゥルメントです。
合計のみのインストゥルメントの精緻化については、数値 M と N を持つ2つのイベントは、値 M+N を持つ単一のイベントと意味的に等価であるというセマンティックなプロパティがあります。
たとえば、イベントにバスで到着した参加者の合計のみを数える場合、到着したバスの台数には関心がありません。
合計のみのインストゥルメントとは、イベントの数ではなく、Sum のみが数えられるインストゥルメントです。
合計のみのインストゥルメントの重要なプロパティは、delta値とcumulative値のどちらを報告する場合であっても、常にレート集約をサポートするということです。
MeasureインストゥルメントとObserverインストゥルメントの両方が、合計のみの精緻化をサポートします。
事前計算済みの合計
事前計算済みの合計の精緻化は、インストゥルメントを通じて報告される値が、時間とともに変化する合計として観測または測定されることを示します。 事前計算済みの合計のインストゥルメントはcumulative報告をサポートします。これは、タイムスタンプまたは収集間隔にわたる差分を計算することでレート集約が定義されることを意味します。
事前計算済みの合計の精緻化は、合計のみの精緻化を暗示します。 事前計算済みの合計に関連する値は、依然として合計であることに注意してください。 事前計算済みの合計の値は、空間の次元にわたって集約する際には加算を使って結合されます。特別な扱いを受けるのは時間の次元のみです。
非負のレート
非負のレートのインストゥルメントの精緻化は、レート集約が非負の結果のみを生成することを示します。 delta報告とcumulative報告のそれぞれについて、以下のような関心のある非負のレートのケースがあります。
delta報告の場合、非負かつ合計のみのインストゥルメントは、非負のレートのインストゥルメントでもあります。
cumulative報告の場合、合計のみかつ事前計算済みの合計であるインストゥルメントは、必ずしも非負のレートを持つとは限りませんが、明示的な非負のレートの精緻化を追加することで、0.2の仕様における Monotonic と同等になります。
たとえば、連続する収集間隔で読み取られるプロセスのCPU時間は、負の量だけ変化することはあり得ません。なぜなら、負の量のCPU時間を使用することは不可能だからです。 CPU時間はObserverインストゥルメントを通じて報告される典型的な値なので、特定のラベルの集合に対するレートは、直近のオブザベーションから前回のオブザベーションを引くことで定義されます。 非負のレートの精緻化を使用することは、後続の収集間隔で値が非負の量だけ増加することを主張します。
議論: 加法的な数と非加法的な数
上記で提案した精緻化によって、精緻化されていないMeasureと UpDownCumulativeCounter の違いが気になるかもしれません。 どちらの値も範囲に関して制約がないため、なぜ異なる扱いをする必要があるのでしょうか。
UpDownCumulativeCounter は、合計のみと事前計算済みの合計の精緻化を持ち、これは観測される数値が加算の結果であることを示しています。
これらのインストゥルメントは、N と M を別々に観測することが N+M を観測することと等価であるという加法的なプロパティを持ちます。
このような加法的なプロパティを持つデータに対して空間的集約を実行する場合、合計を計算するのが自然です。
加法的なプロパティを持たないデータに対して空間的集約を実行する場合、分布を結合するのが自然です。 その違いは、集約する際に値をどのように解釈するかにあります。 デフォルトの設定で合計を報告するには合計のみの精緻化のいずれかを使用し、そうでなければ分布を報告するために合計のみでないインストゥルメントのいずれかを使用してください。
言語レベルの精緻化
OpenTelemetryの実装は、組み込みの型に対応するためにインストゥルメントの精緻化を追加したいと考えるかもしれません。
整数と浮動小数点数を区別する言語では、それぞれに対してインストゥルメントの精緻化を提供するべきであり、その結果、Int64Measure や Float64Measure のような型名になります。
符号なし整数型をサポートする言語では、これらの値を報告するために専用のインストゥルメントを作成したいと考えるかもしれず、その結果、UnsignedInt64Observer や UnsignedFloat64Observer のような型名になります。
これらは自然に非負の精緻化を適用することになります。
組み込み型の精緻化のその他の用途には、期間の測定のための型があります。 たとえば、2つのクロック測定値の差分のための組み込み型がある場合、OpenTelemetryのAPIは、測定に正しい時間の単位を自動的に適用する精緻化を提供するべきです。
Counterの精緻化
CounterはMeasureインストゥルメントの、合計のみで非負な、したがって非負のレートを持つ精緻化です。
新しいインストゥルメントの標準化
これらの精緻化があれば、それぞれ異なる種類のインストゥルメントを網羅的に列挙できます。 以下の表には、合計で12個の仮説上のインストゥルメントが列挙されており、そのうち標準化されているのは1つだけです。 仮説上の将来のインストゥルメント名は 斜体 で示されています。
| 基礎的なインストゥルメント | 合計のみ? | 事前計算済みの合計? | 非負? | 非負のレート? | インストゥルメント名 (仮説) |
|---|---|---|---|---|---|
| Measure | 合計のみ | 非負 | 非負のレート | Counter | |
| Measure | 合計のみ | 事前計算済みの合計 | 非負のレート | CumulativeCounter | |
| Measure | 合計のみ | UpDownCounter | |||
| Measure | 合計のみ | 事前計算済みの合計 | UpDownCumulativeCounter | ||
| Measure | 非負 | AbsoluteDistribution | |||
| Measure | Distribution | ||||
| Observer | 合計のみ | 非負 | 非負のレート | DeltaObserver | |
| Observer | 合計のみ | 事前計算済みの合計 | 非負のレート | CumulativeObserver | |
| Observer | 合計のみ | UpDownDeltaObserver | |||
| Observer | 合計のみ | 事前計算済みの合計 | UpDownCumulativeObserver | ||
| Observer | 非負 | AbsoluteLastValueObserver | |||
| Observer | LastValueObserver |
この一覧に至るまでに、いくつかの前提が置かれています。 たとえば、事前計算済みの合計と非負のレートの精緻化は、合計のみの精緻化と組み合わせた場合にのみ適用可能です。
事前計算済みの合計のインストゥルメントについては、レート集約が差分を計算するため、技術的には入力が非負かどうかは気にしません。 しかし、他の集約にとっては、事前計算済みの合計がゼロから始まると仮定することが有用であり、私たちは事前計算済みの合計がゼロ以外の初期値を持つケースは無視します。
Gaugeインストゥルメント
デフォルトのLast Value集約を持つMeasureインストゥルメントを定義することができ、これは仮に Gauge インストゥルメントと呼ばれます。 これは、この挙動を望むユーザーに利便性を提供するものです。というのも、それ以外にはLast Value集約を持つ標準的なMeasureの精緻化が存在しないためです。
この仮説上のインストゥルメントの合計のみの用途には、代わりに CumulativeCounter または UpDownCumulativeCounter を使用するべきです。なぜなら、それらは合計を報告するものだからです。 この(仮説上の)Gauge インストゥルメントは、値が時間依存であり、平均値に関心がない場合に有用です。
内部の詳細
これは理解の変更です。 新しいインストゥルメントの作成やAPIの変更を求めるものではありませんが、新しいインストゥルメントを追加する際にどのように考えるべきかを規定するものです。
本提案では、APIの変更は求められていません。
既知のシステムへの変換
Prometheus
Prometheusシステムは、4種類の同期的なメトリクスインストゥルメントを定義しています。
| システム | メトリクスの種類 | 操作 | 集約 | 備考 |
|---|---|---|---|---|
| Prometheus | Counter | Inc() | Sum | 正のデルタの合計 |
| Prometheus | Counter | Add() | Sum | 正のデルタの合計 |
| Prometheus | Gauge | Set() | Last Value | 非加法的または単調増加な累積値 |
| Prometheus | Gauge | Inc()/Dec() | Sum | デルタの合計 |
| Prometheus | Gauge | Add()/Sub() | Sum | デルタの合計 |
| Prometheus | Histogram | Observe() | Histogram | 非負の値 |
| Prometheus | Summary | Observe() | Summary | 集約はマージされない |
Prometheusの Gauge は5つのメソッド(Set、Inc、Dec、Add、Sub)をサポートしており、そのうち1つが直近値を設定し、他のものは直近値を変更することに注意してください。
このインターフェースはOpenTelemetryと互換性がありません。なぜなら、加法的なメソッド(Inc、Dec、Add、Sub)の1つに続いて直近値を計算するために、SDKがGaugeの値について長期間の状態を保持する必要があるからです。
Prometheusの Gauge を Set メソッドのみをサポートするように制限するか、あるいは加法的なメソッドのみをサポートするように制限すれば、OpenTelemetryと互換性のある方法で、これら2つのインストゥルメントを別々にモデル化できます。
もっぱら Set() とともに使用されるPrometheusの Gauge は、Last Value集約を持つMeasureインストゥルメントとしてモデル化できます。
もっぱら加法的なメソッドとともに使用されるPrometheusの Gauge は、UpDownCounter としてモデル化できます。
Prometheusは「Collector」インターフェースを介した非同期の報告をサポートしていますが、これはエンコードされたメトリクスデータを直接エクスポートすることをサポートする低レベルのAPIです。 PrometheusのCollectorインターフェースはObserverライクなインストゥルメントを実装するために使用できますが、Prometheusではネイティブにサポートされていません。
Statsd
Statsdシステムは同期的な報告のみをサポートします。
| システム | メトリクスイベント | 操作 | 集約 | 備考 |
|---|---|---|---|---|
| Statsd | Count | Count() | Sum | デルタの合計 |
| Statsd | Gauge | Gauge() | Last Value | |
| Statsd | Histogram | Histogram() | Histogram | |
| Statsd | Distribution | Distribution() | 指定なし | 分布の要約 |
| Statsd | Timing | Timing() | 指定なし | 非負、分布の要約、ミリ秒単位 |
| Statsd | Set | Set() | Cardinality | ユニークな値の個数 |
Statsdの Count 操作は、増分が非負であればCounterに変換され、値が負になり得る場合は UpDownCounter に変換されます。 Statsdの Gauge 操作は、Last Value集約で設定されたMeasureインストゥルメントに変換されます。
Histogram、Distribution、Timingの各操作はセマンティックには同一ですが、statsdのシステムでは単位とデフォルトの挙動が異なります。 これらの分布値を持つインストゥルメントはそれぞれ、MinMaxSumCount、Histogram、Exact、Summaryなどの分布値を持つ集約を使ったMeasureに置き換えることができます。
Set 操作は、OpenTelemetryには直接の代替物がありませんが、Measureインストゥルメント、またはObserverインストゥルメントとダミーの値を使って構築できます。 それぞれ異なるラベルセットは、同期的に報告される場合でも非同期的に報告される場合でも、各収集間隔ごとに自然に出力されるため、ユニークな要素としてメトリクスのラベルを使い、集約演算子を使わないことで、集合のサイズを計算できます。
OpenCensus
OpenCensusシステムは、3種類のインストゥルメントを定義しています。
| システム | メトリクスの種類 | 操作 | 集約 | 備考 |
|---|---|---|---|---|
| OpenCensus | Cumulative | Inc() | Sum | 正のデルタ |
| OpenCensus | Gauge | Set() | LastValue | |
| OpenCensus | Gauge | Add() | Sum | デルタ |
| OpenCensus | Raw-Stats | Record() | Sum、Count、Mean、Distribution |
OpenCensusは、Views APIの導入によって従来の慣例から離れました。これによって、複数の方法で設定できるため、より少ない種類のインストゥルメントを直接サポートすることが可能になりました。
Prometheusと同様に、Gaugeインストゥルメント内で複数のAPIを組み合わせることはOpenTelemetryと互換性がありません。
Set() とともに使用されるGaugeは一般に直近値の集約を暗示するのに対して、Add() とともに使用されるGaugeは加法的でSum集約を使用します。
生の統計情報はどのような集約でも集約でき、OpenCensusの集約はすべてOpenTelemetryに相当するものがあります。
OpenCensusは、CumulativeインストゥルメントとGaugeインストゥルメントの両方について、コールバックを基にした非同期の形式をサポートしていました。 非同期のCumulativeインストゥルメントは、OpenTelemetryにおいてはCumulativeObserverに置き換えられます。 非同期のLast-valueのGaugeは、AbsoluteObserverまたは単に制約のないObserverに置き換えられます。 非同期の加法的なGaugeは、DeltaObserverに置き換えられます。
サンプルの提案
上記の情報は、後続のOTEPで同期的なインストゥルメントと非同期的なインストゥルメントの両方について精緻化の集合を提案するために使用されます。 以下は、ここでの議論のきっかけとするための、これから提案される内容のサンプルです。
同期的なインストゥルメント
精緻化や制約のない基礎的な Measure インストゥルメントは、Distribution インストゥルメントと呼ばれます。
Counter と Distribution に加えて、私たちはそれほど一般的ではないもののそれでも重要ないくつかのケースを認識しており、それらを標準化すべき理由があります。
- UpDownCounter: Prometheusの加法的なGaugeインストゥルメントの用途をサポートします。
- Timing: PrometheusとStatsdのタイミング測定をサポートします。
標準化はされていないものの、将来的に標準化される可能性があるインストゥルメント(とその理由)は次のとおりです。
- CumulativeCounter: 同期的な単調増加の累積インストゥルメントをサポートします。
- AbsoluteDistribution: 非負の値を持つ分布をサポートします。
広く有用であるとはあまり考えられていないインストゥルメントは次のとおりです。
- UpDownCumulativeCounter: これは非同期で扱う方が適していると私たちは考えています。
Observerインストゥルメント
精緻化や制約のない基礎的な Observer インストゥルメントは、LastValueObserver インストゥルメントと呼ばれます。
私たちは、標準化すべき重要なケースを特定しました。
- CumulativeObserver: 累積の単調増加カウンターをサポートします。
- DeltaObserver: 非同期のデルタカウンターをサポートします。
将来標準化される可能性があるObserverの精緻化は次のとおりです。
- UpDownCumulativeObserver: 非単調な累積カウンターをオブザーブします。
- UpDownDeltaObserver: 正と負のデルタをオブザーブします。
- AbsoluteLastValueObserver: 非負の現在値をオブザーブします。
例: Observerの集約
CPUコアIDごとに分解した、あるプロセスのCPU使用率を捕捉したいとします。
オペレーティングシステムは /proc ファイルシステムから現在の使用率を読み取るメカニズムを提供しており、これはObserverインストゥルメントを使用して収集間隔ごとに1回報告されます。
これは非負のレートを持つ事前計算済みの合計であるため、CPUコアIDを示すメトリクスラベルとともに、この量を報告するために CumulativeObserver を使用します。
このデータに対してCPU使用率のレートを計算することは一般的です。 個々のCPUコアについてのレートは、2つのメトリクスイベントの値の間の差分を計算することによって計算できます。 すべてのコアにわたる集約レート(空間的集約)を計算するには、これらの差分が加算されます。
オープンな質問
旧来のMonotonicの精緻化をめぐって、まだ疑問は残っているでしょうか。
CumulativeObserver インストゥルメントは MonotonicObserver と名付けるべきでしょうか。 本提案では、Cumulative と UpDownCumulative を選びます。 Cumulative はこの文脈において適切な記述的用語です(すなわち、一部の加法的な値は cumulative(累積) であり、一部は delta(差分) です)。 UpDownCumulative ではなく Cumulative であることは、本提案において単調増加性を暗示します。
同期的なインストゥルメントについては、本提案は CumulativeCounter を標準化していません。 そのようなインストゥルメントは MonotonicCounter と名付けられるかもしれません。
トレードオフと緩和策
ここで明示的に導入されるトレードオフは、複数のセマンティックなオプションをサポートする汎用的なインストゥルメントを作成するのではなく、それぞれ専用の目的を持つ新しいインストゥルメントの精緻化を作成することを優先すべきだということです。
先行技術と代替技術
オプションの挙動である Monotonic と Absolute は、2019年8月のMetricsワーキンググループの会議で最初に議論されました。
将来の可能性
将来のOTEPでは、0.4のAPI仕様のために2つの標準的な精緻化の導入が要求される予定です。
これは、上記で説明した CumulativeObserver インストゥルメントと、正しい単位と言語固有の期間の型を持つ AbsoluteMeasure と同等の、TimingMeasure と名付けられた同期的なタイミングインストゥルメントになります。
上記のオープンな質問が、基礎的なインストゥルメントを抽象的なものとして扱う方向で決定された場合、NonAbsoluteMeasure や NonAbsoluteCounter のようなインストゥルメント名を標準化する必要があります。