背景
弊社ではフロントエンドのエラートラッキングにSentryを導入していたのですが、ほとんど運用されていませんでした。
フロントエンドで重大なエラーが発生することは稀であり、エラー調査においてSentryが必須な場面がほとんどなかったためです。
ただし、小さなバグやユーザーからの問い合わせは定期的に発生しており、その調査を行う際にSentryがあった方が便利だと感じる場面は多々ありました。
そのため、Sentryを適切に活用しようと思ったのですが、現状を整理したところ以下の課題がありました。
- SourceMapsがなく、エラー箇所のコードが読みにくい
- エラーが解決されずに放置され、Issueに大量に溜まっている
- 頻繁に発生するエラーがクォーターを多く消費する
- ユーザーがら問い合わせがあった際に、エラーの再現が難しい(これは可能であれば改善したい課題)
上記の課題を解決するために調査・実施したことを紹介します。
動作環境
- Nuxt(SPA): v3.8.1
- sentry/tracing: v7.38.0
- sentry/vite-plugin: v2.16.1
- sentry/vue: v7.108.0
1. SourceMapsの導入
nuxi generate
時に、SourceMapsを生成するようにします
sourcemap: {
server: true,
client: true
}
assets
にビルドしたコードと生成したSourceMapのディレクトリのパスを指定します。
filesToDeleteAfterUpload
を指定することで、Sentry送信後にSourceMapsを削除します。これにより、SourceMapsがホスティングされて外部に公開されるのを防ぎます。
plugins: [
sentryVitePlugin({
sourcemaps: {
assets: [
'.nuxt/dist/client/_nuxt/*.js',
'.nuxt/dist/client/_nuxt/*.js.map'
],
filesToDeleteAfterUpload: [
'.nuxt/dist/client/_nuxt/*.js.map',
],
}
})
]
ただ、弊社の環境ではfilesToDeleteAfterUpload
を使用してもSourceMapsが削除されなかったので、CI上でホスティング前にSourceMapsを削除する処理を追加しました。
SourceMapsの導入することでコードの可読性が上がり、エラー調査がやりやすくなりました。
エラーの整理
大量に溜まっているエラーを整理するために、以下を実施しました。
- エラー内容を確認し、恒久的に発生しないよう修正する
- エラーの修正が難しい場合
- 重要度の低いエラーや発生頻度の低いエラーはPriorityを下げるか、一旦アーカイブする
- ノイズになる場合や発生頻度が高くクォーターを大量消費する場合は、Sentryに送信しないようにする
- ライブラリ内で発生した例外は原因の特定が難しく、かつノイズになるので送信しないようにしました
-
4xx
系のエラーやネットワークエラーはノイズになるため、送信しないようにしました
エラーを恒久的に発生させないようにするのが最も良いので、1つ1つのバグを潰すような作業を1-2ヶ月かけて実施しました。
null(undefined)の値をメソッドチェーンで参照しようとして発生したエラーが多い印象でした。再現が難しく解決に時間を要するものもありました。
エラーを送信しないようにする方法
Sentryでエラーを送信しないようにする方法は2つあります。
-
エラーの送信前にフィルタリングするコードを書く(
beforeSend
) - Business plan以上の場合、Delete & Discardで画面上からエラーを送信しないようにできる
弊社はチームプランなので、エラー送信前にフィルタリングする方法を採用しました。
送信前のエラーに対して操作できるbeforeSend関数
第一引数がevent
、第二引数がhint
です。eventとhintの2つの引数があり、それぞれエラーやイベントなどの情報が入っています。
beforeSend
を使って、送信前にエラーオブジェクトに対して操作できます(以下のコードは公式ドキュメントより引用
Sentry.init({
dsn: "https://1e30cec9a9124eb6a66f70a799cee948@o360365.ingest.us.sentry.io/3631418",
// Called for message and error events
beforeSend(event) {
// Modify or drop the event here
if (event.user) {
// Don't send user's email address
delete event.user.email;
}
return event;
},
});
エラーを送信しないようにするためには、beforeSend
の戻り値をnullにします。
Sentry.init({
dsn: "https://1e30cec9a9124eb6a66f70a799cee948@o360365.ingest.us.sentry.io/3631418",
// Called for message and error events
beforeSend(event) {
// Modify or drop the event here
if (event.user) {
// Don't send user's email address
delete event.user.email;
}
return null;
},
});
beforeSend
を使用して、特定のエラーのみ送信しないようにすることが可能です。
例えば、ノイズになるため4xx
系のエラーやネットワークエラーは送信しないようにしました。
Sentry管理画面から見ることのできる情報
Issue
エラーはIssueと呼ばれます。Sentry管理画面のIssues画面から確認できます。
ステータスについては、Issueのステータス一覧に詳しい説明が記載されています。
新しいエラーが発生するとUnresolved
でIssueが作成されます。エラーを修正した場合は画面からResolved
にステータスを変更します。
緊急性が低いもの、またはノイズになるエラーはArchived
に変更します。Archived
にするとアラートを停止します。短時間に大量のエラーが発生するとEscalating
になります。
Issueを完全に削除した場合は、同様のエラーが再発すると新しいエラー扱いになります。
Issue Grouping
Sentryはエラーの類似性を評価し、同様のエラーを同じIssueにグルーピングしています。
fingerprint
→stack trace
→exception
→message
の順でグルーピングに使用されます。後者に行くほどグルーピング精度は下がります。
どの方ほグルーピングされたかは、Issue詳細ページの一番下で確認できます。
Activity
Issueに対してコメント可能です。
Environments
環境tagを設定し、どの環境で発生したエラーか区別できます。
Releases
ソースコードにバージョンをつけることができます。
どのバージョンのソースコードで発生したエラーか区別できます。
Session Replay
Session Replayを導入すると、ユーザーの行動(画面録画、ネットワークリクエスト、DOMイベントやコンソールログ)を記録し再生することができます。
エラーを手元で再現しやすくしたり、ユーザーが操作に戸惑っている箇所を確認しUI/UX向上に役立てることができます。
ユーザーのエラーの再現が今回の目的なので、エラー発生時のみSession Replayを記録するようにしました。
Set up
こちらを参考に、以下のような設定にしました。
import * as Sentry from "@sentry/vue";
Sentry.init({
replaysSessionSampleRate: 0.0, // 正常時の記録は不要なので0.0
replaysOnErrorSampleRate: 1.0, // エラーを記録したいので1.0
integrations: [
Sentry.replayIntegration({
maskAllText: true, // テキストは個人情報を含むのでマスキング
blockAllMedia: true, // img、svgやvideoなどは全て記録しない
networkCaptureBodies: false // リクエスト・レスポンスボディは記録しない
}),
],
});
細かい設定がいろいろできるので、詳細は以下を参照してください。
https://docs.sentry.io/platforms/javascript/session-replay/configuration/
privacyについて
Session Replayでは、ユーザーが入力したパスワードや個人情報を記録しないようにできます。
Masking、Blocking、Ignoringの3種類あります。
Masking
- テキストをマスキングする
- ユーザーが入力したテキストに限らず、画面に表示されているテキストも対象
- defaultで
*
でマスキング
Blocking
- 指定した要素ごとマスキングする
- マスキングされると、空欄スペースになる
Ignoring
- フォームのみ指定可能
- 再生した時に、inputフィールド内に文字が出てこなくなる
class属性などを指定して、個別にマスキング対象を指定可能です
以下を参照ください。
https://docs.sentry.io/platforms/javascript/guides/vue/session-replay/privacy/#privacy-configuration
request・response body はデフォルトでは収集しません
収集するのは、URL、request・response body size、method、status codeです。
収集する項目をカスタマイズするには、networkDetailAllowUrls
やnetworkCaptureBodies
を設定します。
request・response bodyを収集する場合でも、クレジットカード情報、社会保障番号、パスワードなどSentryがパターンマッチで検知してScrubしてくれます。
送信前の録画に対して操作できるbeforeAddRecordingEvent関数
beforeAddRecordingEventを使用することで、画面録画から個人情報を削除したり特定の録画イベントを無視することができます。
現在の弊社の運用ルール
フロントエンドのアラート内容は玉石混合で、全てを解決するのは現実的ではありません。発生期間、priorityやステータスなどで適切にラベリングし、優先順位をつけて対応するようにしました。
ノイズになるため、エラーが発生してもリアルタイム通知はしていません。ただし重要度の高いエラーを見逃してしまう可能性があるため、短時間に大量のエラーが発生した場合は通知する予定です。