# 通知設計書 34-パッケージコールバックエラー

## 概要

本ドキュメントは、Julia のパッケージロードシステムにおいて、パッケージコールバック関数の実行中にエラーが発生した場合に出力されるエラー通知の設計を記述する。

### 本通知の処理概要

本通知は、パッケージがロードされた後に実行される `package_callbacks` 配列内のコールバック関数がエラーをスローした場合に、エラーレベルのログを出力する処理である。

**業務上の目的・背景**：Julia のパッケージロードシステムでは、パッケージがロードされた後に外部ライブラリやフレームワーク（例：Revise.jl、Distributed など）が独自のコールバック処理を登録できる。これらのコールバックがエラーを起こしても、パッケージのロード自体は継続させたい。本通知は、コールバックのエラーを記録しつつ、パッケージロードの安定性を確保するために設けられている。

**通知の送信タイミング**：`run_package_callbacks` 関数内で `package_callbacks` 配列のいずれかのコールバックが例外をスローした時点で送信される。具体的には `base/loading.jl` の1627行目で発火する。

**通知の受信者**：Julia プロセスの標準エラー出力 (stderr) を監視する開発者・運用者。ログレベルは `Error`。

**通知内容の概要**：`"Error during package callback"` というメッセージと共に、キャッチされた例外スタックが `exception` キーワード引数として出力される。

**期待されるアクション**：受信者はコールバックを登録したパッケージ（例：Revise.jl）の問題を調査する。コールバックエラーはパッケージロードを阻害しないため、即座のアクションは必須ではないが、機能の欠落（例：コード変更の自動追跡が動作しない）が発生している可能性がある。

## 通知種別

ログ（Error） -- Julia の標準ログシステム（`@error` マクロ）による stderr へのエラーレベル出力

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（`@error` マクロによる即座のログ出力） |
| 優先度 | 高 |
| リトライ | 無し |

### 送信先決定ロジック

Julia の標準ログシステムにより、現在アクティブなログハンドラに送信される。

## 通知テンプレート

### メール通知の場合

該当なし（本通知はログ出力であり、メール送信は行わない）

### 本文テンプレート

```
Error during package callback
  exception = {例外スタック}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| exception | キャッチされた例外スタック | `current_exceptions()` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| パッケージロード | `package_callbacks` 内のコールバック実行中にエラー発生 | いずれかのコールバックが例外をスロー | `run_package_callbacks` 関数内の `catch` ブロックで検出 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| コールバックが正常終了 | すべてのコールバックが例外をスローしなければ本通知は発生しない |
| `package_callbacks` が空 | 登録されたコールバックがなければ実行されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A["パッケージロード完了"] --> B["run_package_callbacks(modkey)"]
    B --> C["run_extension_callbacks(modkey)"]
    C --> D["require_lock を unlock"]
    D --> E["package_callbacks をイテレート"]
    E --> F{"コールバック実行成功か"}
    F -->|成功| G["次のコールバックへ"]
    F -->|失敗 catch| H["@error パッケージコールバックエラー"]
    H --> I["require_lock を再取得 (finally)"]
    G --> I
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（データベースは使用しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| コールバックエラー | `package_callbacks` 内の関数がエラーをスロー | @error 通知を発行し、パッケージロードは続行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 無し |
| リトライ間隔 | -- |
| リトライ対象エラー | -- |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

本通知は例外スタック情報を含む。スタックトレースにはファイルパスや行番号が含まれるが、通常のデバッグ情報であり、機密情報は含まれない。

## 備考

- `run_package_callbacks` は `require_lock` を一時的に解放してコールバックを実行する。これにより、コールバック内で他のパッケージをロードすることが可能になる。
- コールバックエラーが発生しても、`finally` ブロックで `require_lock` が再取得され、パッケージロードプロセスは続行される。
- `run_extension_callbacks` は `run_package_callbacks` の先頭で呼ばれるが、拡張機能のロードエラーは別の通知（No.35）で処理される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | loading.jl | `base/loading.jl` | `package_callbacks` 配列の定義と `PkgId` 型を理解する |

**読解のコツ**: `package_callbacks` は `Callable` の配列で、各要素は `PkgId` を引数に取るコールバック関数。Revise.jl などの外部パッケージがここに関数を登録する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | loading.jl | `base/loading.jl` | `run_package_callbacks` 関数（1616-1632行目）が本通知のエントリーポイント |

**主要処理フロー**:
1. **1617行目**: `run_extension_callbacks(modkey)` を先に呼び出し
2. **1618行目**: `assert_havelock(require_lock)` でロック保持を確認
3. **1619行目**: `unlock(require_lock)` でロックを一時解放
4. **1621-1622行目**: `package_callbacks` をイテレートし `invokelatest(callback, modkey)` を実行
5. **1624-1627行目**: `catch` ブロックで例外をキャッチし `@error` を発行
6. **1629行目**: `finally` で `lock(require_lock)` によりロック再取得

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

```
require(modkey) [base/loading.jl]
    |
    +-- _require_prelocked(modkey)
            |
            +-- run_package_callbacks(modkey) [行1616]
                    |
                    +-- run_extension_callbacks(modkey) [行1617]
                    +-- unlock(require_lock) [行1619]
                    +-- for callback in package_callbacks [行1621]
                    |       |
                    |       +-- invokelatest(callback, modkey) [行1622]
                    |               (ここでエラー発生時)
                    +-- @error "Error during package callback" [行1627]
                    +-- lock(require_lock) [行1629]
```

### データフロー図

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

modkey (PkgId) -------> run_package_callbacks()
                            |
                            +-- コールバック実行
                            |       |
                            |       +-- エラー発生 --> catch
                            |                           |
                            |                           +--> @error ログ --> stderr
                            |
                            +-- require_lock 再取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| loading.jl | `base/loading.jl` | ソース | パッケージロードシステム、`run_package_callbacks` 関数の定義 |
| logging.jl | `base/logging.jl` | ソース | `@error` マクロの定義 |
