# Observability 配線完了手順 (M-02 + M-03 + Sentry Discord alert)

> **SSoT 参照**: 各通知の severity / channel / format は
> [notification-strategy.md](notification-strategy.md) で定義。
> 本 runbook は Sentry / Honeycomb / Discord webhook の **設定手順** のみ扱う。

> 監査 2026-04-29 #3 (Operations High) 対策。  
> 「observability 配線が "コード完備 / 配線未完" で実値ゼロ」を解消するための user 操作手順。

## 全体像

| 項目 | コード | dev 配線 | prod 配線 | 通知ルート |
|---|---|---|---|---|
| Sentry (Worker) | 完備 (`api/src/lib/sentry.ts`) | ✅ M-02 partial | ❌ 残 | email のみ → Discord 追加要 |
| Sentry (Flutter) | 完備 | ❌ 残 | ❌ 残 | (上記と同じ Sentry alert に集約) |
| Sentry (4 Web Portal) | 完備 (browser SDK + Replay) | ❌ 残 | ❌ 残 | (同上) |
| Honeycomb (OTel) | 完備 (`api/src/lib/otel.ts`) | ❌ 残 | ❌ 残 | n/a (手動 explore) |

## 手順 1: Sentry prod Worker DSN 投入

**前提**: Sentry org `parky-72` に 6 project 作成済 (commit `426919d3`)。

1. https://parky-72.sentry.io/settings/projects/parky-api/keys/ を開く
2. 既存 DSN または「Generate New Key」で新規作成 → DSN URL をコピー
3. ローカルで実行:
   ```bash
   bash scripts/secrets/inject-observability.sh sentry-prod-worker
   # プロンプトで DSN を貼り付け (入力非表示)
   ```
4. デプロイ後の smoke: dev では `https://dev-api.parky.co.jp/v1/_internal/sentry-test` を叩いて Sentry に Issue が来ることを確認 (prod は 404 化されているため事前に dev で確認すれば OK)

## 手順 2: Sentry Discord alert ルート追加 (M-02 残)

現状 12 alert rule は **email 通知のみ**。Discord に流す:

1. **Discord webhook 用意** (notification-strategy.md SSoT 準拠):
   - severity に応じた 5 channel × 5 webhook (`DISCORD_WEBHOOK_ALERTS` 〜 `DISCORD_WEBHOOK_ADMIN_TASKS`) を 1Password / GH Secrets / Wrangler に投入済の前提
   - Sentry alert rule は severity 別に対応 webhook を参照 ([notification-strategy.md §4](./notification-strategy.md))
2. **Sentry 側で Webhook integration**:
   - https://parky-72.sentry.io/settings/integrations/webhooks/ を開く
   - 「Add Installation」 → URL に Discord webhook URL を入力
   - Discord は Sentry 純正 integration (Slack のような) は無いので、「Webhook」 + Discord 互換の payload format に注意:
     - そのままだと Discord は payload parse できないため、**Webhook proxy** (Cloudflare Worker / Pipedream / Zapier 等) を 1 段挟むのが確実
     - 簡易版: Sentry → 自前の小 Worker (`api.parky.co.jp/v1/_internal/sentry-to-discord`) → Discord webhook の中継を作る
3. **Alert rule に webhook を追加**:
   - https://parky-72.sentry.io/alerts/rules/ を開く
   - 12 rule それぞれを開いて「Actions」 → `Send a notification via webhook` → 上記 webhook を選択
4. **動作確認**: dev で `/v1/_internal/sentry-test` 叩く → Discord に通知が来るか

> **代替案 (推奨)**: Sentry の **Discord integration** はネイティブで提供されている (2024 年以降)。
> https://parky-72.sentry.io/settings/integrations/discord/ で「Add Installation」 → Discord OAuth → channel 選択。
> webhook 中継不要。これが使えるなら手順 2 は 5 分で完了する。

## 手順 3: Sentry Flutter DSN 配線

Flutter は wrangler 配下ではない。配線:

1. **`mobileapp/prototype/flutter/.env.dev.json` に追加**:
   ```json
   {
     "API_BASE_URL": "https://dev-api.parky.co.jp",
     "SENTRY_DSN": "https://<key>@o0.ingest.sentry.io/<project>"
   }
   ```
2. **`.env.prod.json` も同様** (parky-flutter-prod project の DSN)
3. **CI (`.github/workflows/flutter-ci.yml`) で 1Password から取得**:
   ```yaml
   - name: Load Sentry DSN
     uses: 1password/load-secrets-action@...
     env:
       OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
       SENTRY_DSN_FLUTTER: op://p3ezteh54f3msvl4wqyw7gbiam/<sentry_flutter_item>/dsn
   - name: Build APK with Sentry DSN
     run: flutter build apk --dart-define=SENTRY_DSN=$SENTRY_DSN_FLUTTER
   ```
4. **1Password に Sentry DSN item を作成**:
   - Vault: PJ｜Parky
   - Title: `Sentry DSN｜Parky Flutter`
   - Field: `dsn` (secret) = DSN URL
   - dev / prod 別 item 推奨

## 手順 4: Sentry 4 Web Portal DSN 配線

Web Portal の Sentry DSN は Cloudflare Pages の env var で投入。Vite が build 時に embed する。

各 portal (admin/owner/marketing/home) で:

1. **1Password に DSN item を作成** (上記 Flutter と同じ要領で 4 つ):
   - `Sentry DSN｜Parky Admin`
   - `Sentry DSN｜Parky Owner`
   - `Sentry DSN｜Parky Marketing`
   - `Sentry DSN｜Parky Home`
2. **`.github/workflows/deploy-{portal}-{dev,prod}.yml` を編集**:
   ```yaml
   - name: Load secrets from 1Password
     uses: 1password/load-secrets-action@...
     env:
       OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
       VITE_SENTRY_DSN: op://p3ezteh54f3msvl4wqyw7gbiam/<sentry_admin_item>/dsn
       # 既存の VITE_SUPABASE_URL 等と並べる
   ```
3. **Vite build step は既存のまま** (`import.meta.env.VITE_SENTRY_DSN` を Vite が自動 embed)
4. **portal の Sentry init コード**は既に Sprint 0+1 で配線済 (`browserTracingIntegration + replayIntegration`)、DSN が空なら no-op

## 手順 5: Honeycomb signup + endpoint 投入 (M-03)

1. https://www.honeycomb.io/signup でアカウント作成 (無料: 20M events/month)
2. **Team** → **Environments** で `parky-dev` と `parky-prod` を作成
3. 各 environment の **API Key** を取得 (右上の Account → Team Settings → API Keys)
4. ローカルで:
   ```bash
   bash scripts/secrets/inject-observability.sh honeycomb-dev
   # endpoint: https://api.honeycomb.io
   # headers: x-honeycomb-team=<dev_api_key>

   bash scripts/secrets/inject-observability.sh honeycomb-prod
   # endpoint: https://api.honeycomb.io
   # headers: x-honeycomb-team=<prod_api_key>
   ```
5. **smoke**: 1 リクエストを `dev-api.parky.co.jp/v1/openapi.json` に投げて、数秒後 Honeycomb UI の `parky-dev` environment の Trace で span が見えることを確認

## 手順 6: 通知 DLQ digest cron の動作確認 (2026-04-29 配線済)

[notification-strategy.md §7](./notification-strategy.md) Layer 4 の週次 digest cron は `cron/notification-failures-digest.ts` で配線済。配線確認:

- **発火タイミング**: 月曜 UTC 09:00 (= 月曜 18:00 JST)、`HOURLY` cron スロット同居
- **配信先**: `#parky-insights` (`DISCORD_WEBHOOK_INSIGHTS`)
- **0 件なら静寂** (alert 疲れ防止、digest 自体を送らない)
- **集計後**: 該当行を `status='manual_acknowledged'` に更新 → 翌週 digest で再カウントされない

手動発火 (smoke):
```bash
# wrangler trigger は cron を直接叩けないので、開発環境で BFF endpoint 経由か、
# DB 側で 1 行 INSERT しておいて scheduled handler を Workers UI から手動 invoke。
# 本番 prod 投入後の smoke は handler 内 log を Sentry/Honeycomb で追跡。

# 動作確認用: dev DB に dummy failure を 1 件入れる
psql "$SUPABASE_DEV_DB_URL" -c "
  INSERT INTO admin.notification_failures
    (channel, severity, title, summary, payload, error_message, http_status, retry_count, source)
  VALUES
    ('OPS', 'P1', 'smoke test', 'dummy failure for digest smoke',
     '{}'::jsonb, 'simulated 500', 500, 3, 'manual-smoke');
"
# → 月曜 18:00 JST に #parky-insights に 1 件 digest が来るはず (or wrangler 経由で手動 cron 実行)
```

## 完了基準

- [ ] Sentry dev Worker (既に完了)
- [ ] Sentry prod Worker — `wrangler secret put SENTRY_DSN --env prod` 完了
- [ ] Sentry stg Worker — 任意 (stg 環境を別途用意した場合)
- [ ] Sentry Flutter — `.env.{dev,prod}.json` + CI 配線
- [ ] Sentry 4 Web Portal — workflow yml に VITE_SENTRY_DSN 追加 + 1P item
- [ ] Sentry Discord alert — 12 rule に webhook 追加 (推奨は Discord native integration)
- [ ] Honeycomb dev — endpoint + headers 投入
- [ ] Honeycomb prod — endpoint + headers 投入
- [ ] Honeycomb smoke — span 受信確認
- [x] 通知 DLQ table 作成 — admin.notification_failures (dev 適用済 2026-04-29)
- [ ] 通知 DLQ table 作成 — prod 適用 (明示許可待ち、feedback_no_prod_migration_yet)
- [ ] DLQ digest cron smoke — 月曜 18:00 JST に #parky-insights digest 投稿確認

## 関連

- 監査: `.work/parky/2026-04-29_001_parky_comprehensive_evaluation_v2.html` (#3 Operations)
- M-02 / M-03: `.work/parky/2026-04-28_002_parky_manual_action_checklist.html`
- 既存: `docs/ops/sentry-setup.md` (dev Worker 配線手順、本 doc と相互参照)
- スクリプト: `scripts/secrets/inject-observability.sh`
