> Source: https://www.ymotongpoo.com/works/oteps/metrics/otep-0146/


# OTEP-0146: メトリクスAPI/SDKプロトタイピングのシナリオ

トレーシング仕様の安定版リリースを受けて、OpenTelemetryコミュニティはメトリクスAPI/SDKにより多くの労力を割く意向です。
目標は、メトリクスAPI/SDK仕様を2021年5月末までに[`Experimental`](../../specification/versioning-and-stability.md#development)状態にし、2021年内に[`Stable`](../../specification/versioning-and-stability.md#stable)にすることです。

* 2021年5月31日までに、各言語クライアントのオーナーにメトリクスのプレビューリリースへの着手を推奨できるだけの十分な自信を持てるようにするべきです。
  つまり、2021年6月1日以降は仕様に大きな驚きや大きな変更がないようにするべきです。
  その時点で、クライアントのメンテナーに実装を推奨し始めます。
  追加の機能を導入することはあり得ますが、その基準は高いものであるべきです。

* 2021年9月30日までに、メトリクスAPI/SDK仕様を`Feature-freeze`とし、バグ修正や編集上の変更に集中します。

* 2021年11月30日までに、複数の言語SIGがRC（リリース候補）または[安定版](../../specification/versioning-and-stability.md#stable)のクライアントを提供する形で、メトリクスAPI/SDK仕様の安定版リリースを実現したいと考えています。

本ドキュメントでは、メトリクスAPI/SDKのプロトタイピングに使用する2つのシナリオに焦点を当てます。
目標は、主要な要件を明確に捉えた2つのシナリオを用意することで、言語クライアントのSIGと協力してプロトタイプを作成し、学びを集約し、スコープと段階を決定できるようにすることです。
後にこれらのシナリオは、すべての言語クライアントに向けた例およびテストケースとして使用できます。

プロトタイピングの間に使用することで合意した言語は以下のとおりです。

* C#
* Java
* Python

このような膨大な作業を一度に引き受けないようにするため、段階的なアプローチを取り、作業を複数の段階に分割する必要があります。

1. 問題領域全体を理解するために、エンドツーエンドのプロトタイプを作成します。
   この段階では、スコープを明確にし、それを正確に説明できるようにするべきです。
   例を挙げます。

    * なぜ確立されたAPI（Prometheus や Micrometer など）を採用するのではなく、まったく新しいメトリクスAPIを導入したいのか、OpenTelemetryのメトリクスAPIを異なるものにしているものは何か（例えばバゲッジ）。
    * OpenCensus Stats APIのシムを考慮する必要があるか、それともこれはスコープ外か。

2. APIの中核となる部分に焦点を当て、エンドツーエンドのライブラリ計装のシナリオをカバーします。
   この段階では、すべてのAPIをカバーすることは期待していません。
   一部のAPIは非常に似通っている可能性があるためです（例えば、整数を記録する方法がわかっていれば、整数に対して行ったことを複製することで後から追加できるため、float/doubleに取り組む必要はありません）。

3. SDKの中核となる部分に焦点を当てます。
   これにより、エンドツーエンドのアプリケーションを実現する助けになります。

4. ステージ2を複製し、APIの完全なセットをカバーします。

5. ステージ3を複製し、SDKの完全なセットをカバーします。

## シナリオ1：Grocery {#scenario-1-grocery}

**Grocery** シナリオは、開発者が最終的なアプリケーションでメトリクスAPIとSDKをどのように使用できるかをカバーします。
これは自己完結型のアプリケーションであり、以下をカバーします。

* ベンダー非依存な方法でコードを計装する方法
* SDKとエクスポーターを構成する方法

複数の食料品店が存在し得ることを考慮し、収集するメトリクスには店舗名を次元として持たせます。これは店舗が稼働している間はほぼ変化しません。

この店にはジャガイモとトマトが豊富にあり、それぞれ次の価格です。

* ジャガイモ：1個あたり1.00ドル
* トマト：1個あたり3.00ドル

各顧客は一意な名前（例：customerA、customerB）を持ち、顧客は同じ店に複数回来店することがあります。
以下がPythonのスニペットです。

```python
store = GroceryStore("Portland")
store.process_order("customerA", {"potato": 2, "tomato": 3})
store.process_order("customerB", {"tomato": 10})
store.process_order("customerC", {"potato": 2})
store.process_order("customerA", {"tomato": 1})
```

1分ごとに以下のメトリクスが必要です。

**注文情報：**

| Store    | Customer  | Number of Orders | Amount (USD) |
| -------- | --------- | ---------------- | ------------ |
| Portland | customerA | 2                | 14.00        |
| Portland | customerB | 1                | 30.00        |
| Portland | customerC | 1                | 2.00         |

**販売済みアイテム：**

| Store    | Customer  | Item   | Count |
| -------- | --------- | ------ | ----- |
| Portland | customerA | potato | 2     |
| Portland | customerA | tomato | 4     |
| Portland | customerB | tomato | 10    |
| Portland | customerC | potato | 2     |

各顧客は店舗に入店・退店することがあります。

以下がPythonのスニペットです。

```python
store = GroceryStore("Portland")
store.enter_customer("customerA", {"account_type": "restaurant"})
store.enter_customer("customerB", {"account_type": "home cook"})
store.exit_customer("customerB", {"account_type": "home cook"})
store.exit_customer("customerA", {"account_type": "restaurant"})
```

1分ごとに以下のメトリクスが必要です。

**店内の顧客：**

| Store    | Account type | Count |
| -------- | -----------  | ----- |
| Portland | restaurant   | 1     |
| Portland | home cook    | 1     |

## シナリオ2：HTTPサーバー {#scenario-2-http-server}

_HTTPサーバー_ シナリオは、ライブラリ開発者XがメトリクスAPIを使ってライブラリを計装する方法と、アプリケーション開発者YがOpenTelemetry SDKを使うようにそのライブラリを最終的なアプリケーションで構成する方法をカバーします。
XとYは異なる会社で働いており、両者はコミュニケーションを取りません。
このデモには2つの部分があります。ライブラリ（Xが所有するHTTPライブラリとClimateControlライブラリ）とサーバーアプリケーション（Yが所有）です。

* 開発者Xがどのようにベンダー非依存な方法でライブラリコードを計装できるか
  * Xにとってパフォーマンスは重要です
  * Xは、Yがどのメトリクスとどの次元を選ぶのかを知りません
  * Xは、集約の時間ウィンドウも、メトリクスの最終的な送信先も知りません
  * Xは、いくつかのデフォルトの推奨事項（デフォルトの次元、集約の時間ウィンドウ、ヒストグラムのバケットなど）を提供したいと考えています。これによりライブラリの利用者がより良いオンボーディング体験を得られるようにするためです。
* 開発者YがどのようにSDKとエクスポーターを構成できるか
  * YはどのようにしてメトリクスSDKをライブラリに接続すべきか
  * Yはどのようにして時間ウィンドウと送信先を構成すべきか
  * Yはどのようにしてメトリクスと次元を選ぶべきか

### ライブラリ要件 {#library-requirements}

ライブラリ開発者（開発者X）は2つのライブラリを提供します。

* サーバーClimate Controlライブラリ - サーバーの温度と湿度を監視・制御するライブラリ
* HTTPサーバーライブラリ - HTTPサービスを提供するライブラリ

両方のライブラリは、すぐに使えるメトリクスを提供します。メトリクスには2つのカテゴリーがあります。

* プッシュメトリクス - 値が利用可能になった時点で（APIを通じて）報告され、利用者からの要求に基づいて（SDKを通じて）収集されます。
  利用者からの要求がない場合、APIは何もせず、データは破棄されます。
* プルメトリクス - 値は常に利用可能であり、利用者からの要求に基づいてのみ報告・収集されます。
  利用者からの要求がない場合、値はまったく報告されません（例えば、誰かが温度を求めない限り温度を取得するAPI呼び出しは行われません）。

#### サーバーClimate Controlライブラリ {#server-climate-control-library}

注：**Hostname** は [`OpenTelemetry Resource`](../../specification/resource/sdk.md) を活用するべきであるため、APIではなくメトリクスSDKによってカバーされるべきものであり、厳密に言えばSDKの観点からは「次元」とはみなされません。

**サーバー温度：**

| Hostname | Temperature (F) |
| -------- | --------------- |
| MachineA | 65.3            |

注：温度は負の値を取ることがあります。
このため、負の値の記録を受け付け、適切に集約できる計装器を慎重に選ぶ必要があります。

**サーバー湿度：**

| Hostname | Humidity (%) |
| -------- | ------------ |
| MachineA | 21           |

**サーバーCPU使用率：**

| Hostname | CPU usage (seconds) |
| -------- | ------------------- |
| MachineA | 100.1               |

**サーバーメモリ使用量：**

| Hostname | Memory usage (bytes) |
| -------- | -------------------- |
| MachineA | 1000000000           |
| MachineA | 2000000000           |

#### HTTPサーバーライブラリ {#http-server-library}

**受信したHTTPリクエスト：**

注：**Client Type** は [`OpenTelemetry Baggage`](../../specification/baggage/api.md) を通じて渡されるため、厳密に言えばメトリクスAPIの一部ではありませんが、メトリクスSDKの観点からは「次元」とみなされます。

| Hostname | Process ID | Client Type | HTTP Method | HTTP Host | HTTP Flavor | Peer IP   | Peer Port | Host IP   | Host Port |
| -------- | ---------- | ----------- | ----------- | --------- | ----------- | --------- | --------- | --------- | --------- |
| MachineA | 1234       | Android     | GET         | otel.org  | 1.1         | 127.0.0.1 | 51327     | 127.0.0.1 | 80        |
| MachineA | 1234       | Android     | POST        | otel.org  | 1.1         | 127.0.0.1 | 51328     | 127.0.0.1 | 80        |
| MachineA | 1234       | iOS         | PUT         | otel.org  | 1.1         | 127.0.0.1 | 51329     | 127.0.0.1 | 80        |

**HTTPサーバーリクエストの継続時間：**

注：サーバーの継続時間は **完了したHTTPリクエスト** についてのみ利用可能です。

| Hostname | Process ID | Client Type | HTTP Method | HTTP Host | HTTP Status Code | HTTP Flavor | Peer IP   | Peer Port | Host IP   | Host Port | Duration (ms) |
| -------- | ---------- | ----------- | ----------- | --------- | ---------------- | ----------- | --------- | --------- | --------- | --------- | ------------- |
| MachineA | 1234       | Android     | GET         | otel.org  | 200              | 1.1         | 127.0.0.1 | 51327     | 127.0.0.1 | 80        | 8.5           |
| MachineA | 1234       | Android     | POST        | otel.org  | 304              | 1.1         | 127.0.0.1 | 51328     | 127.0.0.1 | 80        | 100.0         |

**HTTPアクティブセッション：**

| HTTP Host | HTTP flavor   | Active sessions |
| --------- | ------------- | --------------- |
| otel.org  | 1.1           | 17              |
| otel.org  | 2.0           | 20              |

### アプリケーション要件 {#application-requirements}

アプリケーションのオーナー（開発者Y）は、以下のメトリクスのみを望んでいます。

* サーバー温度 - 5秒ごとに報告
* サーバー湿度 - 1分ごとに報告
* HTTPサーバーリクエストの継続時間 - 5秒ごとに、以下の次元のサブセットとともに報告
  * Hostname
  * HTTP Method
  * HTTP Host
  * HTTP Status Code
  * Client Type
  * サーバー継続時間の90%、95%、99%、99.9%
* HTTPリクエストのカウンター - 5秒ごとに報告
  * 受信したHTTPリクエストの総数
  * 完了したHTTPリクエストの総数
  * 現在進行中のHTTPリクエストの数（同時HTTPリクエスト数）

  | Hostname | Process ID | HTTP Host | Received Requests | Finished Requests | Concurrent Requests |
  | -------- | ---------- | --------- | ----------------- | ----------------- | ------------------- |
  | MachineA | 1234       | otel.org  | 630               | 601               | 29                  |
  | MachineA | 5678       | otel.org  | 1005              | 1001              | 4                   |

* 例外のサンプル（エグゼンプラー） - HTTP 5xxが発生した場合に、開発者Yはトレース ID、スパン ID、およびすべての次元（IP、ポートなど）を含むサンプルリクエストを見たいと考えています。

  | Trace ID                         | Span ID          | Hostname | Process ID | Client Type | HTTP Method | HTTP Host | HTTP Status Code | HTTP Flavor | Peer IP   | Peer Port | Host IP   | Host Port | Exception            |
  | --------------------------------- | ---------------- | -------- | ---------- | ----------- | ----------- | --------- | ---------------- | ----------- | --------- | --------- | --------- | --------- | -------------------- |
  | 8389584945550f40820b96ce1ceb9299 | 745239d26e408342 | MachineA | 1234       | iOS         | PUT         | otel.org  | 500              | 1.1         | 127.0.0.1 | 51329     | 127.0.0.1 | 80        | SocketException(...) |

