# 機能設計書 40-ロギング

## 概要

本ドキュメントは、Julia Base ライブラリにおけるロギング機能の設計を記述する。`@debug` / `@info` / `@warn` / `@error` マクロと `ConsoleLogger` によるログ出力を提供する。

### 本機能の処理概要

本機能は、Julia の構造化ログフレームワークを実装する。ログレベルに基づくフィルタリング、ロガーの切り替え、キーバリューペアによる構造化メタデータ、スレッドセーフなログ出力を提供する。

**業務上の目的・背景**：ログ出力はプログラムのデバッグ、監視、監査において不可欠な機能である。Julia のロギングフレームワークは、マクロベースの遅延評価により、無効なログレベルのメッセージ構築コストをゼロにしつつ、構造化されたキーバリューペアを通じてリッチなログ情報を提供する。ScopedValue ベースのロガー管理により、タスクごとのロガー切り替えも実現している。

**機能の利用シーン**：デバッグ情報の出力（@debug）、情報メッセージの出力（@info）、警告の出力（@warn）、エラーの報告（@error）、カスタムロガーによるログの収集・フィルタリング、JULIA_DEBUG 環境変数によるデバッグログの有効化。

**主要な処理内容**：
1. `LogLevel` 構造体によるログレベルの定義（125-127行目）
2. 標準ログレベル定数: `BelowMinLevel`(-1000001), `Debug`(-1000), `Info`(0), `Warn`(1000), `Error`(2000), `AboveMaxLevel`(1000001)（144-174行目）
3. `AbstractLogger` 抽象型によるロガーインターフェース（30行目）
4. `shouldlog` / `handle_message` / `min_enabled_level` / `catch_exceptions` のインターフェース関数（41-72行目）
5. `@debug` / `@info` / `@warn` / `@error` / `@logmsg` マクロによるログ出力（275-278行目）
6. `NullLogger` による全メッセージ無効化（100行目）
7. `SimpleLogger` によるシンプルなテキストログ出力（679-729行目）
8. `ConsoleLogger` による整形されたコンソールログ出力（ConsoleLogger.jl 28-36行目）
9. `LogState` と `ScopedValue` によるタスクごとのロガー管理（528-542行目）
10. `with_logger(f, logger)` によるスコープ付きロガー切り替え（652-654行目）
11. `global_logger()` / `global_logger(logger)` によるグローバルロガーの取得・設定（626-632行目）
12. `disable_logging(level)` によるグローバルなログレベルフィルタリング（560-562行目）
13. `JULIA_DEBUG` 環境変数によるデバッグログの選択的有効化（564-611行目）

**関連システム・外部連携**：標準エラー出力（stderr）、環境変数（JULIA_DEBUG）。

**権限による制御**：該当なし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | REPL | 主画面 | ConsoleLogger によるコンソールへのログ出力 |

## 機能種別

構造化ロギングフレームワーク

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| level | LogLevel | Yes（@logmsg） | ログレベル | LogLevel に変換可能 |
| message | Any（式） | Yes | ログメッセージ（遅延評価） | string で文字列に変換可能 |
| key=value | Any | No | 構造化メタデータ（キーバリューペア） | - |
| _module | Module | No | オーバーライド: 発生元モジュール | - |
| _group | Symbol | No | オーバーライド: メッセージグループ | - |
| _id | Symbol | No | オーバーライド: ユニーク識別子 | - |
| _file | String | No | オーバーライド: ソースファイル | - |
| _line | Integer | No | オーバーライド: ソース行番号 | - |
| maxlog | Integer | No | 表示回数の上限 | - |
| exception | Exception / Tuple{Exception,Any} | No | 例外情報（@error で使用） | - |

### 入力データソース

ソースコード中の @debug/@info/@warn/@error マクロ呼び出し。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ログ出力 | テキスト | ロガーのストリームへの整形されたログメッセージ |

### 出力先

ロガーの stream フィールド（ConsoleLogger/SimpleLogger）。デフォルトは stderr。

## 処理フロー

### 処理シーケンス

```
1. @info "message" key=value [275-278行目でマクロ展開]
   └─ logmsg_code が展開コードを生成 [350-429行目]
       └─ _min_enabled_level の高速チェック [401行目]
       └─ current_logger_for_env で現在のロガーを取得 [341-347行目]
       └─ shouldlog でロガー固有のフィルタリング [409行目]
       └─ メッセージ式を評価 [386-394行目]
       └─ handle_message_nothrow でメッセージを処理 [431-441行目]
           └─ handle_message を呼び出し [434-436行目]

2. handle_message(ConsoleLogger, ...) [ConsoleLogger.jl 110-196行目]
   └─ maxlog による表示回数制限の処理 [113-121行目]
   └─ メッセージを行ごとに分割 [126-134行目]
   └─ key=value ペアの整形（showvalue） [141-158行目]
   └─ meta_formatter でプレフィックス・サフィックス・色を取得 [162行目]
   └─ ボックス描画文字（┌│└）で整形出力 [175-191行目]
   └─ ロックを取得して書き込み [194行目]

3. with_logger(f, logger) [652-654行目]
   └─ with_logstate(f, LogState(logger)) [653行目]
   └─ @with(CURRENT_LOGSTATE => logstate, f()) [542行目]
```

### フローチャート

```mermaid
flowchart TD
    A["@info message key=val"] --> B{_min_enabled_level チェック}
    B -->|レベル不足| C[nothing: ログ出力なし]
    B -->|レベル十分| D[current_logger_for_env]
    D --> E{logger === nothing?}
    E -->|Yes| C
    E -->|No| F[shouldlog logger, level, ...]
    F --> G{shouldlog == true?}
    G -->|No| C
    G -->|Yes| H[メッセージ式を評価]
    H --> I[handle_message_nothrow]
    I --> J[handle_message logger, level, msg, ...]
    J --> K[ストリームへ出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | 遅延評価 | メッセージ式は shouldlog が true の場合のみ評価される（350-429行目） | 全ログマクロ |
| BR-40-02 | 3段階フィルタリング | (1) _min_enabled_level グローバルチェック (2) current_logger_for_env (3) shouldlog（396-424行目） | 全ログマクロ |
| BR-40-03 | maxlog | message_limits Dict でログIDごとの表示回数を制限（SimpleLogger 698-705行目、ConsoleLogger 113-121行目） | maxlog 指定時 |
| BR-40-04 | 例外安全 | catch_exceptions が true なら、メッセージ構築中の例外をキャッチしてエラーログとして出力（497-514行目） | デフォルト |
| BR-40-05 | JULIA_DEBUG | 環境変数でモジュール/グループ単位のデバッグログ有効化、"!" プレフィックスで除外指定（564-611行目） | JULIA_DEBUG 設定時 |
| BR-40-06 | スレッドセーフ | SimpleLogger/ConsoleLogger は ReentrantLock で出力をロック（SimpleLogger 681行目、ConsoleLogger 30行目） | 全ログ出力 |
| BR-40-07 | ScopedValue ロガー | CURRENT_LOGSTATE は ScopedValue で管理され、with_logger でタスクスコープ付き切り替え（535行目） | with_logger |
| BR-40-08 | ログレベル順序 | BelowMinLevel < Debug < Info < Warn < Error < AboveMaxLevel（144-174行目） | 全体 |
| BR-40-09 | ユニークID生成 | log_record_id でモジュール名+レベル+メッセージのハッシュから8桁16進IDを生成（294-312行目） | マクロ展開時 |
| BR-40-10 | SimpleLogger フォールバック | ストリームが閉じている場合、Warn以上はstderr、未満はstdoutへ出力（722-726行目） | SimpleLogger |
| BR-40-11 | ConsoleLogger ボックス描画 | 単一行は "[ "、複数行は "┌ " "│ " "└ " で修飾（ConsoleLogger.jl 176-179行目） | ConsoleLogger |

### 計算ロジック

**LogLevel 算術**（134-135行目）:
- `LogLevel + Integer = LogLevel`（レベル加算）
- `LogLevel - Integer = LogLevel`（レベル減算）
- `isless(LogLevel, LogLevel)` による比較（131行目）

## データベース操作仕様

該当なし。

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | (キャッチ済み) | メッセージ式の評価中に例外発生 | catch_exceptions=true ならエラーログとして記録（497-514行目） |
| - | ErrorException | NullLogger の handle_message が呼ばれた | NullLogger を正しく使用（shouldlog が false を返す）（104-105行目） |
| - | (リスロー) | catch_exceptions=false のロガーでメッセージ構築失敗 | ロガーが例外をリスローする（501-502行目） |

### リトライ仕様

該当なし。ログ出力の失敗はリトライされない。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- マクロベースの遅延評価により、無効なログレベルのメッセージ構築コストはゼロ
- `_min_enabled_level` は `Threads.Atomic{Int32}` でグローバルな高速フィルタリングを実現（178行目）
- `@constprop :none` で不要なメソッドの無効化を防止し、コンパイル時の安定性を確保（77行目、341行目）
- `@invokelatest` により、ロガーメソッドの追加が既存コードの再コンパイルを引き起こさない（409行目、434行目）
- `issimple`/`issimplekw` による最適化パスで、単純なメッセージの場合は try/catch を回避（316-394行目）

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

- ログメッセージには任意のデータが含まれうるため、ログ出力先が信頼できることを確認する必要がある
- `JULIA_DEBUG` 環境変数は外部から設定可能であり、デバッグログの有効化がパフォーマンスに影響する可能性がある
- `catch_exceptions` を false に設定すると、メッセージ構築中の例外がプログラムをクラッシュさせる可能性がある

## 備考

- `CoreLogging` モジュールとして実装され、`Base.CoreLogging` として利用される
- デフォルトのグローバルロガーは `SimpleLogger()`（731行目）
- `ConsoleLogger` は `default_metafmt` でレベルに応じた色分け（Debug=青系、Info=青系、Warn=黄系、Error=赤系）を提供（ConsoleLogger.jl 64-69行目）
- `ConsoleLogger` の `meta_formatter` はカスタマイズ可能（ConsoleLogger.jl 17-19行目）
- `log_record_id` はコンパイル時に生成され、`_log_record_ids` Set で一意性を保証（287-312行目）
- `logmsg_shim` は C コードからのログ呼び出しを受け付ける（518-525行目）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | logging.jl | `base/logging/logging.jl` 30行目 | `AbstractLogger` 抽象型: ロガーインターフェースの基底型 |
| 1-2 | logging.jl | `base/logging/logging.jl` 125-127行目 | `LogLevel` 構造体: `level::Int32` フィールドのみ |
| 1-3 | logging.jl | `base/logging/logging.jl` 144-174行目 | 標準ログレベル定数: BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel |
| 1-4 | logging.jl | `base/logging/logging.jl` 100行目 | `NullLogger`: shouldlog が常に false を返すロガー |
| 1-5 | logging.jl | `base/logging/logging.jl` 528-535行目 | `LogState` 構造体と `CURRENT_LOGSTATE` ScopedValue |
| 1-6 | logging.jl | `base/logging/logging.jl` 679-684行目 | `SimpleLogger` 構造体: stream, lock, min_level, message_limits |
| 1-7 | ConsoleLogger.jl | `base/logging/ConsoleLogger.jl` 28-36行目 | `ConsoleLogger` 構造体: stream, lock, min_level, meta_formatter, show_limited, right_justify, message_limits |

**読解のコツ**: ロギングフレームワークは3つの概念層がある。(1) AbstractLogger インターフェース（shouldlog, handle_message, min_enabled_level, catch_exceptions）、(2) ログマクロのコード生成（logmsg_code）、(3) ロガー実装（SimpleLogger, ConsoleLogger）。まず(1)を理解してから(2)(3)を読むのが効率的。

#### Step 2: ログマクロの展開を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | logging.jl | `base/logging/logging.jl` 275-278行目 | @debug/@info/@warn/@error マクロ定義: logmsg_code を呼び出す |
| 2-2 | logging.jl | `base/logging/logging.jl` 350-429行目 | `logmsg_code`: マクロ展開のコード生成。3段階フィルタリング + メッセージ評価 + handle_message |
| 2-3 | logging.jl | `base/logging/logging.jl` 443-484行目 | `process_logmsg_exs`: key=value ペアの解析、特殊キーワード（_module, _group, _id, _file, _line）の処理 |
| 2-4 | logging.jl | `base/logging/logging.jl` 294-312行目 | `log_record_id`: コンパイル時のユニークID生成 |
| 2-5 | logging.jl | `base/logging/logging.jl` 316-338行目 | `issimple`/`issimplekw`: 最適化パスの判定 |

#### Step 3: ロガー実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | logging.jl | `base/logging/logging.jl` 695-729行目 | `SimpleLogger` の handle_message: ボックス描画文字で整形出力 |
| 3-2 | ConsoleLogger.jl | `base/logging/ConsoleLogger.jl` 71-87行目 | `default_metafmt`: レベルに応じた色・プレフィックス・サフィックスの生成 |
| 3-3 | ConsoleLogger.jl | `base/logging/ConsoleLogger.jl` 110-196行目 | `ConsoleLogger` の handle_message: メッセージ分割、key=value整形、ボックス描画、右寄せ |

#### Step 4: ロガー管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | logging.jl | `base/logging/logging.jl` 537-542行目 | `current_logstate`: ScopedValue から LogState を取得、フォールバックは _global_logstate |
| 4-2 | logging.jl | `base/logging/logging.jl` 652-654行目 | `with_logger`: ScopedValue を使ったスコープ付きロガー切り替え |
| 4-3 | logging.jl | `base/logging/logging.jl` 626-632行目 | `global_logger`: グローバルロガーの取得・設定 |
| 4-4 | logging.jl | `base/logging/logging.jl` 560-562行目 | `disable_logging`: _min_enabled_level の設定 |
| 4-5 | logging.jl | `base/logging/logging.jl` 564-611行目 | `env_override_minlevel`: JULIA_DEBUG 環境変数の解析 |

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

```
@info "message" key=val                 [276行目]
    |
    +-- logmsg_code(module, file, line, :Info, message, exs...)  [350行目]
          |
          +-- [展開されたコード:]
                |
                +-- std_level >= _min_enabled_level[]?           [401行目]
                |     |
                |     +-- No: nothing (早期終了)
                |     +-- Yes: 続行
                |
                +-- current_logger_for_env(std_level, group, _module)  [341行目]
                |     |
                |     +-- current_logstate()                     [537行目]
                |     |     |
                |     |     +-- ScopedValues.get(CURRENT_LOGSTATE)  [538行目]
                |     |     +-- フォールバック: _global_logstate   [539行目]
                |     |
                |     +-- std_level >= logstate.min_enabled_level?  [343行目]
                |     +-- env_override_minlevel(group, _module)?    [343行目]
                |
                +-- shouldlog(logger, level, _module, group, id)  [409行目]
                |
                +-- [メッセージ式の評価]                           [386-394行目]
                |
                +-- handle_message_nothrow(logger, level, msg, ...)  [431行目]
                      |
                      +-- handle_message(logger, level, msg, ...)   [434行目]
                            |
                            +-- (SimpleLogger版)                    [695行目]
                            |     |
                            |     +-- maxlog チェック               [698-705行目]
                            |     +-- IOBuffer + IOContext で整形    [706-720行目]
                            |     +-- @lock write(stream, b)        [722-727行目]
                            |
                            +-- (ConsoleLogger版)                   [110行目]
                                  |
                                  +-- maxlog チェック               [113-121行目]
                                  +-- メッセージ行分割              [126-134行目]
                                  +-- key=value 整形               [141-158行目]
                                  +-- meta_formatter               [162行目]
                                  +-- ボックス描画 + printstyled    [175-191行目]
                                  +-- @lock write(stream, b)       [194行目]

with_logger(f, logger)                  [652行目]
    |
    +-- with_logstate(f, LogState(logger))  [653行目]
          |
          +-- @with(CURRENT_LOGSTATE => logstate, f())  [542行目]

disable_logging(level)                  [560行目]
    |
    +-- _min_enabled_level[] = level + 1  [561行目]
```

### データフロー図

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

@info "msg" k=v -----------> logmsg_code マクロ展開 ----------> 展開コード
                                  |
                                  v
                             _min_enabled_level[]
                             (Atomic{Int32}) -------> 高速フィルタ (pass/reject)
                                  |
                                  v
                             current_logstate()
                             (ScopedValue) ----------> LogState{logger, min_level}
                                  |
                                  v
                             shouldlog(logger, ...) -> Bool (pass/reject)
                                  |
                                  v
                             メッセージ式の評価
                             msg = string("msg")
                             kwargs = (k=v,) -------> msg::String, kwargs::NamedTuple
                                  |
                                  v
                             handle_message(logger, ...)
                                  |
                                  +---> SimpleLogger
                                  |      IOBuffer → "┌ Info: msg\n│   k = v\n└ @ Module file:line"
                                  |                                 → stream (stderr)
                                  |
                                  +---> ConsoleLogger
                                         IOBuffer → printstyled で色付き整形
                                         meta_formatter で色・接頭辞・接尾辞
                                                                    → stream (stderr)

JULIA_DEBUG=module --------> env_override_minlevel -> Bool (Debug有効化)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| logging.jl | `base/logging/logging.jl` | ソース | CoreLogging モジュール本体: AbstractLogger, LogLevel, ログマクロ, SimpleLogger, LogState, with_logger, global_logger, disable_logging, JULIA_DEBUG 処理 |
| ConsoleLogger.jl | `base/logging/ConsoleLogger.jl` | ソース | ConsoleLogger 構造体と handle_message 実装: ボックス描画、色付き出力、meta_formatter |
