本番環境の JavaScript エラーのデバッグは困難です。コードはミニファイされており、各ユーザーのデバイス、ブラウザ、ネットワーク環境も異なります。従来のブラウザ監視手法の多くは PerformanceTiming オブジェクトに依存しており、ページ全体の読み込み時間のみを取得し、静的リソースの読み込み時間は含まれないため、パフォーマンスのボトルネックを特定することが困難です。Application Real-Time Monitoring Service (ARMS) のブラウザ監視は、ソースマップのサポートとユーザー行動のバックトラッキングを組み合わせることで、ミニファイされたスタックトレースを元のソースコードにマッピングして、エラーをトリガーしたユーザーの正確な操作を再現できます。
前提条件
開始する前に、以下の準備が整っていることを確認してください。
ソースマップの仕組み
ソースマップは、ミニファイされたコード内の位置を元のソースコード内の対応する位置にマッピングする JSON ファイルです。VLQ エンコーディングを使用して位置データをコンパクトに保存します。ソースマップを使用することで、ブラウザ監視は「1 行目、79585 列目」のエラーを、元のソースコード内の正確なファイル、行、列に変換できます。
診断ワークフロー
以下のワークフローでは、エラーの傾向を特定し、ミニファイされたスタックトレースをソースコードにマッピングして、エラーをトリガーしたユーザー操作を再現するまでの一連のプロセスを説明します。
ステップ 1: エラー概要の表示
ARMS コンソールにログインします。
左側メニューで、 を選択します。
[Browser Monitoring] ページで、上部メニューでリージョンを選択し、管理するアプリケーションの名前をクリックします。
左側メニューで、[JS Error Diagnosis] をクリックします。
このページでは、以下の情報が表示されます。
[Error Overview] セクションには、エラーの総数、JS エラー率、影響を受けるユーザー数とその割合が表示されます。
曲線グラフには、時間経過に伴うエラーの傾向が表示されます。
[Frequent Errors] タブには、頻度の高いエラーがリスト表示されます。
[Page Ranked by Error Rate] タブと [Error View] タブには、ページごとのエラー分布が表示されます。
ステップ 2: 特定のエラーの詳細確認
エントリーポイントは 2 つあります。
[Frequent Errors] タブで、対象のエラーの横にある [Diagnose] をクリックします。
曲線グラフで、特定の時刻のデータポイントをクリックして、[Exception Insight] ダイアログボックスを開きます。
以下の例では、曲線グラフを使用したアプローチを示します。
曲線グラフで、エラー率が急上昇しているポイントを特定します。変曲点にカーソルを合わせてポインターが手のアイコンに変わったら、クリックします。[Exception Insight] ダイアログボックスが表示されます。詳細については、「例外インサイトの表示」をご参照ください。
[Frequent Errors Top 5] タブをクリックし、エラーを選択して、[Operation] 列の [Diagnose] をクリックします。[Error Detail] タブが表示されます。
ステップ 3: エラー詳細の確認
エラー詳細ページには、以下のコンテキスト情報が表示されます。
| フィールド | 説明 |
|---|---|
| 最初の発生時刻 | エラーが最初に記録された時刻 |
| 初回発生時のバージョン | エラーが最初に発生したときのアプリケーションバージョン (オプション) |
| エラー名と分類 | JavaScript エラーの名前と分類 |
| 発生時刻 | このエラーインスタンスが発生した時刻 |
| デバイス、OS、ブラウザ | エラーが発生したクライアント環境 |
| IP アドレス、リージョン | ユーザーのネットワーク位置 |
| 接続タイプ | ネットワーク接続 (Wi-Fi、4G など) |
| エラー URL | エラーがトリガーされたページの URL |
| アプリケーションバージョン | デプロイされたアプリケーションのバージョン |
| ファイル、行、列 | ミニファイされたファイル内の位置 |
例: リアルタイムダッシュボード上のマップモジュールからのエラーを例に考えます。このモジュールは、更新中に無効なデータを報告し、スタックトレースはミニファイされたバンドルの 1 行目、79585 列目を指しています。
「1 行目、79585 列目」は、本番環境のコードがミニファイされているため、対処可能な情報ではありません。次のステップでは、この位置を元のソースコードにマッピングします。
ステップ 4: エラーのソースコードへのマッピング
スタックトレース内の行番号と列番号は、元のソースファイルではなく、ミニファイされたコードを指しています。ソースマップを適用して、元のエラー位置を特定します。
[Stack Info] セクションで、スタックフレームの左側にある
アイコンをクリックして展開し、[Choose Sourcemap] をクリックします。[Sourcemap File] ダイアログボックスで、既存のソースマップファイルを選択するか、新しいファイルをアップロードし、[OK] をクリックします。
説明一度に最大 5 つのファイルをアップロードできます。
ソースマップを適用すると、ブラウザ監視は [Source Code] セクションで元のエラー位置を赤色でハイライト表示して、エラーが発生した正確なファイルと行を示します。エラースタック内の各フレームにソースマップを適用して、完全なコールチェーンをトレースします。
ステップ 5: ユーザー行動のバックトラッキングによるエラーの再現
ソースマップのマッピングにより、エラーがどこで発生したかは明らかになりますが、なぜ発生したかは必ずしも明らかにはなりません。マップモジュールの例では、ソースコードのマッピングにより、コンポーネント作成時に無効なデータがエラーの原因であることが示されました。しかし、ソースコードにはすでにこのデータに対する null チェックとフォールトトレランスが含まれています。無効なデータが発生した理由を理解するには、エラーの直前に何が起こったかを確認する必要があります。
ブラウザ監視は、ユーザー行動を時系列のイベントノードのトレースとして記録します。
ページの読み込み
ルート変更
ページのクリック
API リクエスト
コンソール出力
このトレースを確認して、エラーにつながった一連の操作を再現します。
例: 以下のユーザー行動トレースでは、エラーの直前に API リクエストが発生しています。この API 呼び出しはマップモジュールのリアルタイム更新をリクエストしましたが、レスポンスは有効なマップデータではなく ConsoleNeedLogin を返しました。これは、ユーザーがページからログアウトしていたことを示しており、これが無効なデータの根本原因です。
ソースマップの生成
診断ワークフローのステップ 4 には、ソースマップが必要です。ビルドツールでソースマップを生成して、ARMS コンソールにアップロードします。
ソースマップファイルをアップロードするには、[Browser Monitoring] ページでアプリケーションを見つけ、[Actions] 列で [More] > [Settings] を選択します。設定ページで、[Advanced] タブをクリックします。
Webpack
webpack.config.js で、devtool プロパティを "source-map" に設定します。 Webpack は、さまざまな種類のソースマップに対応する 13 の devtool 値をサポートしています。 "source-map" は、完全で独立した .map ファイルを生成するため、本番環境のエラー診断に推奨されます。
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: "source-map"
};Gulp
gulp-sourcemaps パッケージを使用します:
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
gulp.task('javascript', function() {
gulp.src('src/**/*.js')
.pipe(sourcemaps.init())
.pipe(sourcemaps.write('../sourcemaps'))
.pipe(gulp.dest('dist'));
});Grunt
grunt-contrib-uglify のみを使用する場合:
grunt.initConfig({
uglify: {
options: {
sourceMap: true
}
}
});grunt-usemin を使用する場合 (grunt-contrib-concat と grunt-contrib-uglify を呼び出します):
grunt.initConfig({
concat: {
options: {
sourceMap: true
}
},
uglify: {
options: {
sourceMap: true,
sourceMapIn: function(uglifySource) {
return uglifySource + '.map';
},
}
}
});grunt-jsmin-sourcemap を使用する場合:
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-jsmin-sourcemap');
grunt.initConfig({
'jsmin-sourcemap': {
all: {
src: ['scripts/script.js'],
dest: 'scripts/script.jsmin-grunt.js',
destMap: 'scripts/script.jsmin-grunt.js.map'
}
}
});
grunt.registerTask('default', 'jsmin-sourcemap');
};Angular CLI
ng build --prod --source-map --vendor-source-mapUglifyJS2
UglifyJS2 は CLI ツールです。その他のオプションについては、「CLIソースマップオプション」をご参照ください。
uglifyjs app.js -o app.min.js --source-map app.min.js.mapSystemJS
SystemJS Builder を使用します。
builder.bundle('app.js', 'app-outfile.js', {
minify: true,
sourceMaps: true
});