对于前端应用来说,JS 错误的发生直接影响前端应用的质量,因此对于 JS 错误的定位及诊断显得尤为重要。ARMS 前端监控提供的 JS 错误诊断功能可以辅助排查 JS 错误,能够做到精准定位、快速诊断。

前提条件

前端开发人员已使用构建工具生成 Source Map。

背景信息

实际情况中,JS 错误诊断经常会遇到以下困难。
  • 复现困难

    假设您的一位用户是 A,当 A 访问某网页时,该页面会加载在 A 本地的浏览器上。由于页面的加载耗时受地域、网络情况、浏览器或者运营商等因素影响,排查问题时无法复现 A 在访问页面时的具体情况。

  • 缺少监控信息,无法深入排查

    大部分前端监控会通过 PerformanceTiming 对象来获取完整的页面加载耗时信息,这将缺失页面静态资源的加载情况,导致无法深入定位性能瓶颈。

ARMS 前端监控可利用 Source Map 还原代码真正的错误位置,还可以利用用户行为回溯功能还原 JS 错误现场。这样使得开发者能够迅速定位出错的源代码位置以及相应的代码块。

Source Map是一个存储源代码与编译代码对应位置映射的信息文件,便于开发人员定位上述代码错误。实际上,它就是一个JSON键值对,利用VLQ编码与特定的规则存储位置信息。简而言之,可以理解为Source Map在处理前的代码和处理后的代码之间搭建了一座桥梁。

在配置文件webpack.config.js中设置devtool属性即可生成Source Map文件。

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    devtool: "source-map" // devtool有13种不同取值,对应不同类型的Source Map。建议设为source-map。
};
            
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-contrib-uglify压缩打包:

grunt.initConfig({
    uglify: {
        options: {
            sourceMap: true
        }
    }
});
        

如果使用grunt-usemin压缩打包,由于grunt-usemin会依次调用grunt-contrib-concatgrunt-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');
};
ng build --prod --source-map --vendor-source-map
    

UglifyJS2是命令行工具,执行方法如下所示。单击查看更多source-map选项

$ uglifyjs app.js -o app.min.js --source-map app.min.js.map
    

SystemJS的SystemJS构建工具

builder.bundle('app.js', 'app-outfile.js', {
    minify: true,
    sourceMaps: true
});

步骤一:安装探针

如需全方位监控前端应用,那么您首先需要为您的前端应用安装 ARMS 探针。请根据实际需求选择一种方式来安装探针,详情请参见前端监控接入概述

步骤二:查看错误总览

  1. 登录ARMS控制台
  2. 前端监控页面,单击您的应用名称。
  3. 在左侧导航栏,单击 JS错误诊断
    JS Error Diagnosis
    • 通过错误总览查看总的错误数、错误率、影响用户数及占比。
    • 通过曲线图判断 JS 的错误趋势。
    • 通过高频错误筛选出频繁出现的错误。
    • 通过页面错误率排行错误分布等信息对 JS 错误有一个全局的判断。

步骤三:诊断具体错误

对全局错误信息有一个初步的判断后,还需要排查具体的错误。进入 ARMS 前端监控中 JS 错误诊断的异常诊断有以下两种方式,本文以第二种方式为例。
  • 单击高频错误页签的诊断直接进入异常诊断。
  • 在错误曲线图中下钻到某个时刻的异常洞察弹出框,再进入到异常诊断。
  1. 观察到错误曲线图中某个时刻的错误率突然变高,将鼠标悬浮于该曲线拐点上,当鼠标显示为手形指针时单击拐点,可打开该时间点的异常洞察对话框。详情请参见查看异常洞察
    Exception Insight
  2. 单击高频错误Top 5 页签,选择其中一条错误,然后单击操作列中的诊断,进入 JS错误诊断页面。

步骤四:查看错误详情

JS 错误现场信息包括首次出现时间、首次出现版本(可选上报指标)、错误名称、错误类型、设备、操作系统、浏览器、错误所在页面、错误所在文件及行列信息等。如图 1 所示,可以从错误现场信息中看出是实时大屏页面的地图模块在更新时报了一个数据非法的异常,错误发生在第 1 行第 79585 列。

图 1. 错误详情页面
Error Details

步骤五:定位错误代码

通过查看 JS 错误详情得到的信息不足以诊断问题,因为线上代码一般是经过编译、压缩、混淆等处理,因此您看到的 JS 代码不具备可读性。虽然知道是第 1 行第 79585 列发生了错误,但这并不代表是源码的实际位置。此时,就需要用到 Source Map 进行源码映射。

  1. 堆栈信息区域,单击一条堆栈信息左侧的三角形图标展开该行,并单击选择 Source Map
  2. Source Map文件对话框中选择已有 Source Map 文件,或上传新的 Source Map 文件,并单击确定
    Source Map
  3. ARMS 前端监控将利用 Source Map 文件还原准确的 JS 错误位置,如果选择的 Source Map 能够匹配出源码的错误,则原始错误位置将以红色字体标注于源代码区域中。如图 2 所示,经过解析后就可以一目了然地看出是 geog-map.js 文件里面的第 51 行出现错误。除此之外,错误堆栈的每一行都可以使用 Source Map 做源码映射。
    图 2. Source Map 源码映射
    Stack Information

步骤六:查看用户行为回溯

图 2 所示,虽然可以看到源码报错是创建地图组件时,传入了非法的数据导致的。但非法的数据可能性有很多,上述代码中实际已经对数据做了判空容错处理,但依然触发了数据非法异常。如果能对报错时的数据做一个快照,就能更准确地定位问题。实际上在全局异常捕获中,现场数据一般无法获取到,只能依赖用户主动上报时附带数据信息。

那还有没有其它办法可以辅助排查呢?最好的方式就是复现,ARMS 前端监控把页面上发生的各个事件节点定义为用户行为,包括页面加载、路由跳转、页面单击、API 请求、控制台输出等信息,按照时间顺序将用户行为串联起来就是用户的行为链路。通过出错时的行为回溯可以帮助开发者复现问题。

图 3 所示,通过出错时用户行为回溯可以看到出错前有一个 API 请求,这个 API 请求试图去请求数据实时更新地图模块,但是返回的数据是 ConsoleNeedLogin,说明此时页面已经退出登录状态了,这就是导致非法数据的根源。

图 3. 用户行为回溯
User Behavior Trace