前端错误与异常数据上报 - WGTracker

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

前言

众所周知,在后端的事物处理中。一旦有任何异常对应的开发同学就马上收到报警,并且第一时间处理

但是对于前端来说,往往是实际用户那里的脚本报错后才知道页面出现异常,往往这这时候已经是故障了

为了让前端也能和后端一样,需要将线上的 JavaScript 代码监控起来,当用户端浏览器出现异常前端第一时间被通知到

异常类型

  • JavaScript异常错误:JS代码在执行层面造成的页面逻辑错误和边界值溢出等
  • 主动上报异常:研发同学在开发过程中对代码块容错后主动抛出异常
  • 接口调用错误:api接口除了响应值200意外的所有异常请求错误

JavaScript异常错误

正常情况下,对于JS的异常错误,可以通过全局监听异常来捕获,通过window.onerror或者addEventListener,看以下例子:

window.onerror = function(errorMessage, scriptURI, lineNo, columnNo, error) {
  console.log('errorMessage: ' + errorMessage); // 异常信息
  console.log('scriptURI: ' + scriptURI); // 异常文件路径
  console.log('lineNo: ' + lineNo); // 异常行号
  console.log('columnNo: ' + columnNo); // 异常列号
  console.log('error: ' + error); // 异常堆栈信息
  // ...
  // 异常上报
};
复制代码

image.png

image.png

通过window.onerror事件,可以得到具体的异常信息、异常文件的URL、异常的行号与列号及异常的堆栈信息,再捕获异常后,统一上报至我们的日志服务器(目前我们暂存本地localStorage)

亦或是,通过window.addEventListener方法来进行异常上报,道理同理。

onError 在 IE6 开始就支持了,所以 WGTracker 的主动采集是使用的 onError。

onerror常见问题 - 跨域JS脚本无法准确捕获异常

原因是浏览器的同源性策略(CORS),在高级浏览器中如果浏览器捕获到了错误信息,如果 JS 文件所在的域名(如:172.28.0.7)和当前的页面地址(如:127.26.0.9)是跨域的,那么浏览器会把 onError 中的 msg 替换为 script error,其余字段也会做替换。

目前我们的资源是在同源域名下,但是难保以后会出现跨域的情况,这里也需要为将来出现的情况做一下特殊处理:

webkit 的源代码

Script error 这个问题也是目前大家最诟病的一个问题:当报警发生之后,我们只知道有问题,但是很难知道哪里出了问题。

其实是有解决方案的:

  • 首先 JavaScript 请求的 http 返回头上需要加上一个 Access-Control-Allow-Origin 头。

image.png

  • 在引入 JavaScript 文件的时候需要在 script 标签上添加 crossorigin 属性,在加这个属性前请一定确保上一条已经完成,否则 JavaScript 文件不会执行!!!。
<script type="text/javascript" src="/public/theme/less.min.js" crossorigin></script>
<script type="text/javascript" src="/public/theme/font.js" crossorigin></script>
复制代码

Vue捕获异常

根据官方资料,在Vue中,异常可能被Vue自身给try ... catch了,不会传到window.onerror事件触发。

使用Vue.config.errorHandler这样的Vue全局配置,可以在Vue指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和Vue 实例。

官方文档


Vue.config.errorHandler = function(err, vm, info) {
  // handle error
  // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
  // 只在 2.2.0+ 可用
  // 从 2.6.0 起,这个钩子也会捕获 v-on DOM 监听器内部抛出的错误。另外,如果任何被覆盖的钩子或处理函数返回一个 Promise 链 (例如 async 函数),则来自其 Promise 链的错误也会被处理。
  // console.log(vm.$el.innerHTML);
  console.log(`Error: ${err.toString()}\nInfo: ${info}`);
}
``
复制代码

image.png

image.png

小结

image.png

sourceMap

通常在生产环境下的代码是经过webpack打包后压缩混淆的代码,所以我们可能会遇到这样的问题:我们发现所有的报错的代码行数都在第一行了。

解决办法是开启webpack的source-map,我们利用webpack打包后的生成的一份.map的脚本文件就可以让浏览器对错误位置进行追踪了。此处可以参考webpack document

其实就是webpack.config.js中加上一行devtool: 'source-map',如下所示,为示例的webpack.config.js:

image.png

image.png

主动上报异常

onError 的方案会采集到全面的浏览器报错,但是太全了,没办法指定场景下精细定位问题,而且还会有各种奇奇怪怪的错误

  • ISP 在页面中注入的脚本报错
  • 插件问题(乱七八糟的插件也是一样的令人讨厌,注入奇怪的东西在页面中,引起报错)。

为了让采集到的错误更有价值,需要暴露接口给让前端门自己在代码中上报异常(作为一名合格的代码工程师,一定要有抛异常的习惯呢)。

现在我们提供了自定义上报的接口,没错就是这么简单,你不用担心 WGTracker 脚本有没有加载,只要在你的代码中直接用就行了。

import { WGTracker } from '@utils/tracker.js'
try{
	//your code
}catch(e){
	WGTracker({
		type:'custom'
		error:{
			status:[自定义的唯一标识,便于开发者定位问题],
			message:'巴拉巴拉巴拉巴拉扒拉'
		},
	})
}
复制代码

PS:尽管try catch 对性能的影响微乎其微,但是一些用法会让性能受很大的影响, 在 try 语句块中不要定义太多的变量,最好是只写一个函数调用,避免 try 运行中变量拷贝造成的性能损耗。类似的不只是 try,定义 function 也是一样的。

接口调用错误

在项目中,我们的接口调用统一用基于axios封装的Https.js请求,在该文件中使用拦截器的方式对response信息拦截,同样使用我们提供的自定义上报接口上传接口调用错误信息

import { WGTracker } from '@utils/tracker.js'
/*
  * 失败响应拦截
  * */
  interceptResponseFail(error) {
    if(!error) {
      return;
    }
    const errorObj = JSON.parse(JSON.stringify(error));
	WGTracker({
		type:'api'
		error: errorObj
	})
  }
复制代码

WGTracker参数详情

参数名 描述 类型 可选值
type 上报的错误类型 Enum 1、js2、custom3、api
error 要上报的具体错误信息 Object 见示例
  • error示例(api):
{
    "message": "Request failed with status code 404",
    "name": "Error",
    "stack": "Error: Request failed with status code 404\n    at createError (webpack-internal:///./src/common/node_modules/[email protected]@axios/lib/core/createError.js:16:15)\n    at settle (webpack-internal:///./src/common/node_modules/[email protected]@axios/lib/core/settle.js:17:12)\n    at XMLHttpRequest.onloadend (webpack-internal:///./src/common/node_modules/[email protected]@axios/lib/adapters/xhr.js:66:7)",
    "config": {
        "transitional": {
            "silentJSONParsing": true,
            "forcedJSONParsing": true,
            "clarifyTimeoutError": false
        },
        "transformRequest": [
            null
        ],
        "transformResponse": [
            null
        ],
        "timeout": {},
        "xsrfCookieName": "XSRF-TOKEN",
        "xsrfHeaderName": "X-XSRF-TOKEN",
        "maxContentLength": -1,
        "maxBodyLength": -1,
        "headers": {
            "Accept": "application/json, text/plain, */*",
            "X-Auth-Token": "4dd382a0d0f248ba87c18e619c350565",
            "systemUri": "umc",
            "X-Preference": "%7B%7D"
        },
        "url": "/api/bcs/api/organization/getPageOrg123",
        "method": "get",
        "params": {
            "currentPage": 1,
            "pageSize": 100,
            "clicDetailTypes": "1",
            "orgCodes": "3036"
        }
    },
    "status": 404
}
复制代码
  • error示例(custom):
{
    "message": "Request failed with status code 404",
    "status": 404
}
复制代码
  • error示例(JS):
{
	"isOnError":true/false,
    "errorMessage": "Request failed with status code 404",
    "scriptURI": 'http://xxxx',
	"lineNo":185,
	"columnNo":1,
	"errorStack":"Error: Request failed with status code 404
    	at createError (webpack-internal:///./src/common/node_modules/[email protected]@axios/lib/core/createError.js:16:15)
    	at settle (webpack-internal:///./src/common/node_modules/[email protected]@axios/lib/core/settle.js:17:12)
    	at XMLHttpRequest.onloadend (webpack-internal:///./src/common/node_modules/[email protected]@axios/lib/adapters/xhr.js:66:7)",
	"Info":"created hook (Promise/async)",
}
复制代码

错误信息在localstorage中的存储结构

时间 浏览器 用户名 分辨率 日志错误提示信息 日志类型 status desc Stack 发生页面 工作站
2022-1-09 16:23:45 IE 11.0 杨毅成 1440x900 Request failed with status code 404 接口报错 404 apiDesc【接口的具体报错内容,见下描述】 【objectString堆栈信息】
JS异常 jsDesc【接口的具体报错内容,见下描述】
自主上报 customDesc【接口的具体报错内容,见下描述】

apiDesc

参数名称 关键字 类型
关键错误信息描述 message String
请求headers信息 headers Object
请求参数信息 params Object
请求url地址 url String
请求方式 method Enum

customDesc

参数名称 关键字 类型
关键错误信息描述 message String
唯一错误标识,用于定位代码块 status String

jsDesc

参数名称 关键字 类型
错误的捕捉方式是否为onError isOnError Enum
关键错误信息描述 errorMessage String
报错定位地址 - SourceMap scriptURI String
报错行号 lineNo Number
报错列号 columnNo Nubmer
错误详细堆栈信息 errorStack ObjectString
Vue下对错误的描述补充 Info String

猜你喜欢

转载自juejin.im/post/7105038856126201869
今日推荐