常见错误的分类

对于用户在访问页面时发生的错误,主要包括以下几个类型:

1、js运行时错误

JavaScript代码在用户浏览器中执行时,由于一些边界情况、本地环境的不可控等因素,可能会存在js运行时错误。

而依赖客户端的某些方法,由于兼容性或者网络等问题,也有概率会出现运行时错误。

e.g: 下图是当使用了未定义的变量"foo",导致产生js运行时错误时的上报数据:

2、资源加载错误

这里的静态资源包括js、css以及image等。现在的web项目,往往依赖了大量的静态资源,而且一般也会有cdn存在。

如果某个节点出现问题导致某个静态资源无法访问,就需要能够捕获这种异常并进行上报,方便第一时间解决问题。

e.g: 下图是图片资源不存在时的上报数据:

3、未处理的promise错误

未使用catch捕获的promise错误,往往都会存在比较大的风险。而编码时有可能覆盖的不够全面,因此有必要监控未处理的promise错误并进行上报。

e.g: 下图是promise请求接口发生错误后,未进行catch时的上报数据:

4、异步请求错误(fetch与xhr)

异步错误的捕获分为两个部分:一个是传统的XMLHttpRequest,另一个是使用fetch api。

像axios和jQuery等库就是在xhr上的封装,而有些情况也可能会使用原生的fetch,因此对这两种情况都要进行捕获。

e.g: 下图是xhr请求接口返回400时捕获后的上报数据:

各个类型错误的捕获方式

1、window.onerror与window.addEventListener('error')捕获js运行时错误

使用window.onerror和window.addEventListener('error')都能捕获,但是window.onerror含有详细的error堆栈信息,存在error.stack中,所以我们选择使用onerror的方式对js运行时错误进行捕获。

window.onerror = function (msg, url, lineNo, columnNo, error) {

// 处理错误信息

}

// demo

msg: Uncaught TypeError: Uncaught ReferenceError: a is not defined

error.statck: TypeError: ReferenceError: a is not defined at http://xxxx.js:1:13

window.addEventListener('error', event => (){

// 处理错误信息

}, false);

// true代表在捕获阶段调用,false代表在冒泡阶段捕获。使用true或false都可以,默认为false

2、资源加载错误使用addEventListener去监听error事件捕获

实现原理:当一项资源(如

这些error事件不会向上冒泡到window,不过能被window.addEventListener在捕获阶段捕获。

但这里需要注意,由于上面提到了addEventListener也能够捕获js错误,因此需要过滤避免重复上报,判断为资源错误的时候才进行上报。

window.addEventListener('error', event => (){

// 过滤js error

let target = event.target || event.srcElement;

let isElementTarget = target instanceof HTMLScriptElement || target instanceof HTMLLinkElement || target instanceof HTMLImageElement;

if (!isElementTarget) return false;

// 上报资源地址

let url = target.src || target.href;

console.log(url);

}, true);

3、未处理的promise错误处理方式

实现原理:当promise被reject并且错误信息没有被处理的时候,会抛出一个unhandledrejection。

这个错误不会被window.onerror以及window.addEventListener('error')捕获,但是有专门的window.addEventListener('unhandledrejection')方法进行捕获处理。

window.addEventListener('rejectionhandled', event => {

// 错误的详细信息在reason字段

// demo:settimeout error

console.log(event.reason);

});

4、fetch与xhr错误的捕获

对于fetch和xhr,我们需要通过改写它们的原生方法,在触发错误时进行自动化的捕获和上报。

改写fetch方法:

// fetch的处理

function _errorFetchInit () {

if(!window.fetch) return;

let _oldFetch = window.fetch;

window.fetch = function () {

return _oldFetch.apply(this, arguments)

.then(res => {

if (!res.ok) { // 当status不为2XX的时候,上报错误

}

return res;

})

// 当fetch方法错误时上报

.catch(error => {

// error.message,

// error.stack

// 抛出错误并且上报

throw error;

})

}

}

对于XMLHttpRequest的重写:

xhr改写

// xhr的处理

function _errorAjaxInit () {

let protocol = window.location.protocol;

if (protocol === 'file:') return;

// 处理XMLHttpRequest

if (!window.XMLHttpRequest) {

return;

}

let xmlhttp = window.XMLHttpRequest;

// 保存原生send方法

let _oldSend = xmlhttp.prototype.send;

let _handleEvent = function (event) {

try {

if (event && event.currentTarget && event.currentTarget.status !== 200) {

// event.currentTarget 即为构建的xhr实例

// event.currentTarget.response

// event.currentTarget.responseURL || event.currentTarget.ajaxUrl

// event.currentTarget.status

// event.currentTarget.statusText

});

}

} catch (e) {va

console.log('Tool\'s error: ' + e);

}

}

xmlhttp.prototype.send = function () {

this.addEventListener('error', _handleEvent); // 失败

this.addEventListener('load', _handleEvent); // 完成

this.addEventListener('abort', _handleEvent); // 取消

return _oldSend.apply(this, arguments);

}

}

关于responseURL 的说明

需要特别注意的是,当请求完全无法执行的时候,XMLHttpRequest会收到status=0 和 statusText=null的返回,此时responseURL也为空string。

另外在安卓4.4及以下版本的webview中,xhr对象也不存在responseURL属性。

因此我们需要额外的改写xhr的open方法,将传入的url记录下来,方便上报时带上。

var _oldOpen = xmlhttp.prototype.open;

// 重写open方法,记录请求的url

xmlhttp.prototype.open = function (method, url) {

_oldOpen.apply(this, arguments);

this.ajaxUrl = url;

};

其他问题

1、其他框架,例如vue项目的错误捕获

vue内部发生的错误会被Vue拦截,因此vue提供方法给我们处理vue组件内部发生的错误。

Vue.config.errorHandler = function (err, vm, info) {

// handle error

// `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子

// 只在 2.2.0+ 可用

}

2、script error的解决方式

"script error.”有时也被称为跨域错误。当网站请求并执行一个托管在第三方域名下的脚本时,就可能遇到该错误。最常见的情形是使用 CDN 托管 JS 资源。

其实这并不是一个 JavaScript Bug。出于安全考虑,浏览器会刻意隐藏其他域的 JS 文件抛出的具体错误信息,这样做可以有效避免敏感信息无意中被不受控制的第三方脚本捕获。

因此,浏览器只允许同域下的脚本捕获具体错误信息,而其他脚本只知道发生了一个错误,但无法获知错误的具体内容。

解决方案1:(推荐)

添加 crossorigin="anonymous" 属性。

此步骤的作用是告知浏览器以匿名方式获取目标脚本。这意味着请求脚本时不会向服务端发送潜在的用户身份信息(例如 Cookies、HTTP 证书等)。

添加跨域 HTTP 响应头:

Access-Control-Allow-Origin: *

或者

Access-Control-Allow-Origin: http://test.com

**注意:**大部分主流 CDN 默认添加了 Access-Control-Allow-Origin 属性。

完成上述两步之后,即可通过 window.onerror 捕获跨域脚本的报错信息。

解决方案2

难以在 HTTP 请求响应头中添加跨域属性时,还可以考虑 try catch 这个备选方案。

在如下示例 HTML 页面中加入 try catch:

Test page in http://test.com

// app.js里面有一个foo方法,调用了不存在的bar方法

window.onerror = function (message, url, line, column, error) {

console.log(message, url, line, column, error);

}

try {

foo();

} catch (e) {

console.log(e);

throw e;

}

// 运行输出结果如下:

=> ReferenceError: bar is not defined

at foo (http://another-domain.com/app.js:2:3)

at http://test.com/:15:3

=> "Script error.", "", 0, 0, undefined

可见 try catch 中的 Console 语句输出了完整的信息,但 window.onerror 中只能捕获“Script error”。根据这个特点,可以在 catch 语句中手动上报捕获的异常。

总结

上述的错误捕获基本覆盖了前端监控所需的错误场景,但是第三部分指出的两个其他问题,目前解决的方式都不太完美。

对于有使用框架的项目:一是需要有额外的处理流程,比如示例中就需要单独为vue项目进行初始化;二是对于其他框架,都需要单独处理,例如react项目的话,则需要使用官方提供的componentDidCatch方法来做错误捕获。

而对于跨域js捕获的问题:我们并不能保证所有的跨域静态资源都添加跨域 HTTP 响应头;而通过第二种包裹try-catch的方式进行上报,则需要考虑的场景繁多并且无法保证没有遗漏。

虽然存在这两点不足,但前端错误捕获这部分还是和项目的使用场景密切相关的。我们可以在了解这些方式以后,选择最适合自己项目的方案,为自己的监控工具服务。

—— —— 参考文档 —— ——

1.Using XMLHttpRequest:

2.script error 产生的原因和解决办法:

3.JavaScript执行错误:

4.betterjs的script error:

5.Vuejs的errorHandler:

6.React的componentDidCatch:vivo 互联网web前端开发工程师火热招聘中,发送简历到 2020Labs@vivo.com,获取内推机会哦。

更多内容敬请关注 vivo 互联网技术 微信公众号

注:转载文章请先与微信号:labs2020 联系。

前端捕捉轨迹_一篇文章教你如何捕获前端错误相关推荐

  1. 组件化开发实战_一篇文章搞懂什么是前端“组件化”开发

    学过网页的朋友都知道,制作一个网页离不开HTML.CSS和JavaScript技术.对于初学者来来说,掌握这3门技术就已经很不容易了,为什么前端为什么又要搞出来一个"组件化"开发的 ...

  2. 前端捕捉轨迹_基于JavaScript实现每日签到打卡轨迹功能

    本文实例为大家分享了js实现每日签到打卡轨迹功能的具体代码,供大家参考,具体内容如下 1. 核心文件 calendar.js var calUtil = { //当前日历显示的年份 showYear: ...

  3. 英文连写字体怎么练_一篇文章教孩子学会衡水英文字体,建议收藏!

    "衡水体"实质上是衡水中学学生书写的手写印刷体, 随着中考高考电子阅卷的普及,采用简洁.工整.快速的字体进行书写成为必需,极大地迎合了阅卷老师心理和电脑显示特点.一定程度上有助于大 ...

  4. python爱心代码动态_一篇文章教你用python画动态爱心表白

    hRf免费资源网 初级画心hRf免费资源网 学Python,感觉你们的都好复杂,那我来个简单的,我是直接把心形看作是一个正方形+两个半圆:hRf免费资源网 hRf免费资源网 于是这就很简单了,十行代码 ...

  5. keyshot渲染图文教程_一篇文章教你学会3D建模和渲染 反正我是信了

    平常大家需要学习3D设计,基本上都是通过网上教程,这些教程多以视频为主,因为3D的可操作性非常强,只有在动态的视频上才能完整展示并教学. 但是也有这样或那样的原因,很多小伙伴并不能通过视频学习,那有没 ...

  6. hashmap是散列表吗_一篇文章教你读懂哈希表-HashMap

    题图Pid=68670770 在最近的学习过程中,发现身边很多朋友对哈希表的原理和应用场景不甚了解,处于会用但不知道什么时候该用的状态,所以我找出了刚学习Java时写的HashMap实现,并以此为基础 ...

  7. python每隔半个小时执行一次_一篇文章教你用Python抓取微博评论

    [Part1--理论篇] 试想一个问题,如果我们要抓取某个微博大V微博的评论数据,应该怎么实现呢?最简单的做法就是找到微博评论数据接口,然后通过改变参数来获取最新数据并保存.首先从微博api寻找抓取评 ...

  8. 如何用python赚钱_一篇文章教你使用Python自动赚取支付宝积分,网友:发家致富?...

    打开搜狗搜索APP,查看更多精彩资讯 本文介绍如何利用Python+uiautomator2 每日自动赚取支付宝积分. 支付宝的积分有啥用?诱惑诱惑你:可以兑换视频网站的VIP会员. 可以兑换各种优惠 ...

  9. 位地址和字节地址换算_一篇文章教你了解什么是ip地址

    一.什么是二进制与十进制? 完全搞懂ip地址之前我们首先要知道二进制与十进制的关系,那么什么是二进制呢? 我们知道电脑在进行数据传输的时候只能传输二进制比特流,并且二进制只能用0和1进行表示. 比如一 ...

最新文章

  1. 大楼通信综合布线系统_系统梳理一下综合布线系统的方方面面
  2. 2018/7/12-纪中某C组题【jzoj4272,jzoj4273,jzoj4274】
  3. python将excel日期比大小_sql与excel、python比较(二)——日期和时间函数
  4. python循环语句c次_python循环语句
  5. 通过ssh连接Termux
  6. javaJNI(javah用法)
  7. linux:硬链接和软链接
  8. 解决linux下无法连接为wifi热点上网
  9. 根据三个点的坐标计算三角形面积
  10. MVC《中国电信》项目研发总结
  11. Redis面试完整版
  12. Linux中国微信,「Linux 中国」2018 微信文章排行榜 | Linux 中国
  13. GNU Radio教程 9.QPSK调制解调
  14. 在html中设置margin属性,margin
  15. 如何向开源社区贡献代码
  16. 如何让您的网站符合W3C标准
  17. 某哪旅行cookie值Alina参数生成分析
  18. 集线器、路由器、交换机对帧转发操作
  19. 信号完整性分析1——信号完整性分析概论
  20. 比较好的python线上培训班

热门文章

  1. UI设计素材 | 备用的404界面,存起来
  2. flexsession禁用_flex(替代session过期)用户长时间不操作要求重新登录的处理
  3. u盘盘符不显示 win10_荣耀16.1锐龙版 linux装win10教程
  4. 变量命名规则_Java变量与常量
  5. 最小错误率贝叶斯决策的基本思想_机器学习笔记—模式识别与智能计算(四)基于概率统计的贝叶斯分类器设计(贝叶斯决策)...
  6. hashmap底层原理_周末自己动手撸一个 HashMap,美滋滋
  7. CentOS 7 Linux实时内核下的epoll性能分析
  8. Reactor模型和Proactor模型:同步IO与异步IO
  9. DPDK单生产者入队单消费者出队
  10. cmake 简介-初识