# 帳票設計書 15-ログファイル（ローテーション）

## 概要

本ドキュメントは、Godot Engineにおけるログファイル出力機能に関する設計書である。エンジンのログ出力をファイルに保存し、バックアップ数を設定可能なローテーション機能を提供する RotatedFileLogger クラスについて詳細に記述する。

### 本帳票の処理概要

本機能は、Godot Engineの実行時ログ（print()、エラー、警告、スクリプトエラーなど）をファイルに出力する。既存のログファイルが存在する場合はタイムスタンプ付きでバックアップを作成し、設定された最大バックアップ数を超えた古いファイルは自動的に削除される。ANSIエスケープシーケンス（print_rich()で使用）はファイル出力時に自動的に除去される。

**業務上の目的・背景**：ゲーム開発およびデバッグにおいて、実行時のログを永続化することは問題解析に不可欠である。ローテーション機能により、ログファイルの肥大化を防ぎつつ、過去のログを一定期間保持できる。これにより、後からバグの原因を追跡したり、ユーザーからのバグレポート時にログファイルを参照したりすることが可能となる。

**帳票の利用シーン**：デバッグ時のログ確認、リリースビルドでのエラートラッキング、ユーザー環境での問題調査、自動テスト結果の記録、長時間実行時のログ管理など。

**主要な出力内容**：
1. 通常のprint()出力（標準出力ログ）
2. エラーメッセージ（ERROR、WARNING、SCRIPT ERROR、SHADER ERROR）
3. スクリプトバックトレース情報
4. タイムスタンプ付きバックアップファイル

**帳票の出力タイミング**：エンジン起動時にログファイルが開かれ、print()、print_rich()、各種エラー出力時に随時ログが追記される。エンジン再起動時に既存ログがバックアップされる。

**帳票の利用者**：ゲーム開発者、QAエンジニア、テクニカルサポート、エンドユーザー（ログ提供時）

## 帳票種別

ログ出力 / テキストファイル出力

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | GDScript | print() / print_rich() | スクリプトからのログ出力 |
| - | エンジン内部 | push_error() / push_warning() | エラー・警告出力 |
| - | エディタ | Output パネル | エディタ実行時のログ |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキストファイル（.log 等） |
| 用紙サイズ | N/A（テキスト出力） |
| 向き | N/A |
| ファイル名 | 設定による（例: godot.log） |
| 出力方法 | ファイル追記 |
| 文字コード | UTF-8 |

### ローテーション設定

| 項目 | 内容 |
|-----|------|
| 最大バックアップ数 | コンストラクタ引数 p_max_files（デフォルト10） |
| バックアップ命名規則 | {basename}{timestamp}.{extension} |
| タイムスタンプ形式 | YYYY-MM-DDTHH.MM.SS（コロンをピリオドに置換） |
| ローテーションタイミング | エンジン起動時（既存ファイル存在時） |

## 帳票レイアウト

### レイアウト概要

ログファイルはプレーンテキスト形式で、各ログエントリが行単位で記録される。

```
通常ログ出力例:
Starting game...
Player spawned at position (100, 200)

エラーログ出力例:
ERROR: Failed to load resource
   at: load_resource (res://scripts/loader.gd:42)
   Script Stack:
      main.gd:15 - _ready()
      loader.gd:42 - load_resource()
```

### ログエントリ形式

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | エラータイプ | ERROR/WARNING/SCRIPT ERROR/SHADER ERROR | p_type | 文字列 |
| 2 | エラー詳細 | エラーメッセージまたはコード | p_rationale または p_code | 文字列 |
| 3 | 発生位置 | 関数名、ファイル、行番号 | p_function, p_file, p_line | "at: {func} ({file}:{line})" |
| 4 | スクリプトバックトレース | 呼び出しスタック | p_script_backtraces | インデント付き複数行 |

### バックアップファイル構成

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | ベース名 | 元ファイルのベース名 | base_path.get_basename() | 文字列 |
| 2 | タイムスタンプ | バックアップ作成日時 | Time::get_datetime_string_from_system() | YYYY-MM-DDTHH.MM.SS |
| 3 | 拡張子 | 元ファイルの拡張子 | base_path.get_extension() | 文字列 |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ログ有効化 | CoreGlobals::print_line_enabled / print_error_enabled | No |
| 出力パス | 有効なファイルパス | Yes |
| 最大ファイル数 | 1以上の整数 | Yes（デフォルト10） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | バックアップファイル名（タイムスタンプ） | 昇順（古いものから削除） |

### 改ページ条件

N/A（テキストファイル出力）

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

### 参照テーブル一覧

N/A（ランタイムログ出力）

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 最大バックアップ数 | max_files - 1 | - | 現在のファイル分を除く |
| 削除対象数 | backups.size() - max_backups | - | 超過分を削除 |

## 処理フロー

### 初期化フロー

```mermaid
flowchart TD
    A[RotatedFileLogger コンストラクタ] --> B[base_path 正規化]
    B --> C[max_files 設定]
    C --> D[rotate_file 呼び出し]
    D --> E{既存ファイル存在?}
    E -->|Yes| F{max_files > 1?}
    E -->|No| G[ディレクトリ作成]
    F -->|Yes| H[タイムスタンプ生成]
    F -->|No| I[ファイルオープン]
    H --> J[バックアップコピー作成]
    J --> K[古いバックアップ削除]
    K --> I
    G --> I
    I --> L[ANSI正規表現初期化]
    L --> M[終了]
```

### ログ出力フロー

```mermaid
flowchart TD
    A[logv 呼び出し] --> B{should_log?}
    B -->|No| C[終了]
    B -->|Yes| D{file 有効?}
    D -->|No| C
    D -->|Yes| E[vsnprintf でフォーマット]
    E --> F{バッファサイズ超過?}
    F -->|Yes| G[動的メモリ確保]
    F -->|No| H[ANSIエスケープ除去]
    G --> H
    H --> I[file->store_string]
    I --> J{エラーまたはflush設定?}
    J -->|Yes| K[file->flush]
    J -->|No| L[終了]
    K --> L
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ディレクトリアクセス失敗 | DirAccess::open 失敗 | 暗黙的失敗（return） | パスを確認 |
| ファイルオープン失敗 | FileAccess::open 失敗 | 暗黙的失敗 | 書き込み権限を確認 |
| バックアップコピー失敗 | DirAccess::copy 失敗 | 暗黙的失敗 | ディスク容量を確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定ログ量 | 無制限（ローテーションで管理） |
| フラッシュ頻度 | エラー時または _flush_stdout_on_print 設定時 |
| 同時アクセス | シングルスレッド想定 |
| バッファサイズ | 512バイト（静的）、超過時は動的確保 |

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

- ログファイルに機密情報が含まれる可能性があるため、適切なファイル権限の設定が必要
- ユーザーデータディレクトリに出力される場合、他アプリからのアクセスに注意
- print_rich() のANSIエスケープシーケンスは自動除去される

## 備考

- RotatedFileLogger は ObjectDB からデタッチされる（エンジン終了時の寿命管理のため）
- RegEx モジュールが有効な場合のみANSIエスケープシーケンス除去が機能
- CompositeLogger を使用して複数のロガーを組み合わせることが可能
- StdLogger と組み合わせて標準出力とファイル出力を同時に行うことが多い

---

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

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

### 推奨読解順序

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

まず、Logger 基底クラスと RotatedFileLogger の継承関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | logger.h | `core/io/logger.h` | Logger 抽象クラスの定義、ErrorType 列挙型 |
| 1-2 | logger.h | `core/io/logger.h` | RotatedFileLogger クラスのメンバ変数定義（行111-126） |

**読解のコツ**: Logger は抽象基底クラスで、StdLogger（標準出力）、RotatedFileLogger（ファイル出力）、CompositeLogger（複合ロガー）が派生クラス。

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

処理の起点となるコンストラクタを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | logger.cpp | `core/io/logger.cpp` | RotatedFileLogger コンストラクタ（行170-180） |

**主要処理フロー**:
1. **行170-172**: base_path の正規化と max_files の設定
2. **行173**: rotate_file() 呼び出し
3. **行175-179**: RegEx モジュール有効時のANSIエスケープ除去用正規表現初期化

#### Step 3: ローテーション処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | logger.cpp | `core/io/logger.cpp` | rotate_file 関数（行142-168） |
| 3-2 | logger.cpp | `core/io/logger.cpp` | clear_old_backups 関数（行108-140） |

**主要処理フロー**:
- **行142-144**: 既存ファイルハンドルをクローズ
- **行145-158**: 既存ファイルが存在し max_files > 1 の場合、タイムスタンプ付きバックアップを作成
- **行157**: clear_old_backups() で古いバックアップを削除
- **行159-164**: ディレクトリが存在しない場合は作成
- **行166-167**: 新規ログファイルをオープン、ObjectDB からデタッチ

#### Step 4: ログ出力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | logger.cpp | `core/io/logger.cpp` | RotatedFileLogger::logv 関数（行182-219） |
| 4-2 | logger.cpp | `core/io/logger.cpp` | Logger::log_error 関数（行58-80） |

**主要処理フロー**:
- **行183-185**: should_log() による出力判定
- **行188-198**: vsnprintf によるフォーマット、バッファサイズ超過時は動的確保
- **行200-207**: ANSIエスケープシーケンス除去（RegExモジュール有効時）
- **行213-217**: エラー時またはフラッシュ設定時にflush()

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

```
GDScript: print() / push_error() / push_warning()
    │
    └─ CompositeLogger::logv() [複数ロガーに分配]
           │
           ├─ StdLogger::logv() ─────▶ stdout/stderr
           │
           └─ RotatedFileLogger::logv()
                  │
                  ├─ should_log() - 出力判定
                  │
                  ├─ vsnprintf() - フォーマット
                  │
                  ├─ strip_ansi_regex->sub() - ANSI除去
                  │
                  └─ file->store_string() ─────▶ ログファイル

エンジン起動時:
RotatedFileLogger::RotatedFileLogger(path, max_files)
    │
    └─ rotate_file()
           │
           ├─ FileAccess::exists() - 既存ファイル確認
           │
           ├─ [存在時] DirAccess::copy() - バックアップ作成
           │       │
           │       └─ clear_old_backups() - 古いバックアップ削除
           │
           └─ FileAccess::open(WRITE) - 新規ファイルオープン
```

### データフロー図

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

print() 呼び出し
    │
    └─ 文字列 ─────────────▶ logv() ─────────────▶ ログファイル
                                │
                                ▼
                         フォーマット処理
                                │
                                ▼
                         ANSIエスケープ除去
                                │
                                ▼
                         store_string()

エンジン起動
    │
    └─ 既存ログ ────────▶ rotate_file() ───────▶ バックアップファイル
                                │                {basename}{timestamp}.{ext}
                                ▼
                         clear_old_backups()
                                │
                                ▼
                         古いバックアップ削除
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| logger.cpp | `core/io/logger.cpp` | ソース | ロガー実装の主コード |
| logger.h | `core/io/logger.h` | ヘッダー | Logger基底クラスと派生クラス宣言 |
| file_access.h | `core/io/file_access.h` | ヘッダー | ファイル書き込みAPI |
| dir_access.h | `core/io/dir_access.h` | ヘッダー | ディレクトリ操作API |
| time.h | `core/os/time.h` | ヘッダー | タイムスタンプ取得 |
| regex.h | `modules/regex/regex.h` | ヘッダー | ANSIエスケープ除去用正規表現 |
| core_globals.h | `core/core_globals.h` | ヘッダー | print_line_enabled / print_error_enabled |
| script_backtrace.h | `core/object/script_backtrace.h` | ヘッダー | スクリプトバックトレース |
