Files
hakorune/docs/development/current/main/hako_logging_design.md
nyash-codex 024a09c15b feat(core): Phase 122 ConsoleBox.println / log unification
## Phase 122: ConsoleBox.println / log の統一 (完了)

### 概要
ConsoleBox.printlnをlogのエイリアスとしてVM/TypeRegistryレベルで統一。
Phase 120で発見されたesc_dirname_smoke.hakoの「Unknown method 'println'」
エラーを完全解消。

### 完了タスク
-  TypeRegistry修正: printlnをlogのエイリアス(slot 400)として追加
-  ConsoleBox実装: printlnメソッドのラッパ追加
-  VM Method Dispatch: ConsoleBox専用ハンドラ追加
-  Plugin設定: nyash.tomlにprintln (method_id 2)追加
-  ドキュメント更新: 3ファイル(hako_logging/logging_policy/core_boxes)

### 実装詳細

#### 1. TypeRegistry (src/runtime/type_registry.rs)
```rust
const CONSOLE_METHODS: &[MethodEntry] = &[
    MethodEntry { name: "log",     arity: 1, slot: 400 },
    MethodEntry { name: "warn",    arity: 1, slot: 401 },
    MethodEntry { name: "error",   arity: 1, slot: 402 },
    MethodEntry { name: "clear",   arity: 0, slot: 403 },
    // Phase 122: println は log のエイリアス
    MethodEntry { name: "println", arity: 1, slot: 400 },
];
```

#### 2. ConsoleBox (src/boxes/console_box.rs)
- WASM/非WASM両方にprintln()メソッド追加
- 内部的にlog()に委譲

#### 3. VM Method Dispatch (src/backend/mir_interpreter/handlers/calls/method.rs)
- ConsoleBox専用ハンドラ追加
- log/println/warn/error/clearをサポート
- args配列の正しい処理(len > 1時はargs[1]を使用)

#### 4. Plugin設定 (nyash.toml)
- ConsoleBox.methodsにprintln追加(method_id=2)
- method_id整合性: log=1, println=2

### テスト結果
-  esc_dirname_smoke.hako実行成功
  - 旧エラー: "Unknown method 'println'" ← **完全解消**
  - 新出力: `[Console LOG] dir1/dir2`

### Phase 120問題解決
**Phase 120ベースラインでの課題**:
-  esc_dirname_smoke.hako: ConsoleBox.println未実装

**Phase 122での解決**:
-  TypeRegistryレベルでprintln→log正規化
-  全経路(JSON v0/selfhost/通常VM)で一貫性保証

### ファイル構成
**新規作成**:
- docs/development/current/main/phase122_consolebox_println_unification.md

**修正**:
- src/runtime/type_registry.rs (+5行)
- src/boxes/console_box.rs (+14行, WASM/非WASM両対応)
- src/backend/mir_interpreter/handlers/calls/method.rs (+60行)
- nyash.toml (+1メソッド定義)
- docs/development/current/main/hako_logging_design.md (+40行)
- docs/development/current/main/logging_policy.md (+30行)
- docs/development/current/main/core_boxes_design.md (+20行)

### 技術的成果
- **Alias First原則**: VM/TypeRegistryレベルで正規化
- **Phase 120連携**: ベースライン確立→問題発見→根本解決
- **全経路統一**: selfhost/JSON v0/通常VMすべてで動作

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 05:16:06 +09:00

10 KiB
Raw Blame History

Phase 104: .hako側ロギング設計ガイド

概要

Nyash(.hako)アプリケーションからのロギングを体系的に設計し、Ring0.logRust内部 との役割分担を明確にする。

Architecture: 3層ロギングの完全図

┌─────────────────────────────────────────────────────────┐
│  ユーザーアプリケーション (.hako)                        │
│                                                          │
│  box MyApp {                                            │
│    main() {                                             │
│      me.console.println("Result: OK")  ← ConsoleBox     │
│    }                                                    │
│  }                                                      │
└──────────────────┬──────────────────────────────────────┘
                   │
              ConsoleService
              (user-facing)
                   │
┌──────────────────▼──────────────────────────────────────┐
│  Rust Runtime (Ring0.log)                               │
│                                                          │
│  ring0.log.debug("[joinir] Processing...")      ← internal
│  ring0.log.error("VM error: {}")                ← internal
└──────────────────┬──────────────────────────────────────┘
                   │
                stderr/stdout
                   │
           ┌───────▼────────┐
           │  Terminal      │
           │  (user sees)   │
           └────────────────┘

ロギングの4つのカテゴリ

1. User-Facing (ユーザー向け)

属性: 常に表示(本番環境対応) 出力先: ConsoleBox → stdout 制御: フラグなし(常時有効)

:

box FileProcessor {
    console: ConsoleBox

    process(filename) {
        me.console = new ConsoleBox()

        if file_not_found(filename) {
            me.console.println("❌ File not found: " + filename)
            return -1
        }

        me.console.println("✅ Processing: " + filename)
        return 0
    }
}

2. Dev-Debug (開発用デバッグ)

属性: 開発時のみ(本番環境では削除推奨) 出力先: println!(局所的) 制御: bool flag環境変数or定数

:

box MathBox {
    DEBUG: BoolBox = false  // 環境変数から設定

    calculate(a: Integer, b: Integer) {
        if DEBUG {
            print("[DEBUG] a=" + a + ", b=" + b)
        }
        return a + b
    }
}

3. Monitoring (運用監視)

属性: 定期レポート(本番環境で有効) 出力先: ConsoleBox → stdout 制御: 定期的(タイマーベース)

:

box ServiceMonitor {
    console: ConsoleBox
    requests_count: IntegerBox

    tick() {
        me.console = new ConsoleBox()
        me.console.println("[PERF] Requests/sec: " + me.requests_count)
        me.requests_count = 0
    }
}

4. Internal Rust (内部実装)

属性: Rust実装の内部ログ 出力先: Ring0.log → stderr 制御: NYASH_RING0_LOG_LEVEL

(.hako側には関係ない、参考):

// src/mir/builder.rs
ring0.log.debug("[mir] Building function...");

ロギングBox設計パターン

パターン 1: Lightweight Logger

用途: 単純なユースケース(メッセージのみ)

box SimpleLogger {
    console: ConsoleBox

    birth() {
        me.console = new ConsoleBox()
    }

    log(msg) {
        me.console.println(msg)
    }
}

// 使用例
box MyApp {
    main() {
        local logger = new SimpleLogger()
        logger.log("Processing...")
        logger.log("✅ Done")
    }
}

パターン 2: Structured Logger

用途: レベル分けERROR/WARN/INFO

box StructuredLogger {
    console: ConsoleBox

    birth() {
        me.console = new ConsoleBox()
    }

    error(msg) {
        me.console.println("[ERROR] " + msg)
    }

    warn(msg) {
        me.console.println("[WARN] " + msg)
    }

    info(msg) {
        me.console.println("[INFO] " + msg)
    }
}

// 使用例
box DatabaseConnector {
    logger: StructuredLogger

    birth() {
        me.logger = new StructuredLogger()
    }

    connect(host) {
        if network_unavailable() {
            me.logger.error("Network unavailable")
            return false
        }

        me.logger.info("Connected to: " + host)
        return true
    }
}

パターン 3: Contextual Logger

用途: コンテキスト情報を含むログ

box ContextualLogger {
    console: ConsoleBox
    context: StringBox  // operation_id等

    birth(ctx) {
        me.console = new ConsoleBox()
        me.context = ctx
    }

    log(msg) {
        me.console.println("[" + me.context + "] " + msg)
    }
}

// 使用例
box RequestHandler {
    logger: ContextualLogger

    birth(request_id) {
        me.logger = new ContextualLogger(request_id)
    }

    process() {
        me.logger.log("Request started")
        // ... 処理 ...
        me.logger.log("Request completed")
    }
}

ベストプラクティス

DO (推奨)

  1. 一元化: ロギングBoxを1つ所有

    box MyApp {
        logger: LoggerBox  // ← 一箇所だけ
        main() { ... }
    }
    
  2. 構造化: メッセージは構造化

    logger.log("[OPERATION] " + op_name + " status=" + status)
    
  3. 条件付き: デバッグログにフラグ

    if debug_enabled {
        logger.debug("var=" + var)
    }
    

DON'T (回避)

  1. 散乱: 複数の ConsoleBox 初期化

    // ❌ ダメ
    box MyApp {
        main() {
            local c1 = new ConsoleBox()
            local c2 = new ConsoleBox()  // 重複
        }
    }
    
  2. 混在: データ処理ロジックとログ混在

    // ❌ ダメ
    box Processor {
        process(x) {
            print(x)  // ← ロジックに埋め込み
            return x + 1
        }
    }
    
  3. 過剰: 全行でログ出力

    // ❌ ダメ
    calculate(a, b) {
        print("enter")
        local x = a + b
        print("x=" + x)
        print("exit")
        return x
    }
    

ロギング制御フロー

┌─────────────────┐
│ .hako app start │
└────────┬────────┘
         │
    ┌────▼──────────────────────────┐
    │ Check NYASH_RING0_LOG_LEVEL env │
    └────┬──────────────────────────┘
         │
    ┌────▼────────────┐      ┌──────────────────┐
    │ DEBUG level?    │─Y─>  │ Enable full logs │
    └────┬────────────┘      └──────────────────┘
         │
         N
    ┌────▼────────────┐      ┌──────────────────┐
    │ INFO level?     │─Y─>  │ Error/Warn only  │
    └────┬────────────┘      └──────────────────┘
         │
         N  (default)
    ┌────▼──────────────┐
    │ User-facing only  │
    │ (ConsoleBox)      │
    └───────────────────┘

チェックリスト: ロギング設計確認

実装時には以下をチェック:

  • ConsoleBox初期化は1回のみ
  • ユーザーメッセージは全て ConsoleBox経由
  • デバッグログに "[DEBUG]" prefix
  • エラーメッセージに "" 絵文字
  • 成功メッセージに "" 絵文字
  • ロギングLogicを別Boxに分離した
  • テストコード内の println! は許容
  • 本番環境での不要なログを削除した

Phase 105: Logger Box Framework Integration

Phase 105 で Logger Box フレームワークが導入され、以下のパターンが正式化されました:

  • LightweightLoggerBox: メッセージのみの単純ロギング
  • StructuredLoggerBox: DEBUG/INFO/WARN/ERROR レベル付きロギング
  • ContextualLoggerBox: contextrequest ID等付きロギング

詳細な設計と実装例は Logger Box Design を参照してください。

Integration with Phase 104 Categories

Logger Box は以下のカテゴリに対応:

Phase 104 Category Logger Box Pattern Use Case
user-facing Direct ConsoleBox (no Logger Box needed) 単純なユーザーメッセージ
dev-debug StructuredLogger (DEBUG level) デバッグ出力
monitoring StructuredLogger (INFO level) 監視情報
internal Rust Ring0.log Rust側の内部ログ

ConsoleBox の使い方Phase 122 更新)

基本パターン

local console = new ConsoleBox()
console.println("Hello")  // 内部的には log と同じスロット
console.log("World")      // println と同じ動作

ConsoleBox vs LoggerBox vs ConsoleService

  • ConsoleBox: ユーザーコードで直接使用(println / log
  • LoggerBox: 構造化ログ・ログレベル管理
  • ConsoleService: CLI/システム内部での出力Ring0 経由)

Phase 122 での統一

  • ConsoleBox.printlnConsoleBox.log の完全なエイリアス
  • VM の TypeRegistry で slot 400 に正規化される
  • すべての経路JSON v0 / selfhost / 通常VMで一貫性を保つ

関連ドキュメント