# 画面設計書 26-Master Log（マスターログ）

## 概要

本ドキュメントは、マスターノードのログファイルをページネーション付きで表示する画面「Master Log（マスターログ）」の設計書である。

### 本画面の処理概要

本画面は、Spark Standaloneクラスタのマスターノードで出力されたログファイルの内容をWeb UI上で閲覧するための画面である。

**業務上の目的・背景**：Standaloneクラスタのマスターノードのログを確認する際、通常はSSH等でサーバーに接続してログファイルを直接参照する必要がある。本画面を使用することで、Web UIからリモートでマスターのログファイルの内容を確認でき、障害調査や運用監視の効率化を図ることができる。

**画面へのアクセス方法**：Master Overview画面（No.24）のStatus行にあるLogリンクをクリックすることで本画面に遷移する。URLパラメータとしてログタイプ（logType）が必要であり、通常は `out` が指定される。`self` パラメータが付与されることでマスター自身のログであることを示す。

**主要な操作・処理内容**：
1. ログファイルの指定範囲表示（デフォルトで末尾100KBを表示）
2. Load Moreボタンによる過去ログの追加読み込み
3. Load Newボタンによる最新ログの読み込み
4. バイト範囲の表示（表示中のバイト位置と総ファイルサイズ）

**画面遷移**：
- 遷移元：Master Overview画面（No.24）のLogリンク
- 遷移先：Master Overview画面への「Back to Master」リンク

**権限による表示制御**：特になし。Master WebUIのアクセス制御に依存する。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | Spark Web UI | 主機能 | マスターノードのログファイルをページネーション付きで表示する主処理 |
| 84 | Standaloneクラスタマネージャ | 補助機能 | マスタープロセスのログディレクトリからログファイルを取得 |

## 画面種別

詳細（ログビューア）

## URL/ルーティング

- **URL**: `/logPage/?self&logType={logType}`
- **HTTPメソッド**: GET
- **パラメータ**:
  - `logType` - ログタイプ（必須、"stderr" / "stdout" / "out"）
  - `self` - マスター自身のログであることを示すフラグ（存在するだけで有効）
  - `offset` - 表示開始バイト位置（任意）
  - `byteLength` - 表示バイト長（任意、デフォルト: 102400 = 100KB）
- **ページ名（WebUIPage）**: `logPage`
- **テキストAPI**: `/log?logType={logType}` （プレーンテキスト応答）

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| logType | 入力（URLパラメータ） | String | 必須 | ログタイプ（stderr/stdout/out） |
| offset | 入力（URLパラメータ） | Long | 任意 | 表示開始バイト位置 |
| byteLength | 入力（URLパラメータ） | Int | 任意 | 表示バイト長（デフォルト: 102400） |

## 表示項目

### ログ表示セクション

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| Showing {curLogLength} Bytes | endByte - startByte | 現在表示中のバイト数 |
| {startByte} - {endByte} | startByte, endByte | 表示範囲のバイト位置 |
| of {logLength} | logLength | ログファイルの総バイト数 |
| ログテキスト | getLog()の戻り値 | ログファイルの内容（pre要素で表示） |

### 操作ボタン

| ボタン名 | 説明 |
|----------|------|
| Load More | 過去のログを追加読み込み（上部に表示） |
| Load New | 最新のログを読み込み（下部に表示） |
| Back to Master | Master Overview画面に戻るリンク |

## イベント仕様

### 1-ページ読み込み

1. `logType` パラメータからログタイプを取得する
2. `SPARK_LOG_DIR` 環境変数からログディレクトリを特定する（デフォルト: "logs/"）
3. `deploy.Utils.getLog` でログファイルの指定範囲を読み取る
4. ログテキスト、開始バイト、終了バイト、ログ総長を取得する
5. HTML要素を構成し、`initLogPage` JavaScript関数のパラメータとしてバイト情報を渡す
6. ページロード時に `initLogPage` が自動実行される

### 2-Load Moreボタン押下

JavaScriptの `loadMore()` 関数が呼び出され、現在の表示範囲より前のログデータをAJAXで取得して画面上部に追加表示する。

### 3-Load Newボタン押下

JavaScriptの `loadNew()` 関数が呼び出され、現在の表示範囲より後のログデータをAJAXで取得して画面下部に追加表示する。ログの末尾に達した場合は「End of Log」アラートが表示される。

### 4-テキストAPI

`/log` エンドポイントへのアクセス時、`renderLog` 関数（deploy.Utils内）によりプレーンテキスト形式でログが返却される。

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ページ読み込み | ファイルシステム | READ | ログファイルの読み取り |

### テーブル別更新項目詳細

データベースへの更新は発生しない。ファイルシステムからのログ読み取りのみ。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 発生条件 |
|-------------|------|--------------|----------|
| MSG-01 | 情報 | "End of Log" | Load New押下時にログの末尾に達した場合 |
| MSG-02 | エラー | "Error: Log type must be one of stderr, stdout, out" | サポートされていないlogTypeが指定された場合 |
| MSG-03 | エラー | "Error getting logs due to exception: {message}" | ログファイル取得中に例外が発生した場合 |

## 例外処理

| 例外 | 発生条件 | 処理 |
|------|----------|------|
| 不正なlogType | stderr/stdout/out以外のlogTypeが指定された場合 | エラーメッセージを表示（ログテキスト領域に） |
| ファイル読み取りエラー | ログファイルが存在しない、または読み取り権限がない場合 | エラーメッセージを表示 |

## 備考

- ログファイルは `RollingFileAppender.getSortedRolledOverFiles` でロールオーバーファイルを含めてソートされる
- デフォルトの表示バイト数は 100KB（DEFAULT_BYTES = 100 * 1024）
- offsetが指定されない場合は、ログファイルの末尾から byteLength 分を表示する
- JavaScriptによるLoad More/Load Newの動的読み込みは `/log` テキストAPIを使用する
- ログ表示領域は `height:80vh; overflow:auto` で画面の80%の高さにスクロール可能
- ページタイトルは "{logType} log page for master"
- `utils.js` がモジュールとしてインポートされ、`initLogPage` 関数が提供される

---

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

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

### 推奨読解順序

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

ログファイルの読み取り結果のタプル構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Utils.scala | `core/src/main/scala/org/apache/spark/deploy/Utils.scala` | getLogメソッド（58-103行目）: 戻り値 `(String, Long, Long, Long)` = (logText, startByte, endByte, totalLength)。SUPPORTED_LOG_TYPES（37行目）: "stderr", "stdout", "out" |

**読解のコツ**: getLogはRollingFileAppenderのロールオーバーファイル対応であり、複数ファイルにまたがるログを統合的にバイト位置指定で読み取る。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | LogPage.scala（master） | `core/src/main/scala/org/apache/spark/deploy/master/ui/LogPage.scala` | renderメソッド（29-78行目）: パラメータ取得、getLog呼び出し、HTML生成。initLogPage JavaScriptの初期化パラメータを確認 |

**主要処理フロー**:
1. **30行目**: `SPARK_LOG_DIR` 環境変数からログディレクトリ取得（デフォルト "logs/"）
2. **31行目**: `request.getParameter("logType")` でログタイプ取得
3. **32-34行目**: offset, byteLength パラメータの取得
4. **35-36行目**: `getLog(parent.master.conf, logDir, logType, offset, byteLength)` 呼び出し
5. **37-41行目**: 表示範囲情報（Showing bytes）の生成
6. **58行目**: `logParams` にリクエストパラメータを構成
7. **59-60行目**: `initLogPage` JavaScript呼び出しコードの生成
8. **62-74行目**: HTML構成（Load More、ログテキスト、End of Logアラート、Load New）
9. **76行目**: `UIUtils.basicSparkPage` でHTMLレスポンス生成

#### Step 3: テキストAPI処理の理解

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Utils.scala | `core/src/main/scala/org/apache/spark/deploy/Utils.scala` | addRenderLogHandler（39-43行目）: `/log` エンドポイントのハンドラ登録。renderLog（45-55行目）: プレーンテキスト形式でのログ応答生成 |

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

```
LogPage.render(request)                     [GET /logPage/]
    |
    +-- sys.env.getOrElse("SPARK_LOG_DIR", "logs/")
    |
    +-- deploy.Utils.getLog(conf, logDir, logType, offset, byteLength)
    |       |
    |       +-- RollingFileAppender.getSortedRolledOverFiles(logDir, fileName)
    |       +-- Utils.getFileLength(file, conf)
    |       +-- Utils.offsetBytes(files, fileLengths, startIndex, endIndex)
    |
    +-- HTML生成（range, moreButton, logText, alert, newButton）
    |
    +-- initLogPage() JavaScript初期化
    |
    +-- UIUtils.basicSparkPage(request, content, title)

deploy.Utils.renderLog(request, conf)        [GET /log]
    |
    +-- deploy.Utils.getLog(conf, logDir, logType, offset, byteLength)
    +-- プレーンテキスト応答
```

### データフロー図

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

URLパラメータ            ───> LogPage.render                 ───> HTML Page
(logType, offset,             |                                  |
 byteLength)                  |                                  +-- バイト範囲表示
                              |                                  +-- ログテキスト (pre)
SPARK_LOG_DIR            ───> ログディレクトリ特定              +-- Load More/New ボタン
                              |
ファイルシステム               |
  +-- RollingFileAppender ───> getLog()                     ───> (logText, start, end, total)
      ログファイル群           |
                         offsetBytes()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| LogPage.scala | `core/src/main/scala/org/apache/spark/deploy/master/ui/LogPage.scala` | ソース | マスターログ画面のメインページクラス |
| Utils.scala | `core/src/main/scala/org/apache/spark/deploy/Utils.scala` | ソース | ログ取得ユーティリティ（getLog, addRenderLogHandler） |
| MasterWebUI.scala | `core/src/main/scala/org/apache/spark/deploy/master/ui/MasterWebUI.scala` | ソース | ページ・ハンドラ登録 |
| RollingFileAppender.scala | `core/src/main/scala/org/apache/spark/util/logging/RollingFileAppender.scala` | ソース | ロールオーバーログファイル管理 |
| utils.js | `core/src/main/resources/org/apache/spark/ui/static/utils.js` | JavaScript | initLogPage, loadMore, loadNew 関数の実装 |
| UIUtils.scala | `core/src/main/scala/org/apache/spark/ui/UIUtils.scala` | ソース | HTML生成ユーティリティ |
