本番環境のエラー監視をブラウザ標準のwindow.onerrorハンドラだけに頼っているプロジェクトは、意外と多く残っています。
この仕組みはエラーメッセージを拾うことはできますが、スタックトレース(エラー発生箇所の呼び出し履歴)も、リリースバージョンとの紐付けも持っていません。
本記事では、この「観測性の盲点」がなぜ生まれるのかを分解し、自分のプロジェクトが該当するかの確認方法と、Sentry(エラー監視SaaS)を使った改善の設計判断を整理します。
何が起きるか:エラーは見えているのに「同じバグ」か分からない
分析プラットフォームInsightTrackの開発チームは、window.onerrorだけでエラーを拾っていた時期、深刻な問題を抱えていました。
エラー自体はログに残るものの、それが「新しいバグ」なのか「3回前のデプロイで直したはずのバグの再発」なのかを区別できなかったのです。
複数サイトを1つのダッシュボードで管理する構成では、この問題はさらに深刻になります。
どのサイトのどのリリースでエラーが増えたのかが分からないと、顧客への説明が「エラーが増えています」という曖昧な一言で終わってしまいます。
これは単発のバグではなく、エラー観測パイプライン全体の設計不備という、アーキテクチャレベルの欠陥です。
なぜ起きるか:フィンガープリンティングとリリースコンテキストの欠落
原因を段階的に分解すると、3つの要素が欠けていたことが分かります。
1つ目は、フィンガープリンティング(同種のエラーをグループ化する仕組み)の欠如です。
スタックトレースの型やエラー発生箇所を基に同一バグを束ねる機能がないと、1つのバグが100件のエラーとして個別に表示され、ノイズと化します。
2つ目は、リリースコンテキストの欠如です。
どのデプロイでエラーが増えたか分からなければ、Regressed(再発)なのか新規バグなのかの判定は人間の推測に依存してしまいます。
3つ目は、ユーザー影響数(そのエラーが何人に影響したか)が見えないことです。
影響範囲が分からないと、優先度付けの根拠がなくなり、対応順序が「声の大きさ」で決まりがちになります。
これらはいずれも、Sentryのようなエラー監視SaaSが標準機能として持っているものです。
つまり、自作のwindow.onerrorハンドラで代替しようとした時点で、車輪の再発明に近い状態になっていたと言えます。
自分のプロジェクトが該当するか確認する方法
まず、現状のエラー監視がどのレベルにあるかをチェックしてみましょう。
# フロントエンドコード内でエラーハンドラの実装を検索
grep -rn "window.onerror\|window.addEventListener('error'" src/
# Sentry SDKが導入済みか確認
grep -rn "@sentry/" package.json上記コマンドでwindow.onerrorだけが見つかり、Sentry SDKへの依存が package.json にない場合、フィンガープリンティングやリリース追跡の機能を持たない状態と判断できます。
もう1つの確認ポイントは、デプロイ時にリリースタグを送信しているかどうかです。
Sentryを導入済みでも、CI/CDパイプライン内でsentry-cli releases newのようなコマンドを実行していなければ、リリースとエラーの紐付けは機能しません。
CIの設定ファイル(.github/workflows/*.ymlや.gitlab-ci.yml)にsentry-cliやSENTRY_AUTH_TOKENという文字列があるかも確認してみてください。
対策の手順:二重取り込みとデータ分離という設計判断
InsightTrackが採用した設計は、単にSentryを埋め込むだけではなく、データの取り込み経路を2本用意する構成でした。
1つ目はポーリング(定期的なAPI呼び出し)による取り込みです。
有限のワーカープール(同時実行数を制限した処理単位群)を用意し、5分ごとにSentry APIを叩く構成にすることで、遅いプロジェクトが全体の処理を止めてしまう事態を防いでいます。
さらに、活発なプロジェクトは頻繁に、静かなプロジェクトやエラーが出ているプロジェクトはポーリング間隔を指数的に伸ばす「適応的ケイデンス」を採用しています。
認証失敗(401エラー)が続く場合はポーリング間隔を6時間まで落とす、という具体的な劣化戦略も組まれていました。
2つ目はWebhook(イベント発生時に即時通知される仕組み)による取り込みです。
SentryのInternal Integration機能でWebhookを設定し、HMAC署名(改ざん検知のための署名方式)を一定時間で検証することで、なりすましリクエストを防ぎつつミリ秒単位で新規・再発イベントを受信します。
そして最も重要な設計判断が、書き込みと読み取りの経路分離です。
ポーリングとWebhookの両方の書き込み先はPostgreSQL(リレーショナルデータベース)に一本化し、60秒ごとにバックグラウンド処理でDuckDB(分析処理に強い組み込み型データベース)へ同期します。
ダッシュボードの表示や分析クエリは、すべてDuckDBだけから読み取る構成です。
この分離は、CQRS(コマンドとクエリの責務分離)と呼ばれる設計パターンに近い考え方です。
書き込みの信頼性が必要な処理と、高速な読み取りが必要な処理を1つのDBに詰め込むと、片方の負荷がもう片方の性能に影響します。
この構成を自分のプロジェクトに当てはめる場合、まず現在のダッシュボードクエリが本番用DBに直接投げられていないかを確認するとよいでしょう。
# アプリケーションログで分析系クエリの実行先を確認
grep -rn "SELECT.*GROUP BY\|SELECT.*COUNT" logs/query.log | grep -v "analytics_db"本番トランザクション用DBに分析クエリが混在している場合、ダッシュボード側のアクセス増加が本番機能の遅延を招くリスクがあります。
異常検知のロジックについても触れておきます。
InsightTrackでは、7日間の移動平均に対して2シグマ(標準偏差の2倍、統計的に見て通常の範囲を外れた値の目安)を超えたら異常とみなすアラートを実装していました。
この閾値設計は、固定回数のエラーで単純にアラートを出す方式より、日々のトラフィック変動に強い判定方法です。
固定閾値だと、トラフィックが多い日は誤検知が増え、少ない日は検知漏れが起きやすくなります。
導入前に確認すること
エラー監視をwindow.onerror単体からSentryのような専用SaaSへ移行する判断は、単なる機能追加ではなくアーキテクチャの意思決定です。
移行を検討する際は、次の3点を確認してみてください。
- 現在のエラーログにフィンガープリンティングとリリースタグが付いているか(
grepでSDK導入状況を確認)
- ダッシュボードの読み取りクエリが書き込み用DBと分離されているか(分析用途に別DBやレプリカを用意しているか)
- 異常検知の閾値が固定値か、移動平均ベースの統計的な閾値になっているか
これらが1つも当てはまらない場合、エラー監視は「ログを見て気づく」段階にとどまっている可能性があります。
段階的にでも、リリースコンテキストの付与とデータ経路の分離から着手すると、後々の技術的負債を抑えられます。