# 機能設計書: logging - ログ出力機能

## 1. 機能概要

### 1.1 目的
loggingモジュールは、Nimアプリケーションにシンプルで効率的なログ出力機能を提供する。コンソール、ファイル、ローリングファイルへのログ出力をサポートし、ログレベルによるフィルタリング機能を備える。

### 1.2 機能範囲
- 複数のログレベル（Debug, Info, Notice, Warn, Error, Fatal）
- 複数のロガータイプ（ConsoleLogger, FileLogger, RollingFileLogger）
- ハンドラベースのログ管理
- カスタマイズ可能なフォーマット文字列
- スレッドローカルなログフィルタ

### 1.3 関連コンポーネント
- `strutils` モジュール: 文字列操作
- `times` モジュール: 日時フォーマット
- `os` モジュール: ファイル操作（非JSバックエンド）

## 2. データ構造

### 2.1 型定義

```nim
type
  Level* = enum  ## ログレベル列挙型
    lvlAll,      ## すべてのレベルを有効
    lvlDebug,    ## デバッグレベル以上
    lvlInfo,     ## 情報レベル以上
    lvlNotice,   ## 通知レベル以上
    lvlWarn,     ## 警告レベル以上
    lvlError,    ## エラーレベル以上
    lvlFatal,    ## 致命的エラーレベル以上
    lvlNone      ## ログ無効

  Logger* = ref object of RootObj  ## ロガー基底型
    levelThreshold*: Level   ## しきい値レベル
    fmtStr*: string          ## フォーマット文字列

  ConsoleLogger* = ref object of Logger
    useStderr*: bool         ## stderr使用フラグ
    flushThreshold*: Level   ## フラッシュしきい値

  FileLogger* = ref object of Logger  ## ※非JSバックエンド
    file*: File              ## ファイルハンドル
    flushThreshold*: Level

  RollingFileLogger* = ref object of FileLogger  ## ※非JSバックエンド
    maxLines: int            ## 最大行数
    curLine: int             ## 現在行数
    baseName: string         ## 基本ファイル名
    baseMode: FileMode       ## ファイルモード
    logFiles: int            ## ログファイル数
    bufSize: int             ## バッファサイズ
```

### 2.2 定数

| 定数名 | 値 | 説明 |
|--------|------|------|
| `LevelNames` | `["DEBUG", ...]` | レベル名の配列 |
| `defaultFmtStr` | `"$levelname "` | デフォルトフォーマット |
| `verboseFmtStr` | `"$levelid, [$datetime] -- $appname: "` | 詳細フォーマット |
| `defaultFlushThreshold` | `lvlError` or `lvlAll` | デフォルトフラッシュしきい値 |

## 3. 主要機能

### 3.1 ロガー作成

#### newConsoleLogger
```nim
proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr,
    useStderr = false, flushThreshold = defaultFlushThreshold): ConsoleLogger
```
- **目的**: コンソール出力ロガーを作成
- **パラメータ**:
  - `levelThreshold`: ログ出力しきい値
  - `fmtStr`: フォーマット文字列
  - `useStderr`: stderrへの出力フラグ
  - `flushThreshold`: 即時フラッシュしきい値
- **ソース位置**: `lib/pure/logging.nim:405-432`

#### newFileLogger
```nim
proc newFileLogger*(filename = defaultFilename(), mode: FileMode = fmAppend,
                    levelThreshold = lvlAll, fmtStr = defaultFmtStr,
                    bufSize: int = -1, flushThreshold = defaultFlushThreshold): FileLogger
```
- **目的**: ファイル出力ロガーを作成
- **ソース位置**: `lib/pure/logging.nim:507-537`

#### newRollingFileLogger
```nim
proc newRollingFileLogger*(filename = defaultFilename(), mode: FileMode = fmReadWrite,
                          levelThreshold = lvlAll, fmtStr = defaultFmtStr,
                          maxLines: Positive = 1000, bufSize: int = -1,
                          flushThreshold = defaultFlushThreshold): RollingFileLogger
```
- **目的**: ローテーション付きファイルロガーを作成
- **特徴**: 指定行数に達すると新しいファイルにローテーション
- **ソース位置**: `lib/pure/logging.nim:565-614`

### 3.2 ログ出力

#### log メソッド
```nim
method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`])
method log*(logger: FileLogger, level: Level, args: varargs[string, `$`])
method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`])
```
- **目的**: 指定レベルでメッセージをログ出力
- **動作**: `levelThreshold`とグローバルフィルタの両方をチェック
- **ソース位置**: `lib/pure/logging.nim:358-404, 435-467, 623-664`

#### ログテンプレート
```nim
template log*(level: Level, args: varargs[string, `$`])
template debug*(args: varargs[string, `$`])
template info*(args: varargs[string, `$`])
template notice*(args: varargs[string, `$`])
template warn*(args: varargs[string, `$`])
template error*(args: varargs[string, `$`])
template fatal*(args: varargs[string, `$`])
```
- **目的**: 登録されたすべてのハンドラにログ出力
- **ソース位置**: `lib/pure/logging.nim:673-833`

### 3.3 ハンドラ管理

| プロシージャ | 説明 | ソース位置 |
|-------------|------|-----------|
| `addHandler` | ハンドラを登録 | 835-849 |
| `removeHandler` | ハンドラを削除 | 851-859 |
| `getHandlers` | 登録済みハンドラ一覧を取得 | 861-866 |
| `setLogFilter` | グローバルログフィルタを設定 | 868-885 |
| `getLogFilter` | グローバルログフィルタを取得 | 887-892 |

### 3.4 フォーマット文字列

#### substituteLog
```nim
proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string
```
- **目的**: フォーマット文字列の変数を置換
- **サポートされる変数**:

| 変数 | 説明 |
|------|------|
| `$date` | 現在の日付 |
| `$time` | 現在の時刻 |
| `$datetime` | 日付と時刻 |
| `$app` | アプリケーションパス |
| `$appname` | アプリケーション名 |
| `$appdir` | アプリケーションディレクトリ |
| `$levelid` | レベルの1文字 |
| `$levelname` | レベル名 |

- **ソース位置**: `lib/pure/logging.nim:287-340`

## 4. 動作フロー

### 4.1 ログ出力フロー

```
1. log テンプレート呼び出し
2. グローバルフィルタチェック (level >= logging.level)
3. logLoop でハンドラをイテレート
4. 各ハンドラの levelThreshold チェック
5. substituteLog でフォーマット置換
6. 出力先に書き込み
7. flushThreshold に応じてフラッシュ
```

### 4.2 RollingFileLoggerのローテーション

```
1. curLine >= maxLines を判定
2. 現在のファイルをクローズ
3. rotate() で既存ファイルをリネーム
   - file.log -> file.log.1
   - file.log.1 -> file.log.2
4. 新しいファイルをオープン
5. curLine をリセット
```

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

### 5.1 推奨読解順序

1. **型定義** (157-281行目)
   - `Level`列挙型、`Logger`基底型、各ロガー型の理解

2. **フォーマット処理** (287-340行目)
   - `substituteLog`プロシージャの変数置換ロジック

3. **ロガー作成** (405-614行目)
   - `newConsoleLogger`, `newFileLogger`, `newRollingFileLogger`

4. **ログ出力メソッド** (342-664行目)
   - 各ロガーの`log`メソッド実装

5. **テンプレートとハンドラ管理** (668-892行目)
   - グローバルハンドラ管理とテンプレート定義

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

```
info("message")  [725行目]
└── log(lvlInfo, args) [673行目]
    └── logLoop(level, args) [668行目]
        └── for logger in handlers:
            └── log(logger, level, args)  [358行目以降]
                ├── substituteLog(fmtStr, level, args)
                └── writeLine(handle/file, ln)
                    └── flushFile (if level >= flushThreshold)
```

### 5.3 データフロー図

```
[アプリケーション]
      │
      ▼ log(lvlInfo, "message")
┌─────────────────────────────────────┐
│          logging モジュール           │
│                                      │
│  ┌────────────────────────────────┐  │
│  │ グローバルフィルタ (level変数)    │  │
│  └───────────┬────────────────────┘  │
│              ▼                       │
│  ┌────────────────────────────────┐  │
│  │ handlers リスト (スレッドローカル) │  │
│  │  ├── ConsoleLogger             │  │
│  │  ├── FileLogger                │  │
│  │  └── RollingFileLogger         │  │
│  └───────────┬────────────────────┘  │
│              │                       │
│              ▼                       │
│  ┌────────────────────────────────┐  │
│  │ substituteLog (フォーマット処理)  │  │
│  │  $date, $time, $levelname...   │  │
│  └───────────┬────────────────────┘  │
└──────────────┼───────────────────────┘
               ▼
    [stdout/stderr/file]
```

### 5.4 読解のコツ

- **スレッドローカル変数**: `level`と`handlers`は`{.threadvar.}`で定義されており、マルチスレッド環境では各スレッドで個別に設定が必要
- **JavaScriptバックエンド**: `when not defined(js)`ブロックで分岐。JSでは`FileLogger`/`RollingFileLogger`は利用不可
- **メソッドディスパッチ**: `Logger`基底型の`log`メソッドは空実装。サブタイプでオーバーライド

## 6. 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| `lib/pure/logging.nim` | ソース | メイン実装 |
| `lib/pure/strutils.nim` | ソース | 文字列操作 |
| `lib/pure/times.nim` | ソース | 日時フォーマット |
| `lib/pure/os.nim` | ソース | ファイル操作 |

## 7. 設計上の考慮事項

### 7.1 パフォーマンス
- テンプレートによる条件付きコンパイル
- グローバルフィルタによる早期リターン
- フラッシュしきい値によるI/O最適化

### 7.2 拡張性
- `Logger`基底型を継承したカスタムロガーの作成が可能
- `log`メソッドをオーバーライドして独自の出力先を実装

### 7.3 マルチスレッド対応
- ハンドラリストとログフィルタはスレッドローカル
- 各スレッドで個別にハンドラ登録が必要
- ファイル書き込みの排他制御は実装されていない
