# 帳票設計書 7-MetricsServlet JSON レポート

## 概要

本ドキュメントは、Apache Spark の MetricsSystem における MetricsServlet が出力する JSON 形式メトリクスレポートの設計書である。HTTP エンドポイントとして Jetty サーブレットを登録し、リクエストに応じて Dropwizard MetricRegistry の全メトリクス情報を JSON 形式で返却する。Jackson の MetricsModule を利用して JSON シリアライズを行う。

### 本帳票の処理概要

MetricsServlet は HTTP GET リクエストに対して、Spark の全メトリクス情報を JSON 形式で返却するサーブレットベースの帳票機能である。

**業務上の目的・背景**：Spark の Web UI に統合されたメトリクスエンドポイントとして、プログラムから容易にメトリクスデータを取得できる手段を提供する。REST API 的なアクセスパターンでメトリクスを取得できるため、カスタムモニタリングスクリプトやヘルスチェック機構から利用可能である。JSON 形式は構造化データの標準的な表現形式であり、あらゆるプログラミング言語から容易にパース可能である。

**帳票の利用シーン**：(1) カスタムモニタリングスクリプトから HTTP 経由でメトリクスを取得する場合、(2) ヘルスチェック機構で Spark の状態を確認する場合、(3) メトリクスデータを JSON として取得し独自のダッシュボードに表示する場合。

**主要な出力内容**：
1. Gauge メトリクス群（名前・値）のJSON 表現
2. Counter メトリクス群（名前・カウント値）のJSON 表現
3. Histogram メトリクス群（名前・統計値）のJSON 表現
4. Meter メトリクス群（名前・レート情報）のJSON 表現
5. Timer メトリクス群（名前・統計値・レート情報）のJSON 表現

**帳票の出力タイミング**：HTTP GET リクエストを受信した時点でオンデマンドに生成・返却される。定期出力ではなくリクエスト駆動型である。

**帳票の利用者**：Spark アプリケーション開発者、モニタリングスクリプト、ヘルスチェックシステム。

## 帳票種別

メトリクスレポート（HTTP オンデマンド型の JSON レスポンス）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | メトリクス JSON エンドポイント | `{servletPath}`（設定による、例: /metrics/json） | HTTP GET リクエスト |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | N/A（HTTP レスポンスボディ） |
| 出力方法 | HTTP レスポンス |
| 文字コード | UTF-8 |
| Content-Type | text/json |

### JSON 固有設定

| 項目 | 内容 |
|-----|------|
| シリアライザ | Jackson ObjectMapper + Dropwizard MetricsModule |
| 時間単位（Duration） | ミリ秒（MILLISECONDS） |
| レート単位 | 秒あたり（SECONDS） |
| サンプル表示 | 設定可能（sample プロパティ、デフォルト: false） |

## 帳票レイアウト

### レイアウト概要

JSON オブジェクトとして以下のトップレベル構造で出力される（MetricsModule の標準フォーマット）。

```json
{
  "version": "4.0.0",
  "gauges": { ... },
  "counters": { ... },
  "histograms": { ... },
  "meters": { ... },
  "timers": { ... }
}
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | version | MetricsModule バージョン | Dropwizard Metrics | 文字列 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | gauges | Gauge メトリクス群 | MetricRegistry.getGauges() | JSON オブジェクト | 可変 |
| 2 | counters | Counter メトリクス群 | MetricRegistry.getCounters() | JSON オブジェクト | 可変 |
| 3 | histograms | Histogram メトリクス群 | MetricRegistry.getHistograms() | JSON オブジェクト | 可変 |
| 4 | meters | Meter メトリクス群 | MetricRegistry.getMeters() | JSON オブジェクト | 可変 |
| 5 | timers | Timer メトリクス群 | MetricRegistry.getTimers() | JSON オブジェクト | 可変 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| N/A | フッターなし | JSON にフッターは存在しない | N/A | N/A |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| MetricRegistry 登録 | MetricRegistry に登録されている全メトリクスが対象 | Yes |
| HTTP リクエスト | servletPath への GET リクエスト受信 | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | メトリクス種別 | 固定順（gauges, counters, histograms, meters, timers） |
| 2 | メトリクス名 | 昇順（SortedMap） |

### 改ページ条件

JSON レスポンスのため改ページなし。

## データベース参照仕様

### 参照テーブル一覧

本帳票はデータベースを参照しない。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| N/A（MetricRegistry） | メトリクスデータの取得元 | N/A |

### テーブル別参照項目詳細

#### MetricRegistry（インメモリ）

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| 全メトリクス | JSON オブジェクト | mapper.writeValueAsString(registry) | Jackson でシリアライズ |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| レート変換 | 元値 / 秒 | MetricsModule 既定 | TimeUnit.SECONDS |
| Duration変換 | 元値(ns) -> ms | MetricsModule 既定 | TimeUnit.MILLISECONDS |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[HTTP GET リクエスト受信] --> B[getMetricsSnapshot]
    B --> C[ObjectMapper.writeValueAsString - registry]
    C --> D[JSON 文字列を HTTP レスポンスとして返却]
    D --> E[Content-Type: text/json]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| パス未設定 | servletPath プロパティが null | NullPointerException（サーブレットハンドラ登録時） | path 設定を追加 |
| シリアライズ失敗 | JSON 変換に失敗 | JsonProcessingException | メトリクス値の型を確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | メトリクス数に依存 |
| 目標出力時間 | リクエスト受信から数百ミリ秒以内 |
| 同時出力数上限 | Jetty サーバのスレッド数に依存 |

## セキュリティ考慮事項

- Spark Web UI のアクセス制御設定（ACL、認証）に従う
- メトリクスデータにはクラスタの内部状態が含まれるため、外部からのアクセスを適切に制限すること
- SparkConf の設定によりエンドポイントのアクセス制御が可能

## 備考

- 設定は `metrics.properties` ファイルで行う。設定例：
  ```
  *.sink.servlet.class=org.apache.spark.metrics.sink.MetricsServlet
  *.sink.servlet.path=/metrics/json
  *.sink.servlet.sample=false
  ```
- MetricsSystem 内で特別扱いされており、通常の Sink とは異なり metricsServlet フィールドに格納される（行207-211）
- start()/stop()/report() はすべて空実装であり、サーブレットハンドラとしてのみ機能する
- getHandlers() メソッドにより Spark Web UI にサーブレットとして登録される

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Sink.scala | `core/src/main/scala/org/apache/spark/metrics/sink/Sink.scala` | Sink トレイト定義（行20-24） |

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MetricsSystem.scala | `core/src/main/scala/org/apache/spark/metrics/MetricsSystem.scala` | registerSinks() 内の servlet 特別処理（行207-211）と getServletHandlers()（行89-93）を確認。MetricsServlet は sinks 配列ではなく metricsServlet フィールドに格納される |

**主要処理フロー**:
1. **行207-211**: registerSinks() で kv._1 == "servlet" の場合、MetricsServlet をインスタンス化して metricsServlet フィールドに格納
2. **行89-93**: getServletHandlers() で metricsServlet.getHandlers(conf) を呼び出し、Jetty ハンドラを返す

#### Step 3: MetricsServlet の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MetricsServlet.scala | `core/src/main/scala/org/apache/spark/metrics/sink/MetricsServlet.scala` | クラス全体（行32-64）を通読。サーブレット型の Sink であり、start/stop/report は空実装。getHandlers() と getMetricsSnapshot() がメインロジック |

**主要処理フロー**:
- **行32-33**: Properties と MetricRegistry をコンストラクタ引数として受け取る
- **行35-43**: servletPath と servletShowSample の設定読み込み
- **行45-46**: ObjectMapper に MetricsModule を登録（秒単位のレート、ミリ秒単位の Duration、サンプル表示設定）
- **行48-53**: getHandlers() で Jetty の ServletContextHandler を生成。リクエスト受信時に getMetricsSnapshot() を呼び出すラムダを登録
- **行55-57**: getMetricsSnapshot() で mapper.writeValueAsString(registry) により JSON 文字列を生成
- **行59-63**: start()/stop()/report() はすべて空実装

### プログラム呼び出し階層図

```
MetricsSystem.start()
    |
    +-- registerSinks()
    |       +-- kv._1 == "servlet"
    |       +-- MetricsServlet.<init>(properties, registry)
    |               +-- ObjectMapper + MetricsModule 初期化
    |       +-- metricsServlet = Some(servlet)
    |
    +-- getServletHandlers()
            +-- MetricsServlet.getHandlers(conf)
                    +-- createServletHandler(servletPath, ...)
                            +-- [HTTP GET] getMetricsSnapshot(request)
                                    +-- mapper.writeValueAsString(registry)
                                    +-- JSON レスポンス返却
```

### データフロー図

```
[入力]                      [処理]                         [出力]

HTTP GET リクエスト      MetricsServlet                HTTP レスポンス
(servletPath)           +-- getMetricsSnapshot()      Content-Type: text/json
                            +-- ObjectMapper           {
MetricRegistry              +-- MetricsModule            "gauges": {...},
(Gauges,                    +-- writeValueAsString       "counters": {...},
 Counters,                      (registry)               "histograms": {...},
 Histograms,                                             "meters": {...},
 Meters,                                                 "timers": {...}
 Timers)                                               }

Properties
(path, sample)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Sink.scala | `core/src/main/scala/org/apache/spark/metrics/sink/Sink.scala` | ソース | Sink トレイト定義 |
| MetricsServlet.scala | `core/src/main/scala/org/apache/spark/metrics/sink/MetricsServlet.scala` | ソース | JSON メトリクスエンドポイント実装 |
| MetricsSystem.scala | `core/src/main/scala/org/apache/spark/metrics/MetricsSystem.scala` | ソース | MetricsServlet の特別処理と getServletHandlers() |
| JettyUtils.scala | `core/src/main/scala/org/apache/spark/ui/JettyUtils.scala` | ソース | Jetty サーブレットハンドラ生成ユーティリティ |
