那些年,我们解析过的前端异常
前言
本文目的:
能让非前端同学大致了解下,现代『前端异常解析』是怎么做的,以及大部分的坑会是哪些
对于专业的前端同学,本文中也许有些坑,你还没有踩到,也可以看下。
从 window.onerror 说起
相信大部分接触过『前端错误监控』话题的同学都知道,通过浏览器提供的 window.onerror 能够捕获大部分的前端异常,比如 js 报错(本文讨论的话题),assets 加载等。
对于 js 报错,这个 api 会提供出报错消息,源文件,lineno, colno
(行号列号),详细的错误堆栈等消息,如下所示:
window.onerror = function(message, source, lineno, colno, error) { ... }
复制代码
我们拿到这些消息后就能知道,『哦,原来的我的某个 js 里的第 x 行,第 y 列,我写错了啊』。
但是,事情真的这么简单吗?
反解压缩过的 js 代码
首先第一个现实问题,我线上的 js 代码一定是经过压缩(压缩文件传输)的。所以,在 window.onerror
这个方法里拿到的 lineno 通常是 1,colno 通常是 123456 (一个很大的数字),然后当你定位到对应源码位置的时候,通常看到这类代码:
什么鬼?js 的压缩代码不仅会压缩代码行数,还会对某些可替换的临时变量名做重命名,换成简短的形式。所以正常靠肉眼要定位到真正错误的源码是很难很费劲的。
source-map 登场
这个时候我们第一个王牌登场,既然知道是怎么压缩的,那这个压缩的对应关系也一定是有的,这个对应关系就是 source-map。
然后我们通过一些反解析 source-map 的工具,把源码的 lineno, colno
(行号,列号) 和 source-map 文件输入,就能得到知道哪行源码写错了,类似效果如下:
一些安全性的考虑
通常出于安全性考虑,source-map 文件是不会随着 js 资源文件一起发布的。js 资源的发布又是很频繁的,所以会造成大量 souce-map 文件管理的问题,后面会讲到一种方案。
现在看似我们能够 cover 住所有的前端 js 异常了。但是事实上是,如果你的前端资源是通过 cdn 的方式来部署的话,你会收到很多 script error(可能 80% 都是这个错误).
script error
script error 大部分是浏览器 跨域 安全限制导致的。通常的解决方式是,对跨域的脚本加上 crossorigin 属性就好了,但还有一个必要条件是服务端要支持这个属性。这个服务端包括:
- cdn 的服务器。
- 低端的安卓系统版本(离线包拦截请求无法设置这个属性)。
所以在开启这个属性前,一定要确认好以上两个地方是否支持,不然的话,嘿嘿,白屏!
ok,到现在我们基本解决 window.onerror 报上来的问题了。
那还有其他的前端异常吗?
成也框架,败也框架
近些年,随着前端工程化,组件化的流行,越来越多的 MVVM 的框架登上历史舞台(vue,react,angular 等)。
对前端监控来说,拿 vue 来举例。
你写的 vue 业务代码,会发现 window.onerror 是监控不到的。原因是这些框架他们内部会消化掉这些错误,必须得通过他们的 API 才能 catch 到。比如:
Vue.config.errorHandler = function (err, vm, info) {// handle error// `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
}
复制代码
重点注意一下,这个函数不像 window.onerror
会把错误的 lineno, colno
一起上报。只能看到一个干巴巴的错误消息,比如:
TypeError: Cannot read property 'id' of nullat a.next (https://xxx.com/0-1.0.0-5ba93bf.js:1:25746)at n (https://xxx.com/x2-1.0.0-5ba93bf.js:13:1417)
复制代码
这是线上代码,同样也是经过压缩的,而且由于被框架代码包了一层,在压缩过的代码里定位变得更加费劲。
那有什么方法可以拿到 lineno, colno
然后通过 source-map 反解析源码么?
答案其实就在上面,在错误堆栈里。仔细看 https://xxx.com/0-1.0.0-5ba93bf.js:1:25746
,按照 js stack 的生成规则,通常*.js:x:y
第一个冒号后面的 x 代表了 lineno
行数,第二个冒号后面的 y 代表了 colno
列数。
所以我们通过肉眼或者是可以正则解析出 lineno, colno
的,然后通过 source-map 工具能够定位出具体的 vue 代码出错位置了。
同样要问一个,那还有其他的前端异常了吗?
明明白屏了,咋不报错呢
假设不小心,写了如下的 vue 代码。
<template>
</template><script>
export default {mounted () {const somePromise = new Promise(_ => {let aa.doingNothing // 空指针,出错啦!!!})somePromise.then(console.log)}
}
</script>
复制代码
明明里面有一个空指针会导致白屏的问题,可是你会发现 window.onerror
捕捉不到,Vue.config.errorHandler
同样也捕捉不到(不是说好框架全吃的吗!)。
仔细看,哦,原来这是写在 promise 里的代码啊,这个代码被这个 promise 吃掉了,因为这里没有 catch,所以也就不知道了。
随着 js 语言的发展,越来越多的特性被引入到浏览器中,按照新的规范,像 promise 里面的错误应该 promise 由开发者自己负责来 catch 而不会被 window.onerror 捕获。
但是理想很丰满,现实却还是 ---- 不能保证每个人都会写 catch。
所以对于 promise 的错误,我们需要做全局监听,代码如下:
window.addEventListener('unhandledrejection', function (event) {const error = event && event.reasonconsole && console.warn && console.warn('WARNING: Unhandled promise rejection. Shame on you! Reason: ' + error)// ... report error
}
复制代码
同样对于这里没有上报 lineno, colno
,但是有 error stack,我们可以使用同样的套路对他们用 source-map 来反解析。
那些年,我们解析过的前端异常相关推荐
- 监控html页面数据获取失败,前端异常采集(附实例)
原标题:前端异常采集(附实例) 为什么要做前端代码异常采集?好问题! 为了用户能安心用产品,不至于时不时"卡壳"崩溃. 为了能高效定位线上代码的异常并提供简单提示信息. 为了程序猿 ...
- 前端异常监控调研总结
前端异常监控 一.异常监控系统 二.异常情况 三.异常表现分类 四.Web前端监控分类 五. 监控分类解析 六 错误分类和捕获 七 小程序异常监控 八 前端异常上报方式 一.异常监控系统 前端和后端处 ...
- 前端try catch是如何捕获异常的_一文告诉你如何优雅处理前端异常?
前端一直是距离用户最近的一层,随着产品的日益完善,我们会更加注重用户体验,而前端异常却如鲠在喉,甚是烦人. 一.为什么要处理异常? 异常是不可控的,会影响最终的呈现结果,但是我们有充分的理由去做这样的 ...
- 从无到有<前端异常监控系统>落地
从无到有<前端异常监控系统>落地 参考文章: (1)从无到有<前端异常监控系统>落地 (2)https://www.cnblogs.com/1wen/p/7942608.htm ...
- 构建可靠的前端异常监控服务-采集篇
http://jdc.jd.com/archives/2175 在 Web 应用异常复杂的今天,一个页面不单单只包含文字.图片和超链接,还可能包含复杂表单.大量动画.海量交互.很多 Web 应用完全单 ...
- 直播回顾丨神策数据王朋:如何搭建一套高可用的前端异常监控系统?
本文根据神策数据资深前端研发工程师王朋在神策「大数据技术系列直播课」第二季"前端专题"第四讲的直播整理. 本次分享主要分为三大部分:前端异常监控概述,异常监控的背景意义,以及做一个 ...
- [转] 前端异常监控解决方案研究
前端监控包括行为监控.异常监控.性能监控等,本文主要讨论异常监控.对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己等监控方案,但两者并不分离,因为一个用户在操作应用过程中如 ...
- 学习 sentry 源码整体架构,打造属于自己的前端异常监控SDK
前言 这是学习源码整体架构第四篇.整体架构这词语好像有点大,姑且就算是源码整体结构吧,主要就是学习是代码整体结构,不深究其他不是主线的具体函数的实现.文章学习的是打包整合后的代码,不是实际仓库中的拆分 ...
- 如何解决网页中console的显示内容出现undefined和null_如何优雅地处理前端异常?...
前端一直是距离用户最近的一层,随着产品的日益完善,我们会更加注重用户体验,而前端异常却如鲠在喉,甚是烦人. 一.为什么要处理异常? 异常是不可控的,会影响最终的呈现结果,但是我们有充分的理由去做这样的 ...
最新文章
- 用c自制编程语言,(怒)自制编程语言
- Redisson 是如何实现分布式锁的?
- VC++ MFC中如何将应用程序的配置信息保存到注册表中(一)
- 014_Spring事务
- 用XInput库使用xbox360手柄
- Oracle的if else if
- JPA规范的主要内容
- 陈强《高级计量经济学及stata应用》相关数据
- 将TXT文件作为数据库批量生成条形码
- [Linux] scp免密码登录
- php 倒置,PHP依赖倒置案例详解
- Deepin升级内核后无法启动的问题
- 中国无尘室饮水机市场趋势报告、技术动态创新及市场预测
- ArrayList和LinkedList的区别以及优缺点
- 构造IOCTL命令的学习心得-----_IO, _IOR, _IOW, _IOWR 幻数的理解
- (六)CDA 数据分析师Level1考试新版大纲解析(自己整理)PART 6业务数据分析
- 阿里云自定义域名详细过程----hexo博客配置实测详细过程整理之二
- 微信小程序--地理位置获取、导航
- 手机怎么在线拍照翻译英语?只要几个步骤轻松解决
- 用vegas制作美美哒写真相册——教程
热门文章
- Centos7部署轻量级自动化运维工具pssh (亲测)
- 使用Python破解zip的密码
- 日语学习 (助词 「で」 和「に」 的区别)
- H5 播放视频常见bug及解决方案
- 程序员的基础和解决问题的思维很重要
- 数据库主从数据一致性的几种解决方案
- (转) Eclipse Maven 编译错误 Dynamic Web Module 3.1 requires Java 1.7 or newer 解决方案
- 从Android应用程序访问Internet需要什么权限?
- oracle存储格式化时间,ORACLE日期时间的格式化参数大全
- activiti(7.0)排他网关