前言

本文目的:

能让非前端同学大致了解下,现代『前端异常解析』是怎么做的,以及大部分的坑会是哪些

对于专业的前端同学,本文中也许有些坑,你还没有踩到,也可以看下。

从 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 属性就好了,但还有一个必要条件是服务端要支持这个属性。这个服务端包括:

  1. cdn 的服务器。
  2. 低端的安卓系统版本(离线包拦截请求无法设置这个属性)。

所以在开启这个属性前,一定要确认好以上两个地方是否支持,不然的话,嘿嘿,白屏!

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 来反解析。

那些年,我们解析过的前端异常相关推荐

  1. 监控html页面数据获取失败,前端异常采集(附实例)

    原标题:前端异常采集(附实例) 为什么要做前端代码异常采集?好问题! 为了用户能安心用产品,不至于时不时"卡壳"崩溃. 为了能高效定位线上代码的异常并提供简单提示信息. 为了程序猿 ...

  2. 前端异常监控调研总结

    前端异常监控 一.异常监控系统 二.异常情况 三.异常表现分类 四.Web前端监控分类 五. 监控分类解析 六 错误分类和捕获 七 小程序异常监控 八 前端异常上报方式 一.异常监控系统 前端和后端处 ...

  3. 前端try catch是如何捕获异常的_一文告诉你如何优雅处理前端异常?

    前端一直是距离用户最近的一层,随着产品的日益完善,我们会更加注重用户体验,而前端异常却如鲠在喉,甚是烦人. 一.为什么要处理异常? 异常是不可控的,会影响最终的呈现结果,但是我们有充分的理由去做这样的 ...

  4. 从无到有<前端异常监控系统>落地

    从无到有<前端异常监控系统>落地 参考文章: (1)从无到有<前端异常监控系统>落地 (2)https://www.cnblogs.com/1wen/p/7942608.htm ...

  5. 构建可靠的前端异常监控服务-采集篇

    http://jdc.jd.com/archives/2175 在 Web 应用异常复杂的今天,一个页面不单单只包含文字.图片和超链接,还可能包含复杂表单.大量动画.海量交互.很多 Web 应用完全单 ...

  6. 直播回顾丨神策数据王朋:如何搭建一套高可用的前端异常监控系统?

    本文根据神策数据资深前端研发工程师王朋在神策「大数据技术系列直播课」第二季"前端专题"第四讲的直播整理. 本次分享主要分为三大部分:前端异常监控概述,异常监控的背景意义,以及做一个 ...

  7. [转] 前端异常监控解决方案研究

    前端监控包括行为监控.异常监控.性能监控等,本文主要讨论异常监控.对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己等监控方案,但两者并不分离,因为一个用户在操作应用过程中如 ...

  8. 学习 sentry 源码整体架构,打造属于自己的前端异常监控SDK

    前言 这是学习源码整体架构第四篇.整体架构这词语好像有点大,姑且就算是源码整体结构吧,主要就是学习是代码整体结构,不深究其他不是主线的具体函数的实现.文章学习的是打包整合后的代码,不是实际仓库中的拆分 ...

  9. 如何解决网页中console的显示内容出现undefined和null_如何优雅地处理前端异常?...

    前端一直是距离用户最近的一层,随着产品的日益完善,我们会更加注重用户体验,而前端异常却如鲠在喉,甚是烦人. 一.为什么要处理异常? 异常是不可控的,会影响最终的呈现结果,但是我们有充分的理由去做这样的 ...

最新文章

  1. 用c自制编程语言,(怒)自制编程语言
  2. Redisson 是如何实现分布式锁的?
  3. VC++ MFC中如何将应用程序的配置信息保存到注册表中(一)
  4. 014_Spring事务
  5. 用XInput库使用xbox360手柄
  6. Oracle的if else if
  7. JPA规范的主要内容
  8. 陈强《高级计量经济学及stata应用》相关数据
  9. 将TXT文件作为数据库批量生成条形码
  10. [Linux] scp免密码登录
  11. php 倒置,PHP依赖倒置案例详解
  12. Deepin升级内核后无法启动的问题
  13. 中国无尘室饮水机市场趋势报告、技术动态创新及市场预测
  14. ArrayList和LinkedList的区别以及优缺点
  15. 构造IOCTL命令的学习心得-----_IO, _IOR, _IOW, _IOWR 幻数的理解
  16. (六)CDA 数据分析师Level1考试新版大纲解析(自己整理)PART 6业务数据分析
  17. 阿里云自定义域名详细过程----hexo博客配置实测详细过程整理之二
  18. 微信小程序--地理位置获取、导航
  19. 手机怎么在线拍照翻译英语?只要几个步骤轻松解决
  20. 用vegas制作美美哒写真相册——教程

热门文章

  1. Centos7部署轻量级自动化运维工具pssh (亲测)
  2. 使用Python破解zip的密码
  3. 日语学习 (助词 「で」 和「に」 的区别)
  4. H5 播放视频常见bug及解决方案
  5. 程序员的基础和解决问题的思维很重要
  6. 数据库主从数据一致性的几种解决方案
  7. (转) Eclipse Maven 编译错误 Dynamic Web Module 3.1 requires Java 1.7 or newer 解决方案
  8. 从Android应用程序访问Internet需要什么权限?
  9. oracle存储格式化时间,ORACLE日期时间的格式化参数大全
  10. activiti(7.0)排他网关