Sentryはフロントエンド開発においてエラーログを管理する強力なツールですが、不要なエラーの送信を避けるための工夫が必要です。ここでは、Sentryでの不要なエラーのフィルタリング方法について説明します。
Sentryの組み込みフィルタの活用
Sentryはいくつかの組み込みフィルタを提供しており、これにより一般的なエラー(例えば、よく知られたブラウザ拡張のエラーや古いブラウザ、BOTからのエラー)を除外したり、glob pattern matchingを用いてエラーをフィルタリングすることが可能です。
しかし、これらのフィルタは細かい制御ができないため実運用上不要なエラーが多く送信されて、quotaの枯渇を招くことがあります。
フィルタしたい例
Sentryでフィルタができないエラーは次のようなものです。
- 特定のバージョン以前のブラウザで発生するエラー
- Seleniumなどの許可されていないクローラーによるエラー
- マイナーなブラウザ拡張のエラー
また、逆に特定のエラーの送信割合を増やしたい場合もあります。
フロントエンドでのフィルタリング方法
フロントエンドでのフィルタリングは、Sentryの公式ドキュメントで紹介されている方法を用いて実施できます。
拒否リストを使って宣言的に定義したり、イベントオブジェクトの内容を参照してクライアント側で送信するかどうかを制御することも可能です。
Sentry.init({
ignoreErrors: [
// 例: ランダムなプラグインや拡張機能のエラー
"top.GLOBALS",
"originalCreateNotification",
// ...
],
denyUrls: [
// 例: Facebookのエラー
/graph\.facebook\.com/i,
/connect\.facebook\.net\/en_US\/all\.js/i,
// ...
],
// クライアントでイベントを参照する方法
beforeSendTransaction(event) {
// ここでイベントを変更または除外
if (event.transaction === "/unimportant/route") {
return null; // nullを返すとSentryに送信しない
}
return event;
},
});
デメリット
この方法には以下のようなデメリットがあります。
- フィルタ条件が複雑になると、クライアントがダウンロードするファイルサイズが大きくなり、ユーザー体験が悪化する
- フィルタ条件をユーザーに知られたくない場合がある
- フィルタ条件を変更したコードをデプロイしてから、ユーザーのクライアントに適用されるまでにタイムラグが生じる。この間、古いページを利用しているユーザーから不要なエラーが送信される可能性がある。
バックエンドでのフィルタリング方法
Sentryにエラーを送信する前に自前のAPIでチェックする方法もあります。
Sentry.init({
// ...
beforeSend(event, hint) {
return fetch('/api/error/filter', {
body: JSON.stringify({
exception: event.exception?.values ?? {}
}),
headers: {
"Content-Type": "application/json",
},
method: "POST",
})
.then((res) => {
return res.ok ? event : null; // APIが200を返した場合のみエラーを送信
})
.catch(() => null); // フィルタAPIの送信に失敗した場合はエラーを送らない
},
});
エンドポイント POST /api/error/filter
はスタックトレースを受け取り、重要なエラーの場合に200を返します。このAPIは各サービスごとに実装してください。
デメリット
この方法にもデメリットがあります。
- フィルタAPIの送信に失敗するとエラーが送られない
-
.catch(() => null);
を.catch(() => event);
に変更すれば、フィルタAPIの送信に失敗した場合に常にエラーを送信するように設定できます。
-
- エラーが送信された直後にページが閉じられると、エラーが送信されない
- ユーザーから送信されたスタックトレースにセンシティブな情報が含まれていても、サーバーのログに残さないように注意が必要(Sentryは外部サイトへの通信ログをexceptionに含まないため、行儀の悪いブラウザ拡張を使用していない限り、センシティブな情報を受け取ることはほとんどありません)。
どう組み合わせるか
モイではまずクライアントで明らかに不要なエラーを除外して対象の件数を減らし、サーバー側でより詳細に調査するという方針にしています。
クライアント側のフィルタリング:
- サンプリングレートを設定して件数を絞る
- 発生件数が多いブラウザ拡張のエラーを弾くようにdeniedUrlsに設定する
サーバーサイドのフィルタリング:
- 古いコードで起きているエラーや詳細な判定でエラーを弾く
Sentryの設定
- デフォルトのフィルターをON
- 緊急に停止したいエラーをフィルタに設定する
- エラーが来た時はSlackに通知
- Spike Protection を設定して大量にエラーが来た時に気づけるようにする
まとめ
この記事では、Sentryに重要なエラーだけを送信するために、サーバーサイドでログを送信するかどうかを制御する方法を紹介しました。実装の参考にしていただければ幸いです。