# 機能設計書 110-Instrumentation

## 概要

本ドキュメントは、Next.jsのInstrumentation（計装）機能の設計を記述する。サーバーサイドの計測フックとして、monitoring、logging、tracing用の初期化コードを実行し、リクエストエラー時のカスタムハンドリングを提供する仕組みである。

### 本機能の処理概要

`instrumentation.ts`（または`instrumentation.js`）ファイルで定義された`register()`関数と`onRequestError()`コールバックを通じて、サーバー起動時の初期化処理とリクエストエラー時のカスタム処理を実行する機能である。OpenTelemetryやSentry等の監視ツールとの統合に使用される。

**業務上の目的・背景**：本番環境でのアプリケーション監視、エラートラッキング、パフォーマンス分析のために、サーバー起動時に監視ツールのSDKを初期化し、リクエストエラー発生時にカスタムレポートを行う必要がある。Instrumentation機能はこれらの初期化・フック処理を一元化するAPIを提供する。

**機能の利用シーン**：プロジェクトルートに`instrumentation.ts`ファイルを配置して使用する。`register()`関数はサーバー起動時に1回だけ呼び出され、OpenTelemetry SDKの初期化やカスタムメトリクスの設定等を行う。`onRequestError()`はリクエスト処理中にエラーが発生した際に呼び出され、エラーレポートサービスへの送信等を行う。

**主要な処理内容**：
1. `instrumentation.ts`ファイルのビルド・バンドル
2. サーバー起動時の`register()`関数の呼び出し（1回のみ）
3. 登録後の拡張処理（`extendInstrumentationAfterRegistration`）
4. リクエストエラー時の`onRequestError()`コールバック呼び出し
5. 再検証理由（on-demand/stale）の判定ユーティリティ

**関連システム・外部連携**：OpenTelemetry、Sentry、Datadog等の監視ツールSDKとの統合に使用される。

**権限による制御**：特に権限制御はない。instrumentation.tsファイルの存在により機能が有効化される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | バックエンド処理専用。画面との直接的な関連はない |

## 機能種別

計測・監視 / サーバーサイド初期化

## 入力仕様

### 入力パラメータ（register）

パラメータなし。register()関数は引数を受け取らない。

### 入力パラメータ（onRequestError）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| error | unknown | Yes | 発生したエラーオブジェクト | - |
| errorRequest | Readonly\<{path, method, headers}\> | Yes | エラー発生時のリクエスト情報 | - |
| errorContext | Readonly\<RequestErrorContext\> | Yes | エラーコンテキスト（ルーター種別、ルートパス等） | - |

### RequestErrorContext型

| フィールド | 型 | 説明 |
|-----------|-----|------|
| routerKind | 'Pages Router' \| 'App Router' | ルーター種別 |
| routePath | string | ルートファイルパス（例: /app/blog/[dynamic]） |
| routeType | 'render' \| 'route' \| 'action' \| 'proxy' | ルートの種別 |
| renderSource | string \| undefined | レンダリングソース（RSC、SSR等） |
| revalidateReason | 'on-demand' \| 'stale' \| undefined | 再検証の理由 |

### 入力データソース

- `instrumentation.ts`ファイル（プロジェクトルート）
- ビルド出力（`server/instrumentation.js`）
- リクエスト処理中のエラー情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| register戻り値 | void | 初期化処理は戻り値なし |
| onRequestError戻り値 | void \| Promise\<void\> | エラーハンドリング処理（非同期可） |

### 出力先

ユーザー定義のinstrumentation.ts内の処理に依存（外部監視サービスへの送信等）

## 処理フロー

### 処理シーケンス

```
1. サーバー起動時
   └─ ensureInstrumentationRegistered(projectDir, distDir)が呼び出される
2. Instrumentationモジュールの読み込み
   └─ getInstrumentationModule()でserver/instrumentation.jsをrequire
3. register()の実行
   └─ ユーザー定義のregister関数を呼び出し
4. 登録後拡張処理
   └─ extendInstrumentationAfterRegistration()
5. リクエストエラー発生時
   └─ instrumentationOnRequestError()が呼び出される
6. onRequestError()の実行
   └─ ユーザー定義のonRequestErrorコールバックを呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[サーバー起動] --> B[ensureInstrumentationRegistered]
    B --> C{既に登録済み?}
    C -->|Yes| D[スキップ]
    C -->|No| E[getInstrumentationModule]
    E --> F{モジュール存在?}
    F -->|No| G[スキップ]
    F -->|Yes| H{register関数存在?}
    H -->|No| G
    H -->|Yes| I[register実行]
    I --> J[extendInstrumentationAfterRegistration]
    J --> K[登録完了]

    L[リクエストエラー発生] --> M[instrumentationOnRequestError]
    M --> N[getInstrumentationModule]
    N --> O{onRequestError存在?}
    O -->|No| P[スキップ]
    O -->|Yes| Q[onRequestError実行]
    Q --> R{エラー発生?}
    R -->|Yes| S[console.errorでログ出力]
    R -->|No| T[正常完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-110-1 | 1回のみ登録 | registerInstrumentationはPromiseキャッシュにより1回のみ実行 | サーバー起動時 |
| BR-110-2 | ビルド時除外 | `process.env.NEXT_PHASE === 'phase-production-build'`の場合は登録をスキップ | ビルドフェーズ |
| BR-110-3 | モジュールキャッシュ | getInstrumentationModuleは結果をキャッシュし、2回目以降はキャッシュを返却 | 複数回呼び出し時 |
| BR-110-4 | ソフトエラー | onRequestError内でのエラーはconsole.errorでログ出力のみ（元のエラーは既にスローされているため） | onRequestErrorエラー時 |
| BR-110-5 | ENOENT無視 | instrumentation.jsファイルが存在しない場合はエラーとせずundefinedを返却 | ファイル不在時 |
| BR-110-6 | 再検証理由判定 | isOnDemandRevalidateの場合は'on-demand'、isStaticGenerationの場合は'stale'を返却 | ISRリクエスト時 |

### 計算ロジック

再検証理由の判定:
- `isOnDemandRevalidate === true` -> `'on-demand'`
- `isStaticGeneration === true` -> `'stale'`
- それ以外 -> `undefined`

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

### 操作別データベース影響一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | register実行エラー | ユーザーのregister関数内でエラー発生 | エラーメッセージにプレフィックスを追加して再スロー |
| - | onRequestErrorエラー | ユーザーのonRequestError内でエラー発生 | console.errorでログ出力し、元のエラー処理を継続 |
| ENOENT | ファイル不在 | instrumentation.jsが存在しない | undefinedを返却（正常ケース） |
| MODULE_NOT_FOUND | モジュール未発見 | instrumentation.jsの読み込みに失敗 | undefinedを返却（正常ケース） |

### リトライ仕様

リトライは不要。register()は1回のみ実行される。

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

該当なし。

## パフォーマンス要件

- register()はサーバー起動時に1回のみ実行され、レスポンスレイテンシに影響しない
- onRequestError()はエラー発生時のみ呼び出され、正常リクエストのパフォーマンスに影響しない
- モジュールキャッシュにより繰り返しのrequireを回避

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

- instrumentation.tsはサーバーサイドでのみ実行され、クライアントバンドルに含まれない
- onRequestErrorに渡されるリクエスト情報はReadonly型で保護されている

## 備考

- `INSTRUMENTATION_HOOK_FILENAME`定数は`src/lib/constants.ts`で定義されている
- `interopDefault`ヘルパーでCommonJS/ESModuleの互換性を確保している
- `extendInstrumentationAfterRegistration`はNode.js固有の拡張処理を実行する

---

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

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

### 推奨読解順序

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

Instrumentationモジュールの型定義とリクエストエラーコンテキストを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/next/src/server/instrumentation/types.ts` | InstrumentationModule型（23-26行目）：register()とonRequestError()の定義 |
| 1-2 | types.ts | `packages/next/src/server/instrumentation/types.ts` | RequestErrorContext型（1-11行目）：routerKind, routePath, routeType等 |
| 1-3 | types.ts | `packages/next/src/server/instrumentation/types.ts` | InstrumentationOnRequestError型（13-21行目）：エラーハンドラの関数シグネチャ |

**読解のコツ**: `InstrumentationModule`は`register?(): void`と`onRequestError?: InstrumentationOnRequestError`の2つのオプショナルメソッドを持つインターフェースである。両方とも省略可能。

#### Step 2: モジュール読み込みと登録を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | instrumentation-globals.external.ts | `packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts` | getInstrumentationModule関数（13-43行目）、registerInstrumentation関数（47-65行目） |

**主要処理フロー**:
1. **13-43行目**: `getInstrumentationModule` - キャッシュ付きモジュール読み込み
2. **17-19行目**: キャッシュヒット時の早期リターン
3. **22-32行目**: `require()`によるモジュール読み込み（interopDefault付き）
4. **33-42行目**: ENOENT/MODULE_NOT_FOUNDエラーの無視
5. **47-65行目**: `registerInstrumentation` - ビルドフェーズチェック後にregister()を実行
6. **49-51行目**: `phase-production-build`フェーズでの登録スキップ
7. **56-64行目**: register()の実行とエラーメッセージの加工

#### Step 3: エラーハンドリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | instrumentation-globals.external.ts | `packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts` | instrumentationOnRequestError関数（67-79行目） |

**主要処理フロー**:
- **72行目**: `getInstrumentationModule`でモジュール取得
- **74行目**: `onRequestError?.(...args)`でオプショナルチェイン付き呼び出し
- **75-78行目**: エラー発生時のconsole.errorログ出力

#### Step 4: 再検証理由ユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | utils.ts | `packages/next/src/server/instrumentation/utils.ts` | getRevalidateReason関数（1-12行目） |

**主要処理フロー**:
- **5-7行目**: isOnDemandRevalidateの場合は'on-demand'
- **8-10行目**: isStaticGenerationの場合は'stale'
- **11行目**: それ以外はundefined

#### Step 5: 登録の呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | instrumentation-globals.external.ts | `packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts` | ensureInstrumentationRegistered関数（82-93行目） |

**主要処理フロー**:
- **86-88行目**: Promiseキャッシュで1回のみ登録を保証
- **92行目**: キャッシュ済みPromiseを返却

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

```
Next.js Server 起動
    │
    ├─ ensureInstrumentationRegistered(projectDir, distDir)
    │      └─ registerInstrumentation(projectDir, distDir)
    │             ├─ getInstrumentationModule(projectDir, distDir)
    │             │      ├─ require('server/instrumentation.js')
    │             │      └─ interopDefault()
    │             ├─ instrumentation.register()
    │             └─ extendInstrumentationAfterRegistration()
    │
    └─ [リクエスト処理エラー時]
           └─ instrumentationOnRequestError(projectDir, distDir, error, req, ctx)
                  ├─ getInstrumentationModule() [キャッシュ]
                  └─ instrumentation.onRequestError(error, req, ctx)
```

### データフロー図

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

instrumentation.ts ──────────> ビルド ──────────────────────> server/instrumentation.js
                                                                    │
サーバー起動 ────────────────> ensureInstrumentationRegistered()      │
                                   │                                │
                                   ├─ require(instrumentation.js) ──┘
                                   └─ register() 実行

リクエストエラー ─────────────> instrumentationOnRequestError()
  ├─ error                         │
  ├─ {path, method, headers}       └─ onRequestError(error, req, ctx)
  └─ RequestErrorContext                    │
       ├─ routerKind                        └─ ユーザー定義処理
       ├─ routePath                             (Sentry, DataDog等)
       └─ routeType
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| types.ts | `packages/next/src/server/instrumentation/types.ts` | ソース | InstrumentationModule型、RequestErrorContext型の定義 |
| utils.ts | `packages/next/src/server/instrumentation/utils.ts` | ソース | getRevalidateReasonユーティリティ |
| instrumentation-globals.external.ts | `packages/next/src/server/lib/router-utils/instrumentation-globals.external.ts` | ソース | モジュール読み込み、register実行、エラーハンドリングのメイン実装 |
| constants.ts | `packages/next/src/lib/constants.ts` | ソース | INSTRUMENTATION_HOOK_FILENAME定数 |
| interop-default.ts | `packages/next/src/lib/interop-default.ts` | ソース | CommonJS/ESModule互換性ヘルパー |
| base-server.ts | `packages/next/src/server/base-server.ts` | ソース | instrumentationOnRequestErrorの呼び出し元 |
| next-server.ts | `packages/next/src/server/next-server.ts` | ソース | ensureInstrumentationRegisteredの呼び出し元 |
| route-module.ts | `packages/next/src/server/route-modules/route-module.ts` | ソース | ルートモジュールからのInstrumentation連携 |
