# 機能設計書 111-OpenTelemetryトレース

## 概要

本ドキュメントは、Next.jsにおけるOpenTelemetry準拠のトレーシングサポート機能の設計を記載する。ビルド・開発・本番サーバーの各フェーズで発生するイベントのトレースデータを収集・記録・アップロードする仕組みを定義する。

### 本機能の処理概要

**業務上の目的・背景**：Next.jsアプリケーションの開発・ビルド・実行時におけるパフォーマンスボトルネックの特定と改善を支援するため、OpenTelemetry互換のトレーシング基盤が必要である。ビルド時間の分析、開発サーバーのHMRレイテンシ計測、本番環境でのリクエスト処理時間の把握など、計測データに基づく最適化を可能にする。

**機能の利用シーン**：開発者がビルドパフォーマンスを分析する場面、開発サーバーのHMR速度を計測する場面、本番環境でのレスポンス時間を監視する場面、Vercelプラットフォーム等へのトレースデータアップロードを行う場面で利用される。

**主要な処理内容**：
1. Spanの生成・管理（親子関係を持つトレーススパンの作成と停止）
2. トレースイベントの記録（JSON形式でのファイル書き出し）
3. テレメトリへのイベントレポート（特定イベントの利用統計連携）
4. トレースデータの外部アップロード（子プロセスによる非同期送信）
5. ワーカープロセス間でのトレース状態の共有（シリアライズ・デシリアライズ）

**関連システム・外部連携**：トレースアップロードURL（`NEXT_TRACE_UPLOAD_URL`環境変数で指定）への外部送信、テレメトリシステムとの連携がある。

**権限による制御**：特に権限制御はないが、`NEXT_TRACE_SPAN_THRESHOLD_MS`環境変数でスパン記録の閾値を制御でき、`NEXT_TRACE_UPLOAD_DEBUG`環境変数でデバッグモードを有効化できる。

## 関連画面

本機能はバックエンド処理であり、直接関連する画面は存在しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIコマンド実行時にバックグラウンドで動作 |

## 機能種別

データ収集・計測処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | スパンの名前 | 空文字でないこと |
| parentId | SpanId (number) | No | 親スパンのID | 正の整数 |
| attrs | Record<string, string> | No | スパンの属性（タグ） | - |
| startTime | bigint | No | スパン開始時刻（ナノ秒） | - |
| stopTime | bigint | No | スパン停止時刻（ナノ秒） | - |

### 入力データソース

- 環境変数: `TRACE_ID`, `NEXT_PRIVATE_TRACE_ID`, `NEXT_TRACE_SPAN_THRESHOLD_MS`, `NEXT_TRACE_UPLOAD_DEBUG`, `NEXT_TRACE_UPLOAD_FULL`
- ビルドプロセスおよびサーバーランタイムからの内部呼び出し
- グローバルトレース状態（`traceGlobals`マップ: `distDir`, `phase`, `telemetry`）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| name | string | トレースイベント名 |
| duration | number | 期間（マイクロ秒） |
| timestamp | number | タイムスタンプ（マイクロ秒） |
| id | SpanId | スパンの一意ID |
| parentId | SpanId | 親スパンID |
| tags | Object | スパン属性 |
| startTime | number | 開始日時（ミリ秒、Date.now()） |
| traceId | string | トレースセッションID |

### 出力先

- ファイルシステム: `{distDir}/trace`（JSON Lines形式、開発時50MB上限でローテーション）
- ファイルシステム: `{distDir}/trace-build`（ビルド時のみ、許可リストイベントのみ）
- テレメトリシステム: 特定イベント（`webpack-invalidated`等）のみ
- 外部トレースサーバー: `traceUploadUrl`への HTTP POST

## 処理フロー

### 処理シーケンス

```
1. トレースの初期化
   └─ traceGlobalsにdistDir・phase・telemetryを設定
2. Spanの生成
   └─ new Span({ name, parentId, attrs })でスパンを作成
3. 処理の実行とトレース
   └─ traceFn/traceAsyncFnで関数を実行し自動的にスパンを停止
4. スパンの停止とイベント記録
   └─ stop()で期間を算出し、閾値超過時にreporterへ報告
5. マルチレポーターによる分配
   └─ reportToJson, reportToJsonBuild, reportToTelemetryへ並行配信
6. バッチ処理とファイル書き出し
   └─ 100イベント蓄積時またはflush時にJSONをファイルへ書き出し
7. トレースのアップロード（オプション）
   └─ 子プロセスでtrace-uploaderを起動し外部サーバーへ送信
```

### フローチャート

```mermaid
flowchart TD
    A[Span生成] --> B[処理実行]
    B --> C[Span停止]
    C --> D{閾値チェック}
    D -->|超過| E[MultiReporterに報告]
    D -->|未満| F[破棄]
    E --> G[reportToJson]
    E --> H[reportToJsonBuild]
    E --> I[reportToTelemetry]
    G --> J[バッチ蓄積]
    J --> K{100件超過?}
    K -->|Yes| L[ファイル書き出し]
    K -->|No| M[バッチに蓄積継続]
    H --> N{ビルドフェーズ?}
    N -->|Yes| O[許可リストチェック]
    N -->|No| P[破棄]
    O --> Q[trace-buildファイルへ書き出し]
    I --> R{イベント許可?}
    R -->|Yes| S[テレメトリ記録]
    R -->|No| T[破棄]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | スパン重複停止防止 | 同一スパンのstop()を2回呼んでも2回目は無視される | Span.stop()呼び出し時 |
| BR-02 | 閾値フィルタリング | NEXT_TRACE_SPAN_THRESHOLD_MS未満のスパンは記録されない | スパン停止時 |
| BR-03 | ファイルローテーション | 開発モードではトレースファイルが50MBを超えるとローテーションされる | 開発サーバー実行時 |
| BR-04 | ビルドトレース許可リスト | ビルドトレースは許可リストのイベントのみ記録される | ビルドフェーズ |
| BR-05 | セッション分離 | アップロード時は現在セッションのトレースIDに一致するイベントのみ送信 | トレースアップロード時 |

### 計算ロジック

- 期間の算出: `(end - start) / 1000`（ナノ秒からマイクロ秒への変換）
- タイムスタンプ: `start / 1000`（ナノ秒からマイクロ秒）
- `process.hrtime.bigint()`を使用した高精度時間計測

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

本機能はデータベースを使用しない。トレースデータはファイルシステムに書き出される。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Duration overflow | スパン期間がNumber.MAX_SAFE_INTEGERを超過 | Errorをスロー |
| - | File write error | トレースファイルへの書き込み失敗 | console.logでエラー出力、処理は継続 |
| - | Upload failure | トレースアップロードのHTTPリクエスト失敗 | 子プロセスのため本体への影響なし |

### リトライ仕様

トレースアップロードにおけるリトライは実装されていない。アップロードは子プロセスで非同期実行されるため、失敗時は静かに終了する。

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

トランザクション管理は不要。ファイル書き出しはappend modeで行われ、RotatingWriteStreamによりドレイン待ちとローテーションが管理される。

## パフォーマンス要件

- `process.hrtime.bigint()`によるナノ秒精度の時間計測
- バッチサイズ100件でのファイル書き出しによるI/O最適化
- トレースアップロードは`detached`子プロセスで実行され、メインプロセスのパフォーマンスに影響しない
- 開発モードでのファイルサイズ上限50MBによるディスク消費制御

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

- トレースIDは`crypto.randomBytes(8).toString('hex')`で生成され、予測困難
- アップロード先URLは環境変数で指定され、ハードコードされていない
- テレメトリのanonymousIdとsessionIdがアップロードメタデータに含まれる

## 備考

- トレースIDは`TRACE_ID`または`NEXT_PRIVATE_TRACE_ID`環境変数で外部から指定可能
- ビルドトレースの許可リストイベント: `next-build`, `run-turbopack`, `run-webpack`, `run-typescript`, `run-eslint`, `static-check`, `collect-build-traces`, `static-generation`, `output-export-full-static-export`, `adapter-handle-build-complete`, `output-standalone`, `telemetry-flush`

---

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

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

### 推奨読解順序

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

まず、トレースイベントとスパンのデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/next/src/trace/types.ts` | SpanId, TraceEvent, TraceStateの型定義を理解する |
| 1-2 | types.ts (report) | `packages/next/src/trace/report/types.ts` | Reporterインターフェースの定義を理解する |

**読解のコツ**: SpanIdはnumber型のエイリアス、TraceEventはZipkin互換のイベント構造となっている。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/next/src/trace/index.ts` | 外部公開されるAPI（trace, Span, flushAllTraces等）を確認 |
| 2-2 | shared.ts | `packages/next/src/trace/shared.ts` | グローバル状態管理（traceGlobals, traceId）の仕組みを理解 |

**主要処理フロー**:
1. **1-10行目**: trace, Span等のメインAPIをre-export
2. **11行目**: setGlobalでグローバル状態を設定

#### Step 3: Spanクラスの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | trace.ts | `packages/next/src/trace/trace.ts` | Spanクラスの全メソッド（生成、停止、子スパン作成、関数トレース） |

**主要処理フロー**:
- **4行目**: ナノ秒→マイクロ秒変換の定数
- **15-17行目**: `NEXT_TRACE_SPAN_THRESHOLD_MS`環境変数による閾値
- **20-23行目**: SpanStatus enum（Started/Stopped）
- **29-64行目**: Spanコンストラクタ（name, parentId, attrs, 高精度タイマー）
- **70-98行目**: stop()メソッド（期間算出、閾値チェック、reporter呼び出し）
- **100-102行目**: traceChild()で子スパン生成
- **132-146行目**: traceFn/traceAsyncFnで処理をラップしてトレース
- **149-155行目**: trace()ファクトリ関数
- **163-172行目**: ワーカープロセス間のトレース状態管理

#### Step 4: レポーターの仕組みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | report/index.ts | `packages/next/src/trace/report/index.ts` | MultiReporterによる3つのレポーターの統合 |
| 4-2 | report/to-json.ts | `packages/next/src/trace/report/to-json.ts` | JSON形式でのファイル書き出し（バッチ処理、RotatingWriteStream） |
| 4-3 | report/to-json-build.ts | `packages/next/src/trace/report/to-json-build.ts` | ビルド専用のトレース記録（許可リストフィルタ） |
| 4-4 | report/to-telemetry.ts | `packages/next/src/trace/report/to-telemetry.ts` | テレメトリへの選択的イベント転送 |

**主要処理フロー**:
- **report/index.ts 24-28行目**: MultiReporterが3つのレポーターを統合
- **report/to-json.ts 8-32行目**: batcher関数で100件単位のバッチ処理
- **report/to-json.ts 41-98行目**: RotatingWriteStreamでファイルサイズ管理
- **report/to-json-build.ts 77-90行目**: ビルドイベント許可リスト

#### Step 5: トレースアップロードを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | upload-trace.ts | `packages/next/src/trace/upload-trace.ts` | 子プロセスでtrace-uploaderを起動する処理 |
| 5-2 | trace-uploader.ts | `packages/next/src/trace/trace-uploader.ts` | トレースファイルの読み込みとHTTP POSTによるアップロード |

**主要処理フロー**:
- **upload-trace.ts 4-58行目**: spawnで子プロセスを起動、detachedモードで実行
- **trace-uploader.ts 15-26行目**: DEV_ALLOWED_EVENTSでdev時のイベントフィルタ
- **trace-uploader.ts 28-55行目**: BUILD_ALLOWED_EVENTSでビルド時のイベントフィルタ
- **trace-uploader.ts 111-198行目**: メイン処理（ファイル読み込み、フィルタ、HTTP POST）

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

```
trace() / new Span()
    |
    +-- Span.stop()
    |       |
    |       +-- reporter.report(traceEvent)
    |               |
    |               +-- MultiReporter
    |                       |
    |                       +-- reportToJson (to-json.ts)
    |                       |       +-- batcher -> RotatingWriteStream -> {distDir}/trace
    |                       |
    |                       +-- reportToJsonBuild (to-json-build.ts)
    |                       |       +-- batcher -> RotatingWriteStream -> {distDir}/trace-build
    |                       |
    |                       +-- reportToTelemetry (to-telemetry.ts)
    |                               +-- telemetry.record()
    |
    +-- Span.traceFn() / Span.traceAsyncFn()
    |       +-- fn(span) -> span.stop()
    |
    +-- Span.traceChild()
            +-- new Span({ parentId: this.id })

flushAllTraces()
    +-- reporter.flushAll()
            +-- [各レポーターのflushAll]

uploadTrace()
    +-- child_process.spawn(trace-uploader.ts)
            +-- ファイル読み込み -> HTTP POST
```

### データフロー図

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

ビルド/開発/本番処理 -----> Span生成・停止 -----> MultiReporter -----> {distDir}/trace (JSON Lines)
                                                       |
                                                       +-----------> {distDir}/trace-build (ビルド時のみ)
                                                       |
                                                       +-----------> テレメトリシステム (特定イベントのみ)

{distDir}/trace ---------> trace-uploader -----------> 外部トレースサーバー (HTTP POST)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/next/src/trace/index.ts` | ソース | モジュールエントリーポイント・API公開 |
| trace.ts | `packages/next/src/trace/trace.ts` | ソース | Spanクラスとtrace関数の実装 |
| types.ts | `packages/next/src/trace/types.ts` | ソース | 型定義（SpanId, TraceEvent, TraceState） |
| shared.ts | `packages/next/src/trace/shared.ts` | ソース | グローバル状態とトレースID管理 |
| report/index.ts | `packages/next/src/trace/report/index.ts` | ソース | MultiReporterの構築 |
| report/to-json.ts | `packages/next/src/trace/report/to-json.ts` | ソース | JSONファイルへのトレース書き出し |
| report/to-json-build.ts | `packages/next/src/trace/report/to-json-build.ts` | ソース | ビルド専用トレース書き出し |
| report/to-telemetry.ts | `packages/next/src/trace/report/to-telemetry.ts` | ソース | テレメトリへのイベントレポート |
| report/types.ts | `packages/next/src/trace/report/types.ts` | ソース | Reporterインターフェース定義 |
| upload-trace.ts | `packages/next/src/trace/upload-trace.ts` | ソース | トレースアップロード起動処理 |
| trace-uploader.ts | `packages/next/src/trace/trace-uploader.ts` | ソース | トレースアップロード実行処理 |
| trace.test.ts | `packages/next/src/trace/trace.test.ts` | テスト | Spanクラスのユニットテスト |
