🚨あなたが知らなかったOpenTelemetryメトリクスの4段階の秘密

OpenTelemetry (OTEL) メトリクスについて、Counter.add(1) を呼び出すだけで魔法のようにデータが蓄積されると思っていましたか? 🤔

実は、ダッシュボードで見る一つの数字の裏には、目に見えない4つの重要な段階が隠されています。

この全体的な流れを理解していないと、「なぜこのメトリクスはこのように集計されたのか?」や「なぜデータが分離して表示されるのか?」といった問題に直面したときに、解決の糸口を見つけるのが難しくなります。

今日は、開発者がコードを書き始める最初の瞬間からデータが最終ストレージに記録されるまで、OpenTelemetryメトリクスデータが辿る4つの段階を徹底的に掘り下げていきます。この記事を最後まで読めば、OTELメトリクスの専門家になれるでしょう! ✨


ステップ1:どのツールで測定するか? – Instrument Model 🛠️

すべての測定は、「どのツールを使用するか」を決定することから始まります。Instrument Modelとは、まさにデータを生成するツール(API)そのものを意味します。

単に増加するだけの値なのか、それとも増減する値なのかなど、データの性質に応じて適切なツールを選択する必要があります。

  • 説明: Counter、UpDownCounter、Histogramなど、6つの主要な計測器のAPI仕様であり、データ収集の最初の関門です。開発者はこのモデルを通じて、「どのような種類の」メトリクスを作成するかを定義します。
  • 重要な質問:
  • 「このデータは増加するだけか?」 ➡️ Counterを選択!
  • 「現在の状態値(例:CPU使用率)を表すか?」 ➡️ Observable Gaugeを選択!

この段階は、まだ実際のデータが発生する前の、設計図を描く段階と同じです。実際の測定値である「イベントモデル」や、集計されたデータパイプラインである「メトリクスストリーム」とは明確に区別されます。単にどのツールを使うかを「宣言」する段階に過ぎません。

// 例: Go言語で"http.requests"という名前のCounter「ツール」を作成
meter := provider.GetMeter("my-app")
httpRequestsCounter, _ := meter.Int64Counter("http.requests")

ステップ2:データの誕生、最初の測定値 – Event Model 👶

ツールを選択したら、いよいよ実際に測定する番です。Event Modelとは、集計(Aggregation)される前の生測定(Raw Measurement)イベント一つ一つを意味します。

  • 説明: OTELメトリクスモデルに入力される最も純粋で基本的なデータです。私たちがコードでAPIを呼び出すまさにその瞬間に発生します。
  • 例:
  • httpRequestsCounter.Add(ctx, 1) を呼び出すその行為
  • histogram.Record(ctx, 52.4) で応答時間を記録する単一の測定値

これはまだ何の加工も施されていない「生」のデータです。特定の時間、特定の属性で値が「一度」測定されたという事実そのものを表します。これらのイベントが集まって、次の段階である「メトリクスストリーム」を形成します。


ステップ3:データの高速道路を構築する – Metric Stream 🛣️

多数の生測定値(Event)が発生すると、OTEL SDKはそれらを無作為に混ぜることはありません。固有の特性を持つデータ同士をまとめて、個別の「論理的パイプライン」を作成します。これがMetric Streamです。OTEL内部でデータを識別し処理する最も重要な核心単位です。

  • 説明: リソース(Resource)+計測器(Instrument)+属性(Attributes)の固有の組み合わせで識別される論理的なデータフローです。SDKはこのストリーム単位でデータを集計し処理します。
  • 核心: 属性(Attributes)が異なると、同じ計測器であっても完全に異なるストリームとして扱われます。

なぜこの概念が重要なのでしょうか?以下の例を見てみましょう。

// 同じhttp.requests Counterを使用していますが...
// 計測器: "http.requests"

// イベント1
httpRequestsCounter.Add(ctx, 1, attribute.String("method", "GET"), attribute.Int("status", 200))

// イベント2
httpRequestsCounter.Add(ctx, 1, attribute.String("method", "POST"), attribute.Int("status", 500))

// イベント3
httpRequestsCounter.Add(ctx, 1, attribute.String("method", "GET"), attribute.Int("status", 200))

上記のコードでは、Event 1とEvent 3は {method=”GET”, status=”200″} という同じ属性を持ちます。したがって、これら2つは一つのメトリクスストリームに属します。

一方、Event 2は {method=”POST”, status=”500″} という異なる属性を持つため、完全に別個のメトリクスストリームとして処理されます。

  • ストリーム1: http.requests + {method=”GET”, status=”200″}
  • ストリーム2: http.requests + {method=”POST”, status=”500″}

このように、メトリクスストリームはデータを意味のある単位に分類して集計するための中間パイプラインであり、最終的な成果物である「時系列モデル」とは異なります。時系列はこのパイプラインを経て出てくる「結果物」です。


ステップ4:最終レポート、バックエンドへの転送 – Timeseries Model 📊

すべての集計と処理が完了すると、ついにPrometheusやDatadogのようなバックエンドシステムで私たちが見ることができる最終的なデータ形式が作成されます。これがTimeseries Modelです。

  • 説明: OTEL SDKがメトリクスストリームごとに集計、時間性(Temporality)処理などをすべて終えた後、外部にエクスポートする最終成果物です。
  • 構成: (メトリクス名+属性+データポイント[時間+値])の組み合わせで構成されます。

上記のメトリクスストリームの例が最終的にどのような時系列データになるか見てみましょう。

Raw Data (Timeseries Payload)

{
  "resourceMetrics": [
    {
      "scopeMetrics": [
        {
          "metrics": [
            {
              "name": "http.requests",
              "sum": {
                "dataPoints": [
                  {
                    "attributes": [
                      { "key": "method", "value": { "stringValue": "GET" } },
                      { "key": "status", "value": { "intValue": "200" } }
                    ],
                    "startTimeUnixNano": "1762489800000000000",
                    "timeUnixNano": "1762489860000000000",
                    "asInt": "2" // イベント1と3が合算された結果
                  },
                  {
                    "attributes": [
                      { "key": "method", "value": { "stringValue": "POST" } },
                      { "key": "status", "value": { "intValue": "500" } }
                    ],
                    "startTimeUnixNano": "1762489800000000000",
                    "timeUnixNano": "1762489860000000000",
                    "asInt": "1" // イベント2の結果
                  }
                ],
                "aggregationTemporality": "CUMULATIVE",
                "isMonotonic": true
              }
            }
          ]
        }
      ]
    }
  ]
}

Prometheusのようなシステムでは、このデータは以下の形式に変換されて表示されます。

http_requests_total{method="GET", status="200"} 2
http_requests_total{method="POST", status="500"} 1

これが、私たちがダッシュボードで目にするデータの最終形態です!


🗺️ 全体の流れをひと目で

それでは、この全過程を最初から最後までまとめてみましょう。

  1. Instrument Model (ツール選択 🛠️): 開発者が「リクエスト数を数えるぞ!」と言って、Counterというツールを選択します。
  2. Event Model (生測定値発生 👶): ユーザーのリクエストが来るたびに counter.add(1) が呼び出され、属性({method=”GET”})とともに生測定値が発生します。
  3. Metric Stream (論理的パイプライン処理 🛣️): SDKがこの測定値を属性別に区別します。{method=”GET”} リクエストはGETストリームへ、{method=”POST”} リクエストはPOSTストリームへ送られ、それぞれの論理的パイプラインで集計(Sum)が開始されます。
  4. Timeseries Model (最終成果物転送 📊): 一定時間集計された結果が、http_requests_total{method=”GET”} = 120 のような時系列データ(最終成果物)となり、モニタリングバックエンドに転送されます。

これでOpenTelemetryメトリクスがどのように動作するのか、完璧に理解できましたか?この4段階の流れを覚えておけば、今後どのようなメトリクス関連の問題に直面しても、自信を持って分析し解決できるようになるでしょう!


Comments

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です