# 画面設計書 13-Driver Log（ドライバログ）

## 概要

本ドキュメントは、Apache Spark Application UIのDriver Logタブにあるドライバログ画面の設計内容を記述する。本画面はドライバのログファイル（driver.log / stderr / stdout）をページネーション付きで表示する。

### 本画面の処理概要

**業務上の目的・背景**：Sparkアプリケーションのデバッグやトラブルシューティングにおいて、ドライバプロセスのログを確認することは基本的な作業である。特にクラスタモードでドライバが遠隔ノード上で動作する場合、ログファイルへの直接アクセスが困難なことがある。本画面により、Web UI上でドライバログをリアルタイムに閲覧でき、ログファイルの末尾から段階的に読み込む機能やページネーション機能を通じて、大容量のログファイルも効率的に確認できる。

**画面へのアクセス方法**：SparkUIのタブバーに「Logs」タブが表示される（DRIVER_LOG_LOCAL_DIR設定時のみ）。タブをクリックしてアクセスする。logTypeパラメータでログ種別を切り替え可能。

**主要な操作・処理内容**：
1. ログ内容の表示（デフォルトでdriver.logの末尾100KBを表示）
2. 「Load More」ボタンで過去のログを追加読み込みする
3. 「Load New」ボタンで最新のログを追加読み込みする
4. logTypeパラメータでdriver.log / stderr / stdoutを切り替える
5. offset / byteLengthパラメータで表示範囲を指定する

**画面遷移**：SparkUIのタブバーから直接アクセスする。本画面からの遷移先はない。他の画面から本画面への明示的な遷移リンクはない。

**権限による表示制御**：DRIVER_LOG_LOCAL_DIR設定が存在しない場合、Driver Logタブ自体が表示されない。ACL設定が有効な場合、閲覧権限が必要。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | Spark Web UI | 主機能 | ドライバのログファイル（driver.log/stderr/stdout）をページネーション付きで表示する主処理 |
| 11 | イベントログ記録 | 補助機能 | ドライバログディレクトリからRollingFileAppenderのログファイルを取得・表示 |

## 画面種別

詳細（ログビューア）

## URL/ルーティング

- パス: `/logs/`
- パラメータ:
  - `logType` (任意、デフォルト: "driver.log") - ログ種別
  - `offset` (任意) - 読み込み開始バイト位置
  - `byteLength` (任意、デフォルト: 102400) - 読み込みバイト数
- 例: `/logs/?logType=driver.log`

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| logType | 入力（URLパラメータ） | String | 任意 | ログ種別。driver.log, stderr, stdoutのいずれか。デフォルト: driver.log |
| offset | 入力（URLパラメータ） | Long | 任意 | 読み込み開始バイト位置。省略時はログ末尾からbyteLength分 |
| byteLength | 入力（URLパラメータ） | Int | 任意 | 読み込みバイト数。デフォルト: 102400 (100KB) |

## 表示項目

### ログ表示エリア

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| ログディレクトリパス | String | "Logs at {logDir}" 形式でログディレクトリを表示 |
| 表示範囲情報 | String | "Showing {curLogLength} Bytes: {startByte} - {endByte} of {logLength}" |
| ログ本文 | String | preタグ内にログテキストを表示 |

## イベント仕様

### 1-Load More ボタン

「Load More」ボタンをクリックすると、現在表示しているログの前方（過去方向）のログを追加読み込みする。JavaScript関数loadMore()で制御される。AJAX呼び出しによりrenderLogエンドポイントにアクセスし、追加ログを取得する。

### 2-Load New ボタン

「Load New」ボタンをクリックすると、現在表示しているログの後方（最新方向）のログを追加読み込みする。JavaScript関数loadNew()で制御される。ログの末尾に到達した場合は「End of Log」アラートが表示される。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ページ表示 | - | ファイル読み込み | ドライバのローカルログディレクトリからログファイルを読み込み |
| Load More | - | ファイル読み込み | 過去方向のログデータを追加読み込み |
| Load New | - | ファイル読み込み | 最新方向のログデータを追加読み込み |

本画面はデータベースへの更新操作を行わない。ローカルファイルシステム上のログファイルを読み取る。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| MSG-001 | 情報 | "End of Log" | Load Newでログ末尾に到達した場合 |
| MSG-002 | エラー | "Error: Log type must be one of driver.log, stderr, stdout" | 不正なlogTypeが指定された場合 |
| MSG-003 | エラー | "Error getting logs due to exception: {message}" | ログファイル読み込み中に例外が発生した場合 |

## 例外処理

- DRIVER_LOG_LOCAL_DIR未設定の場合: require文によりDriverLogPage生成時にエラーとなり、タブ自体が登録されない
- 不正なlogType指定: サポートされるlogType（driver.log, stderr, stdout）以外はエラーメッセージを返す
- ログファイル読み込みエラー: 例外をキャッチし、エラーメッセージとゼロ値の位置情報を返す

## 備考

- DRIVER_LOG_LOCAL_DIR設定（spark.driver.log.localDir）が存在する場合のみ、DriverLogTabがSparkUIに登録される
- RollingFileAppenderのログローテーションに対応しており、複数のローテーションファイルを跨いでログを読み込むことができる
- デフォルトの読み込みバイト数は100KB（100 * 1024バイト）
- renderLogメソッドはAJAX呼び出し用にプレーンテキスト形式でログを返すAPIとしても機能する
- ログの表示は80vhの固定高さスクロール領域内に表示される

---

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

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

### 推奨読解順序

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

ログファイルの読み込み結果の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | 行106-111: getLogメソッドの戻り値型 (String, Long, Long, Long) = (logText, startByte, endByte, logLength) |

**読解のコツ**: getLogメソッドは4つ組タプルを返す。logTextがログ本文、startByte/endByteが読み込み範囲、logLengthがファイル全体の長さ。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | 行150-155: DriverLogTabクラス。SparkUITab(parent, "logs")でタブ名を設定 |
| 2-2 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | 行36-39: DriverLogPageクラス定義。WebUIPage("")でデフォルトページ。DRIVER_LOG_LOCAL_DIRの存在をrequire |

**主要処理フロー**:
1. **行47-48**: renderメソッドでlogType/offset/byteLengthパラメータを取得
2. **行51**: getLog(logDir, logType, offset, byteLength)でログデータを取得
3. **行52-56**: 表示範囲情報をHTML生成
4. **行58-66**: Load More/Load Newボタン生成
5. **行73-75**: JavaScript初期化コード（initLogPage関数呼び出し）
6. **行77-89**: ログ表示HTML生成
7. **行91**: UIUtils.headerSparkPageでページ全体をラップ

#### Step 3: ログファイル読み込みロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | 行106-144: getLogメソッド。RollingFileAppenderのログファイルをソートして取得し、offsetに基づいて読み込む |
| 3-2 | RollingFileAppender.scala | `core/src/main/scala/org/apache/spark/util/logging/RollingFileAppender.scala` | getSortedRolledOverFilesメソッド。ログローテーションファイルの一覧取得 |
| 3-3 | Utils.scala | `core/src/main/scala/org/apache/spark/util/Utils.scala` | offsetBytesメソッド。複数ファイルを跨いだバイト範囲読み込み |

#### Step 4: AJAX APIを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | 行94-103: renderLogメソッド。AJAX用にプレーンテキスト形式でログを返す |

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

```
DriverLogTab(parent: SparkUI) [行150]
    |
    +-- DriverLogPage(this, parent.conf) [行151]
            |
            +-- render(request) [行46]
            |       |
            |       +-- getLog(logDir, logType, offset, byteLength) [行51]
            |       |       |
            |       |       +-- RollingFileAppender.getSortedRolledOverFiles() [行118]
            |       |       +-- Utils.getFileLength() [行121]
            |       |       +-- Utils.offsetBytes() [行135]
            |       |
            |       +-- JavaScript initLogPage() [行74-75]
            |       +-- UIUtils.headerSparkPage() [行91]
            |
            +-- renderLog(request) [行94] (AJAX API)
                    |
                    +-- getLog(logDir, logType, offset, byteLength) [行100]
```

### データフロー図

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

logType ─────────────────> DriverLogPage.render()
offset  ─────────────────>        |
byteLength ──────────────>        |
                                   +---> getLog()
                                   |       |
                                   |       +---> RollingFileAppender
                                   |       |     .getSortedRolledOverFiles()
                                   |       |           |
                                   |       |     ログファイル一覧 <──
                                   |       |           |
                                   |       +---> Utils.offsetBytes()
                                   |       |           |
                                   |       |     (logText, start, end, length) <──
                                   |       |
                                   +---> ログ表示HTML生成 ────────> ログビューアHTML
                                   |
                                   +---> headerSparkPage() ────────> 完成ページHTML
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DriverLogPage.scala | `core/src/main/scala/org/apache/spark/ui/DriverLogPage.scala` | ソース | メイン画面ページクラス・タブクラス |
| RollingFileAppender.scala | `core/src/main/scala/org/apache/spark/util/logging/RollingFileAppender.scala` | ソース | ログローテーションファイル管理 |
| Utils.scala | `core/src/main/scala/org/apache/spark/util/Utils.scala` | ソース | ファイル読み込みユーティリティ（offsetBytes等） |
| UIUtils.scala | `core/src/main/scala/org/apache/spark/ui/UIUtils.scala` | ソース | UI共通ユーティリティ |
| utils.js | `core/src/main/resources/org/apache/spark/ui/static/utils.js` | JavaScript | initLogPage/loadMore/loadNew関数 |
| DriverLogger.scala | `core/src/main/scala/org/apache/spark/util/logging/DriverLogger.scala` | ソース | DRIVER_LOG_FILE定数定義 |
