# 機能設計書 68-エラー報告

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおけるエラー報告機能の設計を記述するものである。Sentryを使用したクラッシュレポートおよびエラー報告機能の仕様を定義する。

### 本機能の処理概要

本機能は、アプリ内で発生したエラーや例外をSentry.ioに自動的に報告する機能を提供する。これにより、開発者は本番環境で発生した問題を迅速に把握し、アプリの品質向上に役立てることができる。エラー報告はユーザー設定で有効/無効を切り替えられ、デバッグモードでは送信されない。

**業務上の目的・背景**：モバイルアプリでは、ユーザー環境の多様性により予期しないエラーが発生する可能性がある。自動エラー報告機能により、ユーザーからの問い合わせを待たずにプロアクティブに問題を発見・修正できる。また、エラー発生時のコンテキスト情報（サーバーバージョン、デバイス情報等）を収集することで、問題の原因特定が容易になる。

**機能の利用シーン**：
- アプリがクラッシュした際の自動エラー報告
- APIエラー（サーバーエラー等）の自動報告
- 開発者への手動メッセージ送信（デバッグ用）

**主要な処理内容**：
1. FlutterError.onErrorコールバックでFlutterエラーをキャプチャ
2. runZonedGuardedで非同期エラーをキャプチャ
3. エラー情報にサーバー・アプリ・デバイス情報を付加
4. Sentry.captureExceptionでSentryに送信
5. INV_REPORT_ERRORS設定によるエラー報告の有効/無効制御

**関連システム・外部連携**：
- Sentry.io：エラートラッキングサービス
- device_info_plus：デバイス情報取得
- package_info_plus：アプリ情報取得

**権限による制御**：エラー報告はユーザー設定（INV_REPORT_ERRORS）で制御可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | アプリ設定画面 | 補助機能 | エラー報告設定の有効化・無効化 |

## 機能種別

バックグラウンド処理 / 外部サービス連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| error | dynamic | Yes | 発生したエラーオブジェクト | null不可 |
| stackTrace | StackTrace | No | スタックトレース | null許容 |
| source | String | Yes | エラー発生源の識別子 | 空文字列不可 |
| context | Map<String, String> | No | 追加コンテキスト情報 | null許容 |

### 入力データソース

- Flutterフレームワーク：FlutterError.onErrorコールバック
- Dart Runtime：runZonedGuardedのエラーハンドラ
- アプリコード：直接呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| eventId | SentryId | Sentryイベント識別子 |

### 出力先

- Sentry.io：クラウドサービスにエラー情報を送信

## 処理フロー

### 処理シーケンス

```
1. エラー発生
   └─ FlutterError.onError または runZonedGuarded でキャプチャ
2. エラー検証
   └─ sentryIgnoreError()で無視すべきエラーかチェック
   └─ デバッグモードかチェック
   └─ エラー報告設定が有効かチェック
3. コンテキスト情報収集
   └─ getServerInfo()でサーバー情報取得
   └─ getAppInfo()でアプリ情報取得
   └─ getDeviceInfo()でデバイス情報取得
4. Sentryに送信
   └─ Sentry.configureScope()でコンテキストを設定
   └─ Sentry.captureException()でエラーを送信
```

### フローチャート

```mermaid
flowchart TD
    A[エラー発生] --> B{無視すべきエラー?}
    B -->|Yes| C[終了]
    B -->|No| D{デバッグモード?}
    D -->|Yes| E[ログ出力のみ]
    D -->|No| F{報告設定有効?}
    F -->|No| G[ログ出力のみ]
    F -->|Yes| H[コンテキスト情報収集]
    H --> I[サーバー情報取得]
    I --> J[アプリ情報取得]
    J --> K[デバイス情報取得]
    K --> L[Sentry.configureScope]
    L --> M[Sentry.captureException]
    M --> N[終了]
    E --> N
    G --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-68-01 | デバッグモード除外 | デバッグモードではSentryに送信しない | 開発中 |
| BR-68-02 | ユーザー設定制御 | INV_REPORT_ERRORS=falseで送信しない | 設定無効時 |
| BR-68-03 | 404メディアエラー除外 | /media/パスへの404エラーは送信しない | メディアファイル不存在 |
| BR-68-04 | FileSystemException除外 | ローカルファイルエラーは送信しない | ファイルシステムエラー |
| BR-68-05 | URL機密情報除去 | URLからサーバーアドレスを除去してパス・クエリのみ送信 | URL含むエラー |
| BR-68-06 | デフォルト有効 | エラー報告はデフォルトで有効 | 初期状態 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 設定読み込み | settings（sembast store） | SELECT | INV_REPORT_ERRORSキーでエラー報告設定を取得 |

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

#### settings（sembast store）

| 操作 | 項目（キー） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | reportErrors | bool値（デフォルト: true） | エラー報告有効フラグ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Sentry送信エラー | ネットワーク障害等 | エラーログ出力、処理継続 |
| - | デバイス情報取得エラー | まれなケース | 空の情報で続行 |

### リトライ仕様

Sentryパッケージが内部でリトライを管理する。アプリ側での明示的なリトライは実装されていない。

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

本機能ではトランザクションは発生しない。

## パフォーマンス要件

- エラーキャプチャ：10ms以内（非同期送信のため）
- Sentry送信：バックグラウンドで非同期実行

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

- ユーザーの個人情報は収集しない
- サーバーURLからパスとクエリのみを送信（ホスト名は除去）
- DSNキーはdsn.dartで管理（公開リポジトリには含めない）
- ユーザーは設定でエラー報告を無効化可能

## 備考

- SENTRY_DSN_KEYが空の場合、エラー報告機能は無効
- sentryIgnoreError()で特定のエラーをフィルタリング
- assert()を使用したデバッグモード判定（isInDebugMode）

---

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

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

### 推奨読解順序

#### Step 1: エラー報告の初期化を理解する

main.dartでのSentry初期化とエラーハンドラ設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | main.dart | `lib/main.dart` | Sentry初期化とエラーハンドラ |

**読解のコツ**:
- **26-87行目**: `runZonedGuarded`で非同期エラーをキャプチャ
- **35-43行目**: `Sentry.init`でSentryを初期化
- **46-58行目**: `FlutterError.onError`でFlutterエラーをキャプチャ

#### Step 2: エラー報告関数を理解する

sentry.dartでのエラー報告実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sentry.dart | `lib/inventree/sentry.dart` | sentryReportError関数 |

**主要処理フロー**:
- **154-238行目**: `sentryReportError`関数
- **160-163行目**: `sentryIgnoreError`で無視すべきエラーをチェック
- **171-174行目**: デバッグモードチェック
- **180-182行目**: ユーザー設定（INV_REPORT_ERRORS）チェック
- **190-204行目**: FlutterError.onErrorの特定エラーをフィルタ
- **206-208行目**: コンテキスト情報を収集
- **222-227行目**: Sentry.configureScopeでコンテキストを設定
- **229-237行目**: Sentry.captureExceptionでエラーを送信

#### Step 3: コンテキスト情報収集を理解する

デバイス・アプリ・サーバー情報の収集を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sentry.dart | `lib/inventree/sentry.dart` | 各種情報取得関数 |

**主要処理フロー**:
- **13-56行目**: `getDeviceInfo`関数（iOS/Android別のデバイス情報取得）
- **58-61行目**: `getServerInfo`関数（サーバーバージョン・APIバージョン取得）
- **63-73行目**: `getAppInfo`関数（アプリ名・バージョン・ビルド番号取得）

#### Step 4: エラーフィルタリングを理解する

無視すべきエラーの判定ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sentry.dart | `lib/inventree/sentry.dart` | sentryIgnoreError関数 |

**主要処理フロー**:
- **243-253行目**: `sentryIgnoreError`関数
- **245-250行目**: HttpExceptionで/media/パスへの404はtrue（無視）を返す

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

```
main() (main.dart)
    │
    ├─ Sentry.init()
    │      └─ DSN設定、リリース情報設定
    │
    ├─ FlutterError.onError = (details) async
    │      └─ sentryReportError("FlutterError.onError", ...)
    │
    └─ runZonedGuarded(
           body: () => runApp(),
           onError: (error, stackTrace) => sentryReportError("main.runZonedGuarded", ...)
       )

sentryReportError() (sentry.dart)
    │
    ├─ sentryIgnoreError(error)
    │      └─ HttpException 404 /media/ チェック
    │
    ├─ isInDebugMode()
    │      └─ assert()でデバッグモード判定
    │
    ├─ InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS)
    │
    ├─ getServerInfo()
    │      └─ InvenTreeAPI().serverVersion / apiVersion
    │
    ├─ getAppInfo()
    │      └─ PackageInfo.fromPlatform()
    │
    ├─ getDeviceInfo()
    │      └─ DeviceInfoPlugin() [iOS/Android]
    │
    └─ Sentry.captureException(error, stackTrace)
```

### データフロー図

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

                      ┌─────────────────────┐
Flutterエラー ───────▶│ FlutterError.onError│
                      └──────────┬──────────┘
                                 │
非同期エラー ────────▶┌──────────┴──────────┐
                     │ sentryReportError()  │
                     └──────────┬───────────┘
                                │
                                ▼
                     ┌─────────────────────┐
                     │ エラーフィルタリング │
                     │ - sentryIgnoreError │
                     │ - isInDebugMode     │
                     │ - INV_REPORT_ERRORS │
                     └──────────┬──────────┘
                                │
                                ▼
                     ┌─────────────────────┐
                     │ コンテキスト収集    │
                     │ - getServerInfo     │
                     │ - getAppInfo        │
                     │ - getDeviceInfo     │
                     └──────────┬──────────┘
                                │
                                ▼
                     ┌─────────────────────┐
                     │ Sentry.captureExc.  │──────▶ Sentry.io
                     └─────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sentry.dart | `lib/inventree/sentry.dart` | ソース | エラー報告関数・コンテキスト収集 |
| main.dart | `lib/main.dart` | ソース | Sentry初期化・エラーハンドラ設定 |
| dsn.dart | `lib/dsn.dart` | ソース | Sentry DSNキー定義 |
| preferences.dart | `lib/preferences.dart` | ソース | エラー報告設定キー定義 |
| api.dart | `lib/api.dart` | ソース | サーバー情報提供 |
