Sentry だけに Production 環境のソースマップを表示させる
JavaScriptProduction のように、Sentry だけにソースマップを読んでほしい環境がある時の備忘録。
環境、サンプル
- Node v14.5.0
- webpack 5.25.0
- @sentry/webpack-plugin 1.14.2
- @sentry/react 6.2.2
- SaaS 版 Sentry
サンプルコードはこちら
Sentry ソースマップの扱い
ブラウザでソースマップを認識するには、バンドル内にリファレンス用のコメント(pragma)を含めるか、Sourcemap
または X-SourceMap
ヘッダーを追加する必要がある。
Use a source map - Firefox Developer Tools | MDN
SourceMap - HTTP | MDN
一方 Sentry ドキュメントの Source Maps for JavaScript によれば、スタックトレース内の URL をスクレイピングして、ソースマップを自動的に Fetch する、とある。
このソースマップは、 Sentry に Artifact としてアップロードするもの。
「ソースマップを出力しながら参照はさせない」という方法については、webpack の devtool オプションで指定するソースマップのタイプに、hidden-
prefix をつけることで可能。
webpack.prod.config
// 〜〜省略〜〜
devtool: 'hidden-source-map',
// 〜省略〜
これで、ソースマップを出力するが、バンドルファイルにはリファレンスを記載しなくなる。
だから hidden-sourcemap
で出したソースマップを、Sentry にアップロードすれば終わり。
と思っていたがうまくいかない。
ソースマップのアップロード方法
まず、ソースマップを Sentry にアップロードする方法について書き残す。
あらかじめ、ここでトークンを発行しておく。
そして、ドキュメント を参考に、以下のような環境変数を ~/.sentryclirc
や ~/.env
に埋め込んでおく。
- SENTRY_AUTH_TOKEN (発行したトークン)
- SENTRY_ORG (organization)
- SENTRY_PROJECT (project)
sentry-cli には、ソースマップのアップロード向けコマンドがある。
自動的に指定フォルダ内のソースマップを確認し、ソースマップ対象のバンドルファイルと一緒にアップロードしてくれる。
バンドル自体はアップロードしなくていいものの、Sentry 上で正確に表示されなくなるのでほぼ必須。
また、アップロード先の release の名前は Sentry.init()
に渡しているものと揃える。
shell
# dist にある場合
# SENTRY_AUTH_TOKEN, SENTRY_ORG などの env が渡っている場合
# "my-project-バージョン" という release にアップデートする場合
sentry-cli releases files my-project-$npm_package_version upload-sourcemaps ./dist/
また、今回は webpack でバンドルするので、CLI をラップした sentry-webpack-plugin が使える。
webpack.prod.js
const SentryCliPlugin = require('@sentry/webpack-plugin')
// 〜〜省略〜〜
plugins: [
new SentryCliPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: 'my-sentry-arg',
project: 'my-project',
release: `my-project-${process.env.npm_package_version}`,
include: './dist',
}),
],
// 〜〜省略〜〜
ソースマップがない時のトレース。何も見えない。
ソースマップがある時のトレース。コードが見える。
これを hidden-source-map
で実現しようとして、少し困ったという話。
hidden-source-map でうまくいかない時
A. ディレクトリ構造を一致させる
よく見るミスが、Artifact のディレクトリ構造が違うこと。
Sentry フォーラムの関連スレ によれば、Sentry の Artifact と、ページで読み込まれる JS のディレクトリ構造が一致していなければ、正常に動作しない。
sentry-cli のデフォルトでは、 Artifact のアップロード先が ~/
となる。
例えばバンドルファイルの出力先が ~/js/
にあるなら、sentry-cli 側でも --url-prefix
オプションを付けて、~/js/
にアップロードする必要がある。
ただし今回の場合、Artifact と JS のパスは同じ ~/*.js
になっているのでこれは当てはまらない。
B. 拡張子を min.js
に変更する
Sentry や sentry-cli のテストケースを見ると、どうも min.js
を期待しているようなので、雰囲気で拡張子を min.js
にしたら読み込めるようになった。
webpack.common.js
// 〜〜省略〜〜
output: {
filename: '[name].min.js',
},
// 〜〜省略〜〜
upload-sourcemap を見ても、確かに foo.min.js
を指定するように書かれていて foo.js
じゃなかった。
そして、このコメント や このコメントを見ても、.min.js
にすることで正しく動いている様子。
これがバグか仕様か判断が付かず Issue を書いていたが、1回 min.js
で通すと bundle.js
でも .js
でも通ってしまうし、再現できなくなって困っているところ。
C. hidden なしで出力して、コメントだけ消す
A がダメで B も難しそうな時のワークアラウンド。
hidden-
をつけず、普通のソースマップを出力する。
そして、sed
など使って、バンドルの末尾にあるソースマップのコメントを消す。
こちら にいい感じのコマンドがあったので参考にした。
shell
find 'dist' -type f \( -iname \*.js \) -exec sed -i -E 's/\/(\/|\*)# sourceMappingURL=[^ ]*\.js\.map(\*\/)?//' {} +
#※Mac(BSD)の場合はこう
find 'dist' -type f \( -iname \*.js \) -exec sed -i "" -E 's/\/(\/|\*)# sourceMappingURL=[^ ]*\.js\.map(\*\/)?//' {} +
おまけ1: ソースマップの削除
hidden-source-map
は参照こそ消すものの、ファイル自体は残される。
Sentry にアップロード後は不要なので、rm
や rimraf
で消しておく。
shell
rm -rf ./dist/*.js.map
おまけ2: 古い Artifacts の削除
[contenthash]
などを付けてバンドルしている場合、ハッシュが変わるたびに新しい Artifact が生成される。
肥大化するので、アップロード前に初期化しておいた方が良さそう。
現状 sentry-webpack-plugin ではこの機能にアクセスできないので、sentry-cli を使う。
shell
# SENTRY_AUTH_TOKEN, SENTRY_ORG などの env が渡っている場合
# "my-project-バージョン" というリリース名にする場合
npx @sentry/cli releases files my-project-$npm_package_version delete --all
終わり
Sentry でソースマップをうまく読み込めない、という報告が無限にあるみたい。
ドキュメントにトラブルシューティングがあるので、困った時に読んでみると良さそう。
Troubleshooting for JavaScript | Sentry Documentation