v1
PoC / 監視設計

オブザーバビリティ設計

実査機能を構成する全コンポーネント(新基盤 Next.js・minedia-www 新エンジン部・文字起こしワーカー・AI Agent・LiveKit Cloud)のログ・メトリクス・トレース・エラーの計測方針。インフラ(Cloudflare / Google Cloud)が未確定でも成立するよう、OpenTelemetry を計装の共通言語とする。

🧭

用語: 本ページでは「管理API」= 調査側サーバー → 基盤のサーバー間 REST(Room 作成・トークン発行など。REST API の同期プレーン)、「ルーム内API」= 参加者ブラウザ ⇔ 基盤のルーム内ランタイム(入室・チャットなど)を指す。

設計原則

原則名前内容
P1ベンダー中立(OTel 一本)全コンポーネントを OTel SDK / OTLP で計装し、送信先は環境変数だけで差し替え可能にする。インフラ選定(Cloudflare / Google Cloud)の未決に監視設計を人質に取られない。唯一の意図的な例外がエラートラッキング(Sentry)— エラーの自動グルーピング・issue 管理は OTel が標準化しない領域のため。
P2テレメトリ属性も「契約」サービス間の相関に使う属性名(room.uuid など)は API 契約と同格に管理する。命名が揺れた時点で横断検索は成立しない。
P3低トラフィック × ライブイベント特性実査は「時間枠が固定された少数の高価値セッション」。リクエスト比率ベースの監視は母数不足で機能しないため、イベント駆動アラート(録画失敗=即時通知)を主軸にする。
P4相関ファースト障害調査の起点は常に「この Room / この参加者に何が起きたか」。全シグナルを room.uuid で横断検索できることを最優先する。
P5PII・秘匿情報ゼロJWT・API key secret・表示名・発話/チャット本文はいかなるシグナルにも載せない。載せてよいのは不透明 ID(uuid / external_ref / jti)のみ。

計測対象マップ

参加者ブラウザ 実査フロント 新基盤(interview-core) Next.js Route Handlers 管理API / ルーム内API / webhook 文字起こしワーカー Node + BullMQ ⑤ enqueue(traceparent 同梱) LiveKit Cloud SFU / Egress / webhook AI Agent Python / Managed Hosting minedia-www core client / webhook 受信 OTLP バックエンド メトリクス・ログ・トレース Sentry エラートラッキング ④ HTTP(ルーム内API) ① REST + traceparent ② webhook(span link) ③ webhook(room_sid) OTLP ⑥ OTLP 直送 例外(SDK)
#境界計測手段相関キー
minedia-www → core REST(管理API)OTel 自動計装(HTTP client/server)+ W3C traceparent 伝播trace_id(同一トレース)
core → minedia-www Webhook(W1〜W6)配信 attempt ごとに span。配信リクエストに発生元トレースを同梱し、受信側は span link で接続event.id + traceparent link
LiveKit → core webhookLiveKit は traceparent を送らないため、受信 span に livekit.room_sid を付与して属性相関room_sidroom.uuid
参加者ブラウザ → core(ルーム内API)サーバー側 span(入室ゲート・チャット送信・認可)。ブラウザ側 RUM は将来判断room.uuid / participant.uuid / jti
core → 文字起こしワーカー(BullMQ)job payload に traceparent を同梱。ワーカーは新トレースを開始し span link で接続recording.uuid / transcription.uuid
AI Agent(LiveKit Managed Hosting)自前コレクタを置けない前提で OTLP エンドポイントへ直送(環境変数のみで設定)room.uuid
LiveKit メディア品質SaaS のため計装不可。Room ごとの provider_inspector_url(LiveKit ダッシュボード)+接続テストのサマリで代替room_sid

相関設計

トレース伝播ルール

経路ルール理由
同期 REST(①④)W3C Trace Context(traceparent)で親子接続標準。自動計装で完結する
Webhook(②)受信側は新トレースを開始し span link で発生元に接続配信リトライは最大 12 時間後まで走る(Webhook 仕様)。1 トレースに収めると duration が壊れる
非同期ジョブ(⑤)同上(span link)STT は分〜時間単位。enqueue → 完了を 1 トレースにしない
LiveKit webhook(③)伝播不可。属性相関livekit.room_sid)のみ外部 SaaS

標準属性(テレメトリ契約)

全コンポーネント・全シグナル共通。OTel の属性命名規約(dot 区切り小文字)に従う。

属性付与対象
tenant.idint全て
room.uuidstringRoom に紐づく全操作(API 公開 ID)
room.kindstringinterview / connection_test
session.uuidstringSession が存在する場合
participant.uuidstring参加者起点の操作
participant.rolestringmoderator / observer など(JWT の role
token.jtistring入室ゲート・トークン失効
recording.uuid / transcription.uuidstring録画・文字起こし
event.id / webhook.type / webhook.attemptstring / intWebhook 配信・受信
livekit.room_sidstringLiveKit 連携全て
idempotency.keystring作成系 POST(秘匿情報ではない)
🚫

禁止属性: JWT / API key の生値、display_name、email、チャット・発話の本文。これらはいかなるシグナルにも載せない(原則 P5)。

🔎

障害調査の 4 手: オペ画面の Room 詳細で room.uuid をコピー → ログ検索 → trace_id へジャンプ → provider_inspector_url でメディア層を確認。この導線が成立するよう、オペ画面に room.uuid のコピー導線を置く。

シグナル設計

トレース — 手動スパン

HTTP・DB・Redis は自動計装に任せ、ドメイン上意味のある操作だけ手動 span を切る。

Span 名サービス内容・主属性
room.provisioncoreRoom 作成時の LiveKit CreateRoom 同期呼び出し+ロールバック。失敗時 error.type=RTC_PROVISION_FAILED
token.issuecore参加トークン発行・Participant 生成。participant.role, force
entry_gate.verifycore入室検証。却下理由を entry_gate.result に記録: ok / expired / banned
chat.dispatchcore認可 → 永続化 → sendData 配信の 3 段
webhook.delivercore配信 attempt 単位。webhook.type, webhook.attempt, ステータスコード
webhook.receive.livekitcoreLiveKit webhook 処理。livekit.event, livekit.room_sid
recording.controlcoreStartEgress / StopEgress 呼び出し
transcription.stageworkerstage=extract(ffmpeg)/ stt / normalize(WebVTT)の 3 span
core_client.requestwwwcore API 呼び出し(自動計装+ endpoint 属性)
webhook.receive.corewwwW1〜W6 受信。署名検証の結果も属性化

メトリクスカタログ

HTTP の基本メトリクスは自動計装に任せ、ここではドメインメトリクスのみ定義する。接頭辞 interview.

メトリクスラベル目的
interview.room.provisionscounterkind, resultRoom 作成成功率。rtc_failed は LiveKit 障害の一次指標
interview.token.issuancescounterrole, forced, result発行量と異常(ban 済みへの再発行 409 など)の検知
interview.entry_gate.decisionscounterresult(3 値)入室失敗の理由別内訳expired の急増はトークン再取得まわりの不具合シグナル
interview.participants.activegaugekindLiveKit webhook(joined / left)から算出する現在参加者数
interview.recording.transitionscounterto_statusfailed は即アラート
interview.webhook.deliveriescountertype, result, attempt配信健全性
interview.webhook.exhaustedcountertype5 回リトライ失効 → 破棄の件数。受信側の GET ポーリング補填が必要になった契約上の重大イベント
interview.transcription.jobscounterresult文字起こしジョブの成否
interview.transcription.stage.durationhistogramstageffmpeg / STT / normalize の所要時間。STT ベンダの性能劣化検知
interview.transcription.queue.depthgaugeBullMQ の waiting + delayed。滞留検知
interview.transcription.freshnesshistogram録画完了 → 文字起こし完了通知(W6)までの経過秒。遅延監視
interview.connection_test.resultscounterresult, failed_step接続テスト 8 ステップの失敗分布
interview.core_client.fallbackscounterreasonwww 基盤 down 検知 → OpenTok フォールバック判断の発火回数

ログ

  • 形式: 全サービス JSON 構造化ログ(1 行 1 イベント)。フィールドは標準属性+ severity / message / trace_id / span_id(OTel ログブリッジで自動注入)。
  • レベル運用: INFO = ドメインイベント(Room 作成・入退室・録画遷移・webhook 配信結果)、WARN = 自動回復した異常(リトライ成功・入室拒否)、ERROR = 要調査(provision 失敗・録画 failed・配信 exhausted)。
  • アクセスログはインフラ層の標準機能に任せ、アプリでは重複して出さない。
  • 本文の扱い: チャット・文字起こし・提示物の中身はログに載せない(P5)。長さ・件数のみ可。
  • 監査との分界: 「誰が何をしたか」の証跡は RoomEvent(DB)が正本。ログは運用調査用で保持 30 日。監査要件をログ基盤に負わせない。

エラートラッキング(Sentry)

未捕捉例外・要調査エラーの追跡は Sentry に集約する。minedia-www は既に Sentry を「エラー 100% 送信・本番の performance tracing 無効」で運用しており、本設計の分界と矛盾しない。

Sentry が担うもの

例外 → 自動グルーピング → issue 管理

未捕捉例外・スタックトレースの自動グルーピング・リグレッション検知・release health。OTel が標準化しない領域であり、既存運用・通知フローに乗る。

OTel が担うもの

トレース・メトリクス・構造化ログ → OTLP

計装はすべて OTel で書く。Sentry の performance tracing は全サービス無効tracesSampleRate: 0)にして二重トレースを防ぐ。

サービスSDK備考
core(Next.js)@sentry/nextjs または @sentry/cloudflareデプロイ先(Cloud Run / Workers)に応じて選択。サーバー側のみ
文字起こしワーカー@sentry/nodeジョブの最終リトライ超過例外を必ず capture
minedia-www 新エンジン部既存 sentry-ruby追加作業はタグ付与のみ。before_send の除外ルールに実査系を誤って含めない
AI Agent(Python)sentry-sdkManaged Hosting でも DSN(環境変数)だけで動作
  • プロジェクト分割: interview-core / interview-transcriber / interview-agent を www と別プロジェクトにする(issue のオーナーシップとアラート経路の分離)。
  • タグ契約: 標準属性(room.uuid / tenant.id など)を同名で Sentry タグに付与。「Sentry issue → room.uuid → ログ・トレース横断検索」が障害調査の 4 手に接続する。
  • 依存の隔離: アプリコードから Sentry API を直接呼ばない。接点は「初期化 1 ファイル」+「明示 capture 用ラッパー 1 枚」に限定し、将来の乗り換えコストを固定する。
  • Grafana を単一画面にする: Grafana Labs 公式の Sentry データソースプラグイン(Issues / Events / Org Stats をクエリ可・v2.0.0+ で Events の取得フィールド選択可・要 Grafana 10.4+・認証は Sentry Internal Integration トークン)で、ダッシュボードに Sentry issue をトレース・メトリクスと並置できる。Sentry UI ⇔ Grafana を行き来せず、Grafana 起点で調査を完結させる方向とする。
  • PII: sendDefaultPii: false を明示。リクエストボディのキャプチャは無効。

コンポーネント別実装方針

☁️

インフラ未確定への備え: デプロイ先が Cloud Run(Node ランタイム)なら OTel Node SDK がフルに動く。Cloudflare Workers ではフル SDK が動かないため Workers 用 OTel ライブラリ+Workers Logs に切り替える。手動 span と属性契約はどちらでも同一に保つ — これが原則 P1 の効きどころで、インフラ決定を待たずに計装を書き始められる。

コンポーネント実装方針
core(Next.js)OTel Node SDK+自動計装(http / pg / ioredis)。instrumentation.tsregister() から初期化。バンドラは require フックを壊し自動計装を無効化しうる点に注意
文字起こしワーカーcore と同じ SDK 構成。service.name=interview-transcriber。enqueue 時に traceparent を job payload へ同梱
minedia-www 新エンジン部opentelemetry-ruby(rails / net_http / sidekiq)。計装スコープは新エンジン部のみで、既存 OpenTok レーンには手を入れない。エラーは既存 Sentry に任せる
AI Agent(Python)opentelemetry-distro[otlp] で OTLP 直送。pre-fork 型ワーカーでは post-fork フックで provider 再初期化が必要
LiveKit Cloud計装不可。provider_inspector_url と接続テスト結果で代替。webhook の欠落は日次の整合ジョブで補足(後述)
実査フロント(ブラウザ)当面 RUM は入れない。接続テストの診断ログとサーバー側 span で代替し、導入は完全移行後に判断

共通環境変数(送信先の差し替えはここだけ)

OTEL_SERVICE_NAME=interview-core            # サービスごとに変更
OTEL_RESOURCE_ATTRIBUTES="service.namespace=interview,deployment.environment=production"
OTEL_EXPORTER_OTLP_ENDPOINT=<OTLP エンドポイント>
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <credentials>"

アラート

🎯

方針(原則 P3): 実査の同時セッション数は一桁〜十数件で、5xx が 1 件出ただけで比率指標は大きく振れる。リアルタイム検知はイベント駆動アラートが担い、比率指標は月次レビュー用の後方指標に留める。

イベント駆動アラート

Sev条件意味通知
P1 即時recording.transitions{to_status="failed"} ≥ 1納品物(録画)の毀損。現行 OpenTok の録画アラート運用の後継オンコール
P1 即時room.provisions{result="rtc_failed"} ≥ 1(実査時間帯)LiveKit 障害=実査開始不能オンコール
P1 即時/health/ready 失敗 2 回連続基盤 down。www 側フォールバック判断と連動オンコール
P2 15分entry_gate.decisions{result≠"ok"} が同一 Room で ≥ 3特定 Room に入れない参加者の滞留Slack
P2 15分webhook.exhausted ≥ 1リトライ失効 → 破棄が発生。受信側のポーリング補填を確認Slack
P2 15分transcription.queue.depth > 0 が 60 分継続 / ジョブ最終失敗文字起こしの滞留・完了通知の遅延Slack
P3 翌営業日core_client.fallbacks ≥ 1 / JWKS 取得・署名検証エラー構成劣化・鍵運用の異常Slack(低優先)
P3 翌営業日Sentry new issue / regression(interview-* プロジェクト)未知のエラーパターン出現Slack(Sentry 通知。ページングには使わない)

整合性監視(split-brain 対策)

webhook 欠落時に core と minedia-www の状態が乖離するリスクに対し、www 側に日次の突合ジョブ(ローカル参照 ⇔ core の Room 状態照合)を置き、乖離件数を interview.reconciliation.mismatches counter で出す。> 0 で P3 アラート。

バックエンドとコスト

バックエンド比較

構成評価
Grafana Cloud 推奨全サービス → OTLP gatewayインフラ選定(Cloudflare / GCP)と独立に成立する唯一の案。無料枠(メトリクス 10k series・ログ/トレース各 50GB/月)で PoC〜二重運用初期は基本料のみ。乗り換えも環境変数の差し替えだけ(P1)
GCP Cloud OperationsCloud Trace / Logging / MonitoringGCP 主軸確定なら自然。ただし Cloudflare 主軸に転んだ場合に作り直し
Cloudflare 内蔵+外部Workers Logs / Analytics Engine +外部トレース基盤Cloudflare 主軸でもトレースは外部が必要になり結局 2 系統。単独では不成立

エラートラッキングはいずれの案でも Sentry で確定(既存契約の増分利用)。バックエンドの最終決定はインフラ ADR と同時に行う。

コスト試算(Grafana Cloud Pro・月 200 インタビュー想定)

前提: 月 200 インタビュー・平均 6 参加者 × 60 分(= 1,200 参加者セッション/月)。

項目見積もり量/月超過費
基本料金(Pro)$19
トレース≤ 1GB(約 80 万 span)$0 — 無料枠 50GB の 2%。100% 保持でも余裕
ログ≤ 1GB$0 — 無料枠 50GB 内
メトリクス3k〜5k active series$0 — 無料枠 10k series 内。series 数はトラフィック非依存のため案件が 10 倍でもほぼ不変
(参考)合成監視readiness 86k 実行と仮定$0〜37 — 採用は意思決定待ち。採用時はロケーション数が乗数になる点に注意(1 ロケーションなら無料枠 100k 内)
ユーザー3 名まで込み$0〜16(5 名なら +$16)
合計$19〜75 / 月(現実的な着地 〜$35)
💡

コストが跳ねる契機は 2 つだけ: ①メトリクスのカーディナリティ事故(uuid をラベルに載せる等 — 規約で禁止済み)、②(合成監視を採用する場合)プローブ数 × ロケーション数。テレメトリ量は月 2,000 インタビューでも無料枠内であり、量を理由にサンプリングを導入する必要はない

サンプリング・保持

  • トレースは当面 100% 保持parentbased_always_on)。1 セッションの障害調査価値が高く、量も問題にならない。head sampling は「稀な障害を落とす」ため採らない。
  • 量が問題になったら tail sampling(エラー・遅延トレースは全量、正常系を絞る)へ移行。
  • 保持期間: ログ 30 日 / メトリクス 13 ヶ月 / トレース 30 日。

セキュリティ・PII

ルール対象
JWT・API key secret・webhook secret の生値をログ / span / メトリクスラベルに載せない。識別には jti / key_id を使う全サービス
display_name・email・発話 / チャット / 文字起こし本文を載せない。件数・バイト長のみ可全サービス(P5)
Authorization 等のヘッダは自動計装のキャプチャ対象から除外(デフォルト無効を維持)HTTP 計装
メトリクスラベルに高カーディナリティ値(uuid・jti)を使わない。uuid はログ・span 属性のみ全メトリクス
テレメトリ送信先の認証情報は Secret 管理(Secret Manager / wrangler secret)。環境変数直書き禁止デプロイ構成
テレメトリに PII が無いことを維持し、データレジデンシ要求が確定した場合に監視バックエンドを対象外と整理できるようにするインフラ ADR 連動

段階導入プラン

計装は後付けにせず、core の最初のエンドポイントから標準属性の契約で書き始める(contract-first はテレメトリにも適用)。

Phase対応する開発段階導入内容
Acore PoC 着手時OTel SDK 初期化・構造化ログ・標準属性・/health /health/ready・Sentry SDK 導入(tracesSampleRate: 0)。バックエンドは Grafana Cloud Free で開始
Bcore API 実装(www 側は mock 相手の契約テスト)手動 span・ドメインメトリクス・webhook への traceparent 同梱。Sentry → トレースの相関検証
Cwww 側の契約実装 → core 結合www 新エンジン部の計装・webhook の span link 接続・整合ジョブメトリクス
D二重運用開始アラート本番化・ダッシュボード 2 枚(下記)
E完全移行後ブラウザ RUM の導入判断・tail sampling の要否判断

ダッシュボード構成(Phase D で 2 枚)

1. 実査ライブ

実査運用オペレーターが当日見る画面

現在アクティブな Room 一覧・入室ゲート却下・録画状態・LiveKit provision 失敗。

2. API 健全性

開発者向け

管理API / ルーム内API / webhook それぞれのレート・エラー・レイテンシ + 配信リトライ・キュー滞留。

未決事項

#事項依存
OBS-1Webhook 配信への traceparent ヘッダ追加を通信契約(Webhook 仕様)に反映する契約改訂
OBS-2監視バックエンドの最終決定をインフラ ADR に含める(本設計は Grafana Cloud 先行開始を推奨)インフラ ADR
OBS-3オンコール体制(P1 アラートの受け手・エスカレーション)。現行の録画アラート運用の引き継ぎ先運用設計
OBS-4AI Agent(Managed Hosting)で OTLP 直送が実際に通るかの検証LiveKit PoC