「フロントエンドのエラー発生箇所を楽に特定」SentryでSource Mapを活用する
はじめに
「Sentryでエラー通知を受け取ったが、バンドルされたコードでエラー箇所が書いてある。
ソースコードのどこなのかパッとわからない、、」
と感じたことのある方は多いのではないでしょうか?
このような問題を解決する技術・ツールとして「Source Map」というものがあります。
SentryでSource Mapを活用すると、Issueを確認するとき
バンドル後のエラーの発生箇所とソースコードの対応が非常に確認しやすくなります。
導入前(ビルド後のファイル名のみ表示)
導入後(ビルド前・ソースコードのファイル名・内容)
ただ、「すぐできるかな」と思っていた導入で案外ハマってしまったところがありました。
そんなわけでこの記事では
- 導入の背景
- Source Mapの簡単な解説
- 導入方法・結果
- ハマったところ
をまとめました。
背景
普段の開発においてフロントエンドのバンドルツールとして、Webpackを利用しています。
また、エラートラッキングツールとしてSentryを利用しています。
Sentryは当初バックエンドの開発(Ruby)にて利用していました。
便利だったのでフロントエンドでも活用するよう利用範囲を広げた、というのが利用開始の背景です。
課題点
とりあえずの導入はこの辺りのドキュメントを読みながらスムーズに行うことができます。
ただ、バンドルツールなどを使っている場合には
「何のエラーが発生したか?」は分かりますが、「ソースコードのどこでエラーが発生したか」が分かりづらい状態になります。
バンドル後のファイル名のみが記載される(ソースコードのどこだ、、これ、、?)
Source Mapについて
先の課題は
ことに由来して発生しています。
(「変換」は「トランスパイル・コンパイル」などと呼ばれます。)
この問題を解決するのが「Source Map」です。
A source map is a JSON file format that maps between minified or transformed code received by the browser and its original unmodified form, allowing the original code to be reconstructed and used when debugging.
ソースマップは、ブラウザーが受信した縮小または変換されたコードと元の変更されていない形式をマップする JSONファイル形式であり、デバッグ時に元のコードを再構築して使用できるようにします。
まさしく今回のケースにぴったりです。
少し話題がそれますが、Source MapをいじるときはDevelopper Toolsを利用することになると思います。
この辺りの記事を先に目を通しておく良いかもしれません。
Sentry Webpack Pluginとは?
Upload your source maps with our webpack plugin.
記載の通り、「Source Map(*.js.map)」をSentryにアップロードするために必要なプラグインです。
「変換後のファイル(ビルド後の生成物)から、変換前のファイル(普段開発でいじるファイル)の参照」を可能とするために、Sentryは
- 変換後のファイル
- Source Map
にアクセスできる必要があります。
「1」は公開されているはずなのでアクセスは可能(なはず)です。
「2」は該当のSource MapをSentryにアップロードする必要があります。
それらの手助けしてくれるプラグインが「Sentry Webpack Plugin」です。
Source Map公開のリスク(不都合)
「普段の開発時のフォルダ構成などが分かる」状態になります。(「秘密鍵をアップする」と言う類のまずさではないですが)
バンドル前の構成が全て見える
この辺りは「意味あるの?別にいいんじゃない?」的な意見もあるようです。
個人的には、この辺りについては
「公開しなくていい情報は、公開しない」
と考えているので、公開しないようにしています。
Source Mapの設定方法
詳しくは前述の記事などをご覧頂きたいのですが、Source Mapでは
「変換後のファイルの中に、Source Mapがどこにあるかの情報」
を埋め込んでいます。(変換後のファイルに、ソースコードを埋め込むオプションもあります)
このため、以下の2つの作業を行う必要があります。
- Source Mapの作成
- 変換後のファイル(ビルドの成果物)への、「Source Mapがどこにあるかの情報」の埋め込み
バンドルツールを使っていると、この辺りはいい感じにやってくれます
Webpackの設定はこの辺りを参考にします。
JavaScriptでWebpackの設定ファイルを書く場合は、こんな感じになります。
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js',
},
+ devtool: 'source-map',
};
Source Mapの設定方法(+Sentry Webpack Plugin)
この辺りを参考にします。
const path = require('path');
const webpack = require('webpack');
+ const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");
module.exports = {
mode: 'production',
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js',
},
devtool: 'source-map',
+ plugins: [
+ sentryWebpackPlugin({
+ org: ご自身のものを設定してください,
+ project: ご自身のものを設定してください,
+ authToken: ご自身のものを設定してください(process.env.SENTRY_AUTH_TOKENなど),
+ debug: true,
+ sourcemaps: {
+ filesToDeleteAfterUpload: [ご自身のものを設定してください("./public/frontend/**/*.map" など、.mapのファイルが生成されるパスをglobで指定)],
+ }
+ })
+ ]
};
先の「Source Mapを公開しない」話の関連では、「filesToDeleteAfterUpload」が肝になります。
この設定により、Webpackにてビルドが完了すると.mapを削除してくれます。
CI/CD等でビルドする際に、コマンド走らせてrm -rfする感じですね。
エラーを確認する
window.testSentryError = () => {
try {
// タイムスタンプを含めることで毎回ユニークなエラーメッセージを生成
const timestamp = new Date().toISOString();
throw new Error(`Sentryテストエラー [${timestamp}]`);
} catch (e) {
console.error("エラーをSentryに送信:", e);
Sentry.captureException(e);
return "エラーを送信しました";
}
};
適当なファイルをビルド対象に含めておき、ブラウザで実行します。
Sentryを見てみると、変換前のコードが表示され(Unminify)ソースコードとの対応関係が取りやすくなっていることがわかります。
ハマった点とは?
先ほど、
「公開しなくていい情報は、公開しない」
と記載しましたが、これに従うなら
変換後のファイルへの、「Source Mapがどこにあるかの情報」の埋め込み
に対する情報もできれば削りたいところです。
Webpackだと、devtoolの「hidden-...」にこの情報を削ってくれるオプションが複数あります。
私もこの「hidden-...」オプションを使っていたのですが、Source Mapをうまく読み込んでくれない状態になりました。
後述する「Debug ID」がビルド後のファイルにも含まれていることを確認したりしましたが、どうにも認識してくれなかった形です。
Sentryのサポートにも連絡を取ってやり取りをしましたが、解決には至りませんでした。
運よく(?)読み込みができた場合もあったのですが、エラーの発生箇所とソースコードの対応が確認できず結果的にhidden-...オプションではなく、source-mapを使うこととしました。
この際.mapをしっかり処理することで、先のリスクは最小限にするという方針を取りました。
調査に参照したリソース(抜粋)
ちなみにSource Mapに関連して「Debug Id」というもの(今回でいう、変換前後の対応をとる仕組み)があり、これらも見ると面白いと思います。
まとめ
Sentry Webpack Pluginの導入により、フロントエンド周りのエラーの確認・修正が非常に楽になりました。
他にもSentryでよく使っている機能が多くあるので、またの機会にまとめていきたいなと思います。
ここまでご覧頂きありがとうございました!
Discussion