# 帳票設計書 21-DriverLogPage

## 概要

本ドキュメントは、Apache Spark Web UI における Driver ログ表示ページ（DriverLogPage）の帳票設計書である。Driver プロセスのログファイルをブラウザ上で閲覧するための HTML レポートページの仕様を定義する。

### 本帳票の処理概要

DriverLogPage は Spark Driver のログファイル内容をリアルタイムで Web ブラウザ上に表示する帳票レポートページである。

**業務上の目的・背景**：Spark アプリケーション実行時、Driver プロセスのログは障害調査やパフォーマンス分析に不可欠な情報源である。従来はサーバにログインしてファイルを直接閲覧する必要があったが、本帳票により Web UI 経由でリモートからログを確認でき、運用効率が大幅に向上する。

**帳票の利用シーン**：Spark アプリケーション実行中または History Server 経由で過去のアプリケーションログを確認する場合に利用される。アプリケーションエラー発生時の原因調査、処理の進捗確認、デバッグ情報の閲覧などのシーンで活用される。

**主要な出力内容**：
1. ログファイルの内容（テキスト形式）の部分的な表示
2. 現在表示中のバイト範囲情報（開始バイト、終了バイト、総バイト数）
3. 「Load More」ボタンによる過去のログの追加読み込み機能
4. 「Load New」ボタンによる最新ログの読み込み機能

**帳票の出力タイミング**：Spark Web UI の「Logs」タブにアクセスした際にリアルタイムで生成・表示される。ログファイルが存在する限りいつでも閲覧可能である。

**帳票の利用者**：Spark アプリケーション開発者、インフラ運用担当者、データエンジニア

## 帳票種別

Web UI ページ（リアルタイムログビューア）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| 21 | Driver Log Page | `/logs/` | Spark UI の「Logs」タブをクリック |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | HTML（Web UI ページ） |
| 用紙サイズ | N/A（ブラウザ表示） |
| 向き | N/A |
| ファイル名 | N/A（動的 HTML レスポンス） |
| 出力方法 | ブラウザ上にリアルタイム表示 |
| 文字コード | UTF-8 |

### PDF固有設定

N/A（HTML 形式のため該当なし）

### Excel固有設定

N/A（HTML 形式のため該当なし）

## 帳票レイアウト

### レイアウト概要

ページはスクロール可能なログ表示エリアを中心に構成される。上部にバイト範囲情報、ログエリア内に「Load More」「Load New」ボタンを配置する。

```
+---------------------------------------------+
|           Spark UI ヘッダー                    |
+---------------------------------------------+
| Logs at {logDir}                             |
| Showing {bytes} Bytes: {start} - {end} of   |
|   {total}                                    |
+---------------------------------------------+
| [Load More]                                  |
|                                              |
|   ログ内容（<pre> タグ内テキスト）               |
|   （高さ 80vh、スクロール可能）                  |
|                                              |
| End of Log（非表示アラート）                    |
| [Load New]                                   |
+---------------------------------------------+
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | ログディレクトリ | ログファイルの格納先パス | SparkConf (DRIVER_LOG_LOCAL_DIR) | テキスト |
| 2 | バイト範囲情報 | 表示中のバイト範囲 | getLog() の戻り値 | "Showing {curLogLength} Bytes: {startByte} - {endByte} of {logLength}" |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | ログテキスト | ログファイルの部分内容 | RollingFileAppender.getSortedRolledOverFiles() + Utils.offsetBytes() | pre タグ内プレーンテキスト | 100% |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | End of Log アラート | ログ末尾到達時の通知 | JavaScript (display:none 初期値) | Bootstrap alert-info |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| logType | ログ種別（driver.log, stderr, stdout のいずれか） | No（デフォルト: driver.log） |
| offset | 読み出し開始バイト位置 | No（デフォルト: totalLength - byteLength） |
| byteLength | 読み出すバイト数 | No（デフォルト: 102400 = 100KB） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | バイトオフセット | 昇順（ログの時系列順） |

### 改ページ条件

改ページは発生しない。スクロールにより全内容を閲覧する。Load More/Load New ボタンで追加データを読み込む。

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

### 参照テーブル一覧

本帳票はデータベースを参照しない。ローカルファイルシステム上のログファイルを直接参照する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| N/A（ファイルシステム） | Driver ログファイルの読み取り | N/A |

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

#### ログファイルシステム

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| ログファイル内容 | ログテキスト | RollingFileAppender でソートされたファイル群を offset〜endIndex の範囲で取得 | ローテーション対応済み |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| startIndex | max(0, min(offset, totalLength)) | なし | offset 未指定時は totalLength - byteLength |
| endIndex | min(startIndex + byteLength, totalLength) | なし | ファイル末尾を超えない |
| curLogLength | endByte - startByte | なし | 実際に表示するバイト数 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[HTTPリクエスト受信] --> B[パラメータ解析: logType, offset, byteLength]
    B --> C{logType が有効か？}
    C -->|No| D[エラーメッセージ返却]
    C -->|Yes| E[RollingFileAppender でログファイル一覧取得]
    E --> F[ファイルサイズ合計算出]
    F --> G[offset, endIndex 計算]
    G --> H[Utils.offsetBytes でログテキスト取得]
    H --> I[HTML テンプレート生成]
    I --> J[レスポンス返却]
    D --> J
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 不正なログタイプ | logType が supportedLogTypes に含まれない | "Error: Log type must be one of driver.log, stderr, stdout" | 正しいログタイプを指定する |
| ファイル読み取りエラー | ログディレクトリの読み取り時に例外発生 | "Error getting logs due to exception: {message}" | ログディレクトリのパーミッション・存在を確認する |
| 設定不備 | DRIVER_LOG_LOCAL_DIR が未設定 | require 例外によりページ表示不可 | spark.driver.log.localDir を設定する |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 単一ログファイルまたはローテーション済みファイル群（合計数十MB程度） |
| 目標出力時間 | デフォルト 100KB 読み込みで 1 秒以内 |
| 同時出力数上限 | Spark Web UI のスレッドプール制約に従う |

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

- ログファイルにはアプリケーション固有の情報（設定値、エラーメッセージなど）が含まれる可能性があるため、Spark UI のアクセス制御（ACL 設定、spark.ui.filters）を適切に構成する必要がある。
- ログ内容はそのまま HTML の pre タグ内に表示されるが、Scala XML リテラルによりエスケープされるため XSS のリスクは低い。

## 備考

- DriverLogPage は Worker の LogPage と類似した設計を持つ（ソースコード内コメントに記載あり）。
- renderLog メソッドは JSON API 向けのログテキスト返却にも利用される。
- ログファイルのローテーション機能は RollingFileAppender が提供する。

---

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

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

### 推奨読解順序

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

まず、ログの取得に関わる設定値とログファイル管理の仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DRIVER_LOG_LOCAL_DIR | `core/src/main/scala/org/apache/spark/internal/config/package.scala` | ログディレクトリの設定キー定義 |
| 1-2 | DriverLogger.scala | `core/src/main/scala/org/apache/spark/util/logging/DriverLogger.scala` | DRIVER_LOG_FILE 定数の定義、Driver ログの生成元 |
| 1-3 | RollingFileAppender.scala | `core/src/main/scala/org/apache/spark/util/logging/RollingFileAppender.scala` | ログファイルのローテーション管理とソート済みファイル一覧の取得 |

**読解のコツ**: Scala の case class やコンパニオンオブジェクト内の定数定義に注目する。DRIVER_LOG_FILE はログファイル名の定数であり、supportedLogTypes と対応している。

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

処理の起点となる DriverLogTab と DriverLogPage の関係を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | render() メソッドが HTTP リクエストを受け取りHTML生成する起点（行46-92） |

**主要処理フロー**:
1. **行47-50**: リクエストパラメータ（logType, offset, byteLength）の解析
2. **行51**: getLog() メソッドでログテキスト・バイト範囲情報を取得
3. **行77-89**: HTML コンテンツの組み立て（ログ表示エリア、ボタン、JavaScript）
4. **行91**: UIUtils.headerSparkPage でページヘッダー付き HTML を生成

#### Step 3: ログ取得処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | getLog() メソッド（行106-144）がログファイルからテキストを取得する核心部分 |

**主要処理フロー**:
- **行113-115**: logType の検証（supportedLogTypes に含まれるか確認）
- **行118**: RollingFileAppender.getSortedRolledOverFiles でローテーション済みファイル一覧取得
- **行121-122**: 各ファイルのサイズ取得と合計算出
- **行123-133**: offset と startIndex/endIndex の計算
- **行135**: Utils.offsetBytes で指定範囲のテキストを取得

#### Step 4: Tab との接続を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | DriverLogTab クラス（行150-155）が SparkUI にタブを登録する仕組み |

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

```
SparkUI
    |
    +-- DriverLogTab (SparkUITab を継承, prefix="logs")
           |
           +-- DriverLogPage (WebUIPage を継承, prefix="")
                  |
                  +-- render(request)
                  |      +-- getLog(logDir, logType, offset, byteLength)
                  |      |      +-- RollingFileAppender.getSortedRolledOverFiles()
                  |      |      +-- Utils.getFileLength()
                  |      |      +-- Utils.offsetBytes()
                  |      +-- UIUtils.headerSparkPage()
                  |
                  +-- renderLog(request) [JSON API 向け]
                         +-- getLog(logDir, logType, offset, byteLength)
```

### データフロー図

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

HTTPリクエスト         DriverLogPage.render()
(logType,offset,   --> パラメータ解析                 --> HTML レスポンス
 byteLength)           |                                (ログテキスト +
                       v                                 バイト範囲情報 +
ログファイル群     --> getLog()                           操作ボタン)
(driver.log,           |
 stderr, stdout)       +-- RollingFileAppender
                       +-- Utils.offsetBytes()

SparkConf          --> DRIVER_LOG_LOCAL_DIR
(設定値)               (ログディレクトリパス)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | ソース | メインの帳票ページクラスおよびタブクラス |
| DriverLogger.scala | `core/src/main/scala/org/apache/spark/util/logging/DriverLogger.scala` | ソース | DRIVER_LOG_FILE 定数の定義、ログ出力元 |
| RollingFileAppender.scala | `core/src/main/scala/org/apache/spark/util/logging/RollingFileAppender.scala` | ソース | ローテーション済みログファイルのソート取得 |
| UIUtils.scala | `core/src/main/scala/org/apache/spark/ui/UIUtils.scala` | ソース | HTML ヘッダー生成ユーティリティ |
| Utils.scala | `core/src/main/scala/org/apache/spark/util/Utils.scala` | ソース | offsetBytes, getFileLength メソッド提供 |
| config/package.scala | `core/src/main/scala/org/apache/spark/internal/config/package.scala` | ソース | DRIVER_LOG_LOCAL_DIR 設定定義 |
| utils.js | `core/src/main/resources/org/apache/spark/ui/static/utils.js` | 静的リソース | initLogPage 関数（クライアント側ログ操作） |
