こんにちは!日々のエラー対応やアラート対応に追われて、「またこのどうでもいいノイズエラーか…」とぐったりしていませんか?
プロダクトを運営していると、Sentryのようなエラートラッキングツールは本当に心強い味方になります。しかし、何も考えずにデフォルトのまま導入すると、「ブラウザの拡張機能が勝手に引き起こしたエラー」や「ユーザーのネットワークが一時的に切れただけのAbortError」、さらにはうっかり画面に出てしまったパスワードやトークンといったノイズ・機密情報の濁流に飲み込まれてしまいます。
これをそのままにしておくと、本当に解決すべきクリティカルなバグが埋もれてしまい、エンジニアのメンタルもすり減ってしまいますよね。
そこで今回は、Sentryが誇る最強のカスタマイズポイント「`beforeSend` フック」を完全制覇します。TypeScriptを使って、ノイズを完全に消し去り、機密情報を鉄壁のガードでマスクする実践的なテクニックを、優しく丁寧にお伝えしていきますね。
これをマスターすれば、あなたのSentryは「ただのエラーゴミ箱」から「精鋭されたインテリジェントな監視システム」に生まれ変わり、毎日の開発・運用が劇的に楽になりますよ!
—
1. そもそもSentryと「beforeSend」の役割とは?
Sentryは、アプリケーションで起きた例外をキャッチしてクラウドに送信し、スタックトレースやコンテキストを分かりやすくまとめてくれる素晴らしいツールです。
通常、コード内で `try/catch` を抜けたエラーや、未処理の例外(Uncaught Exception)は、自動的にSentryのサーバーへと飛び立ちます。しかし、「サーバーに飛ぶ直前の最後の瞬間」に、そのエラーデータをプログラムで書き換えたり、送信自体をキャンセルできたらどうでしょうか?
それを可能にするのが、SentryクライアントSDKが提供する `beforeSend` コールバック(フック) です。
[アプリケーション内でエラー発生]
↓
[Sentry SDKがキャッチ]
↓
【 beforeSend フック 】 ← ★ここでデータを自由に料理する!
↓ (除外されなければ)
[Sentryサーバーへ送信]
`beforeSend` の中でエラーオブジェクト(Event)を検査し、
- 「あ、これブラウザ拡張機能のエラーだから捨てよう(return null)」
- 「あ、ここにクレジットカード番号が入ってるから「[FILTERED]」に書き換えよう」
といった制御をコードベースで行えるのです。それでは、実際に手を動かしながら、TypeScriptでこの強力な仕組みを実装していきましょう!
—
2. 最速セットアップとTypeScript型安全な基本形
まずは、プロジェクトにSentryを組み込み、TypeScriptで型安全に `beforeSend` を扱える状態を作ります。
インストール
お使いのパッケージマネージャーでSentryのブラウザ/Node用SDKをインストールします(今回は一般的なWebフロントエンドを想定します)。
npm install –save @sentry/browser
基本の初期化コード
TypeScriptでSentryを初期化する際、`beforeSend` の引数には `Sentry.Event` と `Sentry.Hint` が入ってきます。ここをしっかりと型定義してあげるのが、プロの仕事です。
import as Sentry from “@sentry/browser”;
Sentry.init({
dsn: “YOUR_DSN_HERE”,
// サンディー(Sentry)が送信する直前に必ずここを通ります
beforeSend(event, hint) {
// 例として、すべてのエラータイトルをコンソールに出してみる
console.log(“Sentryが捕捉したイベント:”, event.message);
// そのままイベントを返す(=送信する)
return event;
},
});
これだけでも動きますが、ここからが本番です。現場で本当によくある「困ったちゃんエラー」を撃退する高度なフィルタリングを実装していきましょう。
—
3. 実戦投入!ノイズを完全に消し去るフィルタリング実装
ここからは、実務で絶対に役立つ「3大ノイズ排除パターン」をTypeScriptで実装します。
1. ブラウザ拡張機能に起因する無意味なエラーの除外
2. ネットワーク切断やタイムアウトなど、アプリのバグではないエラーの除外
3. 機密情報(トークンやメールアドレス)の徹底的なマスク
これらをすべて網羅した、究極の `beforeSend` 関数がこちらです。
import as Sentry from “@sentry/browser”;
Sentry.init({
dsn: “YOUR_DSN_HERE”,
environment: process.env.NODE_ENV,
beforeSend(event, hint) {
// 1. hint.originalException から元の生エラーオブジェクトを取り出す
const error = hint.originalException;
// ==========================================
// パターン1: ブラウザ拡張機能起因のエラーを除外
// ==========================================
// 例: Chrome拡張機能(LastPass, Grammarlyなど)が勝手にDOMを書き換えて起こすエラー
if (error instanceof Error) {
const extensionNoisePatterns = [
“ResizeObserver loop limit exceeded”, // 描画タイミング由来のよくある無害なエラー
“chrome-extension://”,
“moz-extension://”,
];
const isExtensionError = extensionNoisePatterns.some(pattern =>
error.message.includes(pattern) || (error.stack && error.stack.includes(pattern))
);
if (isExtensionError) {
// nullを返すと、Sentryはこのエラーの送信を完全に破棄(ドロップ)します
return null;
}
}
// ==========================================
// パターン2: ネットワーク一時切断・キャンセル系エラーの除外
// ==========================================
if (error instanceof Error) {
const isNetworkNoise =
error.name === “AbortError” || // fetchのキャンセル
error.message.includes(“NetworkError when attempting to fetch resource”) ||
error.message.includes(“Load failed”); // Safari特有のネットワークエラー
if (isNetworkNoise) {
return null; // これらもアプリのバグではないのでスルー!
}
}
// ==========================================
// パターン3: 機密情報のマスキング(データ加工)
// ==========================================
// 万が一、URLクエリやメッセージにトークンが含まれていた場合をマスクする
if (event.request && event.request.url) {
// URL内の ?token=xxx などを ?token=REDACTED に置換
event.request.url = event.request.url.replace(/([?&])token=[^&]+/, “$1token=REDACTED”);
}
// エラーメッセージ本文にメールアドレスが含まれている場合の簡易マスク
if (event.message) {
event.message = event.message.replace(
/[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+/g,
“[EMAIL_REDACTED]”
);
}
// すべてのチェックを通過した、健全かつ安全なイベントだけをSentryへ送信!
return event;
},
});
このコードの美しいポイント
- `return null;` の魔法: `beforeSend` 内で `return null;` を明示すると、Sentryクライアントはそのエラーをサーバーに送信しません。通信量(クォータ)の節約にもなり、ダッシュボードが綺麗に保たれます。
- `hint.originalException` の活用: Sentryが加工してしまう前の「生のJavaScriptエラーオブジェクト」にアクセスできるため、`instanceof Error` や独自のプロパティを正確に判定できます。
—
4. 動作確認(HelloWorld)の作法
正しくフィルタリングが機能しているか、手元でテストしてみましょう。わざと「除外されるべきエラー」と「送信されるべきエラー」を発生させてみます。
適当なボタンクリックイベントなどに、以下のようなテストコードを仕込んでみます。
// テスト1: ブラウザ拡張機能風のエラー(→ 弾かれてSentryに行かないはず)
const triggerNoiseError = () => {
const err = new Error(“Uncaught Error: chrome-extension://abcdef/script.js injected”);
throw err;
};
// テスト2: 本当に重要なアプリのバグ(→ Sentryに飛ぶはず)
const triggerRealBug = () => {
const user = { id: 123, email: “secret-user@example.com” };
// 存在しないプロパティにアクセスしてTypeErrorを故意に発生させる
// @ts-ignore
console.log(user.profile.age);
};
確認の手順
1. アプリを起動し、ブラウザのデベロッパーツール(Console / Networkタブ)を開きます。
2. `triggerNoiseError()` を実行します。
- → `beforeSend` 内で `null` が返されるため、Sentryへの `envelope` 送信リクエスト(Networkタブに現れる `sentry.io` へのPOSTリクエスト)が発生しないことを確認します。
3. `triggerRealBug()` を実行します。
- → 送信リクエストが飛ぶのを確認し、Sentryのダッシュボードを見てみましょう。メッセージ内のメールアドレスが `[EMAIL_REDACTED]` に綺麗にマスクされているはずです!
—
5. 先輩エンジニアからのアドバイス:運用時の注意点
`beforeSend` は非常に強力ですが、いくつか気をつけておいてほしいアンチパターンもあります。
- 無限ループや重い処理を入れない
`beforeSend` はエラーが発生するたびに(場合によっては大量に)同期的に実行されます。ここで正規表現の多用や重い同期処理を行うと、アプリケーション全体のパフォーマンス(UIのフリーズなど)に悪影響を及ぼします。処理はシンプルかつ高速に保ちましょう。
- デバッグ時は `console.log` を活用する
「なぜかこのエラーがSentryに飛ばないな?」という時は、`beforeSend` の中に一時的に `console.log(error, event)` を仕込み、どの条件分岐に引っかかっているのかを必ずデバッグしてください。
—
まとめ
今回は、Sentryの `beforeSend` フックを活用した、TypeScriptによる高度なエラーフィルタリングと匿名化の裏技を解説しました。
- ノイズの排除: ブラウザ拡張やネットワーク一時切断のエラーを `return null` で華麗にスルーする。
- セキュリティの担保: メッセージやURLに含まれる機密情報を正規表現等でマスクする。
- 型安全な実装: `hint.originalException` を正しく型推論させ、安全にエラーをハンドリングする。
これらを整えるだけで、Sentryの通知は「本当に対応が必要な価値のあるエラー」だけになり、毎日のエラーチェックが驚くほどストレスフリーになります。
「これをマスターすれば、毎日の作業が劇的に楽になりますよ!」
ぜひ、今日のプロダクトのコードにこの `beforeSend` を組み込んで、ワンランク上の洗練されたオブザーバビリティ環境を手に入れてくださいね。あなたの開発ライフがより快適になることを応援しています!