你知道本身的代码在线上有多少问题吗

本文做者@李逸君,京东前端开发工程师。html

前言

badjs,即前端异常的一个洋气的统称。指代那些「找不到对象」、「未定义」、「语法问题」等在前端抛出来的异常错误。前端

笔者负责的是京喜的前端某业务,长期受到大量异常的困扰,又经常找不到缘由。有时异常一下暴涨,又降了回去,定位不到问题,深受其扰。通过长时间的沉淀,分析总结出了一套结论和方法。node

文章篇幅较长,前半部分讲述了为何要作这件事以及怎么收集和分析 badjs,适合于没有系统接触过 badjs 的同窗了解。ios

后半部分对 Script error、Hybrid、大数据系统进行了较为深刻的分析,供你们参考和讨论。有必定经验的同窗能够直接跳到这一部分开始阅读。c++

但愿对于屏幕面前的你能有所启发。git

ps:此系列方法不适用于node.js程序员

京喜的 badjs

先来看下这张图片:github

badjs

这是我所负责的一个线上业务的 badjs 走势。不知道你看到这根刺是什么感受,反正我看到是会很是紧张,不论手上有什么事都得立马扑向电脑检查问题,分析日志,跟老板汇报原由...web

为何要这样一个系统

俗话说,技术服务于业务。咱们的 badjs 日志系统诞生于必然。chrome

以微信小程序商品详情业务为例,日pv有千万。

假如前端出了问题,有啥东西点不动,致使访问的用户变少了。最后的结果是单量少了,用户丢了,还影响了整个部门同窗的饭碗。这个锅,背不起。

面临这些问题,试问一下:若是是你维护的页面,你怕不怕?若是是你即将发布的页面,你手抖不抖?

为了程序员的幸福生活,提早发现问题,把黑锅扼杀在摇篮中,这样一个系统是必须的选择。

badjs原理和收集

咱们没法预测哪一段代码会出问题,成本最小的方案是在一个集中的地方统一处理,而后收集起来。

badjs 的由来

badjs 实质是 js 引擎执行了没法识别的逻辑,出现了异常。

好比在 undefined 上进行操做、用错了数据类型、解析异常、语法错误等:

花式错误

badjs的收集

当 JavaScript 运行发生异常时,而且未被捕获,window 下会触发一个 ErrorEvent 接口的 error 事件,并执行 window.onerror()

咱们能够经过如下两种方式在全局处理异常:

window.addEventListener('error', function(errorEvent) {
    const { message, filename, lineno, colno, error } = errorEvent
    ...
})
复制代码
window.onerror = function(message, source, lineno, colno, error) { ... }
复制代码

这两种方式在写法上前者更优一点,由于 error 事件能够监听多个,window.onerror 只能订阅一个。

惋惜 ie8 之前不支持 ErrorEvent,只能使用 window.onerror。 因此看业务须要合理使用。

另外还有 try...catch 的方式,也能够收集到特定场景下的全局错误信息,但这个方式有不少缺陷,关于它咱们后面再谈。

经过 window 下的 error 事件,咱们正常状况下能收集到五类信息:

属性 含义 说明
message 错误信息 错误描述
filename(source) 发生错误的脚本URL ErrorEvent 中是 filename,在 onError 中是 source
lineno 错误行
colno 发生错误的脚本 URL
error Error 对象 error.stack 是很重要的信息

在 console 内打出来是这样的:

error信息

有了这些,就已经给了咱们至关充分的信息来定位问题了。

这里报错的信息有一点挺有意思。message 里会带上 'Uncaught' 表示未捕获。而 error.stack 内没有这个前缀词:

Uncaught

跨域场景

以上说到的是理想场景,真实环境可能会涉及跨域,这个背景下每每只能收集到一些不太有意义的信息。

跨域error信息

这个问题文章后半部分会深刻的谈谈。

tips

window 下的 error 事件也并非什么异常都能捕获的。

好比咱们直接在 console 里触发的异常、浏览器拦截的数据、资源 404 等都不会触发。

小程序badjs

上面是用 html 的前端错误收集,小程序的有些差别。以微信小程序为例,

小程序的全局异常捕获,能够在 App 注册小程序的方法下订阅。相关文档点这里

App({
  onLaunch (options) {
    // Do something initial when launch.
  },
  ...,
  onError (msg) {
    console.log(msg)
  }
})
复制代码

这里不太同样的是,onError 订阅函数里只有一个 msg 参数。内容相似 error.stack

msg

小程序的场景比较集中,复杂的环境不多,没有跨域脚本的问题,比 H5 简单不少。后面介绍的内容基本通用,对于小程序部分再也不赘述。

badjs上报

要是出了问题,咱们得依靠上报的信息定位问题。那么就要提早确认哪些信息是须要的。另外考虑到数据量级的问题,太多了服务器容易噎着,所以只上报一些必要的信息。

前端必要上报数据

类别 来源 示例
content message + error.stack "ReferenceError: test is not defined at HTMLLIElement. (wq.360buyimg.com/wecteam/bad…)
有帮助的业务信息 / balabalabala

除了 stack 信息,还有些业务信息(好比跟单、用户id等)可能有助于定位,看业务背景自行选择。

另外有些数据是包含在报文当中的,能够在服务端一块儿收集和展现,前端不须要再单独上报。

报文自带的数据

类别 来源 示例
ip / 58.20.191.9
time / May 20th 2020, 14:56:00.062
referer Headers.Referer wqsou.jd.com/wecTeam?key…
UA Headers.User-Agent Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.12(0x17000c2d) NetType/4G Language/zh_CN
Cookie Headers.Cookie pin=wecTeam;

数据拼接

前端把必要的数据拼接起来,准备发送到服务器。相似下面这条连接:

wq.jd.com/wecteam/bad…

数据传输用 get 方式便可。更简单一些的用 img 传递。

var _img = new Image();
_img.src = url;
复制代码

须要注意的是,get 有最大长度(2048个字符)限制,注意裁剪。若是须要突破限制,请用 post 方式上报。

实例:bj-report.js

可能你会问:道理我都懂,但我不太想努力了,有没有现成的?

有。

推荐这款鹅厂出品的工具:bj-report.js 点击这里进入git主页

它能够帮你进行前端日志上报与JS异常监控。经过简单的初始化和传入一些参数,就能够跳过上文的全部步骤~

BJ_REPORT.init({
  id: 1,                                // 上报 id, 不指定 id 将不上报
  uin: 123,                             // 指定用户 id, (默认已经读取 qq uin)
  delay: 1000,                          // 延迟多少毫秒,合并缓冲区中的上报(默认)
  url: "//badjs2.qq.com/badjs",         // 指定上报地址
  ignore: [/Script error/i],            // 忽略某个错误
  random: 1,                            // 抽样上报,1~0 之间数值,1为100%上报(默认 1)
  repeat: 5,                            // 重复上报次数(对于同一个错误超过多少次不上报)
                                        // 避免出现单个用户同一错误上报过多的状况
  onReport: function(id, errObj){},     // 当上报的时候回调。 id: 上报的 id, errObj: 错误的对象
  submit: null,                         // 覆盖原来的上报方式,能够自行修改成 post 上报等
  ext: {},                              // 扩展属性,后端作扩展处理属性。例如:存在 msid 就会分发到 monitor,
  offlineLog : false,                   // 是否启离线日志 [默认 false]
  offlineLogExp : 5,                    // 离线有效时间,默认最近5天
});
复制代码

数据分析

说完了数据上报,再说一下拿到数据怎么分析。

数据提取

如今京喜所使用的日志系统,是基于一个第三方的专业日志分析系统 Kibana 定制而成的:

kibana系统

图上简单的标注了下它有的一些功能,重要的是咱们能够经过它又快又准的查到想要的信息。

须要省事一些的话,也能够搭建一个简单的报表系统:

简单的报表系统

若是想更省事,能够直接连上数据服务器,导出一份文档。

获取上报数据不属于本篇主题,这里就不介绍了。简而言之,咱们在这里提取 badjs 日志。

异常分析

有了数据,就能够开始快(ku)乐(bi)的分析了。

badjs 能够分为两种,一种是由于开发人员所写的代码缺陷而致使的,俗称 bug。还有一种是人为故意的,好比安全扫描、刷子、爬虫、浏览器插件等嵌入的第三方的脚本,触发了本不存在的异常。

咱们须要特别关注前者,分辨出来后者,并尽量不让数据受后者的影响。

content 信息

content 信息即 messageerror.stack,里面是错误的描述和堆栈信息。

message 是对 error.stack 的补充,而一段完整的 error.stack 信息,包含了错误代码堆栈,文件和行列号。经过这些信息咱们基本能够判定错误的位置和触发缘由。

好比这一段 stack 信息(chrome 下):

badjs

咱们一行行来看:

  1. Uncaught TypeError: Cannot read property 'style' of undefined

没法在 undefined 下访问 style 这个属性。就是说在某个对象下,某个属性空了,在这个基础上又访问 style 这个属性,所以报错了。

  1. at removeAD (badjs.js:4)

该异常代码位于badjs.js 这个文件里的第 4 行,所属的方法名叫作 removeAD

  1. at window.fireBadjs (badjs.js:8)

调用 removeAD 的代码位于 badjs.js 第 8 行,所属的方法名叫作 window.fireBadjs

  1. at badjs.html:16

调用 window.fireBadjs 的代码位于 badjs.html 这个文件的第 16 行。

有这么详细的信息,对照源码查一下,对于 badjs 的缘由内心基本就有底了。

还有一种比较短的堆栈信息:

anonymous

发生异常的位置很奇怪,第一行第一列,在 anonymous 中触发。回去一查代码,发现第一行第一列根本没有这个方法。

这个实际上是在浏览器的匿名函数(即 anonymous)中执行的代码,相似直接打在 console 中的代码,或者经过 eval 等函数运行的代码。

事实上,上面图片里的这个 badjs 出现的背景,是 App 的 native 用 js 代码在 Webview 里直接执行了一个 window 做用域下的 callback (即 getNetWrokCallback)。可是这个函数不存在,因此出现异常。

有了上面这个基础,再来看看这个错误。

某错误

一看错误内容,这不是和上面那个例子同样吗,可是没有 anonymous 的信息。但咱们代码里面没有这个 SOHUZ 的属性,因此先猜想多是某个 App 主动执行的 js 代码致使的异常。

进一步的分析须要结合 UA 的信息。UA 里有 kuaizhanAndroidWrapper 这个字段,经过万能的互联网,发现"快站" App 的身份信息里包含这个字段。

所以结论出来了:"快站" App 里访问了这个页面,但它没有进行非空检查,直接访问了 SOHUZ,致使发生 badjs 报在咱们业务这里了。非错误,结案。

UA

在上面一段的结尾提早介绍了下 UA 信息的做用。

咱们能经过 UA,推断出环境:好比运行在某微信、某 App,某浏览器等。还能推断出是谁在访问咱们的页面:好比百度爬虫、xx爬虫、阿里百川等。

UA 能够简单的做为用户指纹。京喜的页面会常常被不知名的网友刷,天天还特别准时。由于不肯定来源,只能戏谑的称之为:刷子。有时咱们会遇到大量奇怪的报错,可是背后的 UA 都是如出一辙的,基本能够认定是有网友在刷咱们的的页面。

须要强调的一点是,UA 能够伪造。有些有经验的刷子,每次的 UA 都是不同的,迷惑性很强。所以 UA 信息是否真实,还得具体状况具体分析。

初见 Script error

京喜 H5 业务线上的 badjs 日志,其实绝大部分详情只展现了 'Script error'。

Script error

出现 Script error,是由于引入了跨域脚本。好比经过 script 标签引入的其余非同域 js 文件,里面发生了 badjs,这时在 onerror 事件里的 message 信息就只有一句 Script error,error 信息为 null

跨域error信息

对于这些已经上报了的 Script error 数据,能获得的数据很是有限,大多数时候只能放弃。

可是对于这个问题并非没有任何解决方法,咱们能够在上报的时候"打开" Script error 里的信息,再上报它。好比跨域脚本设置信任策略等一些方案。

关于 Script error 详细的应对策略,咱们将在下面的内容里进一步讨论它。

Script error

上面卖了很多关子,这一段深刻一下,讨论怎么能挖出 Script error 里有效的信息。

来源

Script error 本质是由于浏览器的跨域安全策略行为,保护非同域代码内容的安全。

全部经过 script 标签引入的跨域脚本,若是出现异常,window 下的 error 事件都只能获得 'Script error'。

Script error

解决方案

对于 script 标签引入的跨域脚本产生 'Script error' 的问题,业内有解决方案。

大概思路就是:不就是安全问题嘛,只要双方都以为可信,那我浏览器就给你放开这个限制。

具体实现以下:

1.response 头增长 Access-Control-Allow-Origin,代表受信任的域名

Access-Control-Allow-Origin

2.请求的 script 标签增长 crossorigin 属性

crossorigin

crossorigin 有两个值,分别是

  • anonymous
  • use-credentials

crossorigin='' ,或者其余字符,和设置 anonymous 的效果同样。anonymous 依赖 response 头的 Access-Control-Allow-Origin。须要注意的是,使用 anonymous 时,request 头不会带上用户信息,好比 cookie。相对的,使用 use-credentials 能够带上用户信息。

use-credentials 须要和 response 头的 Access-Control-Allow-Credentials 配合使用,当 Access-Control-Allow-Credentials 返回 true 的时候,浏览器才会容许运行脚本和信任来源。另外在这种状况下, Access-Control-Allow-Origin 不能设置为 *,必须是具体的域名。

Access-Control-Allow-Credentials

经过以上的两步,web 浏览器下的 window error 事件里的 error 信息就不会被浏览器拦截成 'Script error' 了。

一个变种场景: jsonp

这里提一个 script 标签的变种场景:jsonp

jsonp自己解决的问题就是跨域接口请求,所以大部分使用场景自带跨域光环。另外它经过 script 标签运行,和 js 脚本的性质同样。

所以当它出现了异常,也会存在 Script error 的状况。

jsonp 出现异常的常见的场景就是:callback 未定义

callback 未定义

虽然能够在 console 看到具体错误信息,可是在被捕获的 error 中,由于 url 跨域,没额外设置信任,只能抓到 Script error。

解决方式就是按照「常规解决方案」里列举的两个步骤去"解构" Script error。

这里有个问题在于大部分接口依赖用户信息,前端须要使用 crossorigin='use-credentials' 方式将请求带上 cookie 信息。所以后台在 Access-Control-Allow-Origin 中要返回具体的域名,以及将 Access-Control-Allow-Credentials 设置为 true 以让浏览器经过验证。

Access-Control-Allow-Credentials

use-credentials

额外提一点,jsonp 产生的脚本绝大部分是非异步代码。跨域脚本异步代码有一些坑,后面会介绍。

特殊的解决方案

使用 crossOrigin 是常规解决方案。还有种特殊的方法,就是使用 try...catch

举个简单的栗子:

// https://xxx.badjs.js
window.fireBadjs = function () {
    ops
}
复制代码
<script src="xxx.badjs.js" ></script>
<script> window.addEventListener('error', function (errorEvent) { const { message, filename, lineno, colno, error } = errorEvent debugger }) fireBadjs() try { fireBadjs() } catch (error) { debugger } </script>
复制代码

badjs.js 里面往 window 下塞了一个名为 fireBadjs 的函数,里面运行了一行会出异常的代码。

第一个 fireBadjs() 运行后,error 事件触发,内容以下:

Script error

情理之中,error 事件只能抓到 Script error。

注释掉第一个 fireBadjs(),让在 try...catch 中的 fireBadjs() 执行,badjs 被 catch 捕获。打在 console 里,它的内容以下:

错误信息

神奇的是,本来是 Script error 的内容被挖了出来。

但这种方法终归不优雅,对代码侵入性较强。并且还有个小缺陷,就是不会触发 window 下的 error 事件。

接下来咱们谈谈怎么改善。

try catch 加强

try...catch 是一个让人又爱又恨的工具。不少时候咱们为了让代码有好的健壮性,会用它包裹起来。

可是一旦在 try 中发生了错误,浏览器不会把错误打在 console 里,也不会触发 error 事件。

来看一段简单的代码:

try {
    JSON.stringify(apiData)
} catch (e) {}
复制代码

若是 JSON.stringify(apiData) 发生了错误,代码多是运行正常,可是最后表现没有达到预期,咱们也不知道这里有问题。最后可能绕了一个大圈子才回到这里。

这里的问题出在异常被捕获之后,没有给予咱们提示。那解决方案也很简单,手动补上提示就好。

这时能够在 catch 里,把错误打在 console.error 里面,并手动包装 ErrorEvent,丢给 window 下的 error 事件捕获。

try {
    JSON.stringify(apiData)
} catch (error) {
    console.error(error)
    if (ErrorEvent) {
        window.dispatchEvent(new ErrorEvent('error', { error, message: error.message })) // 这里也会触发window.onerror
    } else {
        window.onerror && window.onerror(null, null, null, null, error)
    }
}
复制代码

须要额外注意的是,try...catch 有个缺点,不能捕获异步代码的异常。好比 setTimeoutpromise、事件等。由于这个问题,咱们没法在代码里从头至尾简单的包裹一层 try...catch 解决全部问题。

Hybrid

开发人员每每在本身的象牙塔内进行改造和升级,可是真实的生产环境每每比预想的更复杂,好比 Hybrid。

这里是广义 Hybrid,除了原生 App 以外,浏览器也算。事实上 H5 真正的场景就是在 Hybrid 当中,这里研究一下 H5 在手机 App 里异常会有什么表现。

Webview

不少 App 里面都会内嵌 Webview 运行 H5 页面。若是 H5 在 Webview 里面发生了 badjs,各位猜一猜,Webview 的表现是什么样的?

这里分两个环境 iOS 和 Android。

1.iOS系统 (系统测试版本:9.0.2/11.0.3/13.4)

在 iOS 中的 Webview,跨域脚本的异步代码若是发生了badjs(注意是异步代码),无论有没有按照常规方案去设置跨域头和 crossOrigin,在 window 下的 error 事件里都只能抓到 'Script error'。

你没看错,只要是 iOS 里的 Webview,不管是你业务下的 App,仍是微信甚至是浏览器。

举个栗子:在iPhone的微信里打开了某个 H5 页面,H5 页面引用了某 (跨域)cdn 的 js 文件。当这个 js 文件内部某个事件产生 badjs 并上报了,咱们在日志系统只能看到 'Script error'。

2.Android系统

在 Android 中的 Webview,和web端同样的表现。设置好了跨域头和 crossOrigin,若是跨域脚本发生 badjs,不论异步同步代码,都能在 window 下的 error 事件里抓到错误详情。

拿一个京喜 h5 的线上的数据,简单验证下这个现象:

一个绝大部分流量跑在 微信 和 手机QQ 里的业务,在 Android 环境下,有 8043 个 Script error

在 iOS 环境下有 25685 个 Script error

顺便一提,iOS 下的非 Script error 只有 393 个。

解决方法,主要是针对 iOS 环境。由于 Android 表现和 web 端一致。

1.跨域脚本改成同域脚本。

同域脚本报错不会显示 Script error。

2.try...catch 包裹

把跨域的脚本里的异步方法用 try...catch 包裹一下,在 catch 中手动触发事件。手动包裹比较麻烦,能够考虑用工具打包的时候自动包裹一下。

3.用 Android 的 badjs 数据参考 iOS

由于两个环境出异常的几率近似相等,在作好跨域工做之后,专一解决 Android 下的 badjs,就能够覆盖绝大部分 iOS 的异常了。

native 代码注入

这里和上面一节的区别是,上一节是 H5 在 App 里"自由落体",这一节是由 App 主动干涉。

App native 往 Webview 注入代码有两种方式。第一种是 native 代码转换成 js 代码,再发送到 Webview 经过 js 引擎执行。第二种是用 c++ 代码,直接写在引擎的底层,成为 native code。

第一种方式实际上仍是经过 Webview 环境执行,和 js 代码无异。常见于在 JSSDK 里的一些 callback 参数。

App 通常会为了不逻辑过于复杂,一般就是往 Webview 里直接执行 window.callback,若是 callback 不存在,出了异常,Android 中和 web 端表现一致。

可是 iOS 里只有 Script error。

若是你在日志里看处处于 anonymous 下第一行第一列的报错,而且 UA 是 App 环境。那颇有多是由 App 主动的调用产生的 badjs:

App 主动调用

Android 没什么好说的,iOS 在 content 这里则会显示 Script error。因为代码是 App 生成发送到 Webview 当中的,所以前端没办法作什么去解构这个 Script error,只能跟 App 的同窗商量作一层保护,好比调用前先检查函数是否认义过了。

第二种用 c++ 代码,直接写在引擎的底层的方式,常见于 native 生成一段供 H5 使用原生 App 能力的接口,好比 jssdk。这种方式生成的代码会成为 native code。

native code 的示例:console.log

native

因为运行在引擎当中,一旦出错,Webview 可能整个都挂了。前端 badjs 事件对于这种状况是无能力为的,能够忽略。

iOS stack 差别

实际上 iOS 下的 error stack 还有细节差别,这里介绍一下。

测试机型:

  • iphone xs max 13.4
  • 荣耀V20 magicUI_3.0.0 andorid_10.0.0.194

测试代码:

<script> window.addEventListener('error', function (errorEvent) { confirm(errorEvent.error && errorEvent.error.stack) }) nextLineBadjs // 触发同域badjs </script>
<!-- 设置了跨域头的脚本 -->
<script src="//wq.360buyimg.com/badjs.js" crossorigin="anonymous"></script>
复制代码
// badjs.js
setTimeout(() => {
    asyncBadjs // 异步代码
}, 1000);

syncBadjs // 同步代码
复制代码

微信 Webview

iOS 下:

iOS

Android 下:

Android

iOS 当中没有显示出来具体的报错代码,用 'global code' 指代。跨域异步脚本显示的是 Script error。

Android 一切正常。

iOS 的 'global code' 增长了一点定位难度,可是由于指明了位置,因此问题也不大。额外提一句,这里是 error.stack 里面的信息,事实上 message 里面是有包含 'global code' 里面的详细信息。

经过 confirm(errorEvent.message) 咱们把它打出来

'global code'

所以,上报信息的时候能够把 errorEvent.message 一块儿带上去。

其余 Webview

笔者还测试了其余移动端的 App 和浏览器,有京东 App、百度浏览器、chrome、qq浏览器、firefox

由于结果类似,没有本质的区别,这里就很少展现了。

聊聊后台和大数据的一些事

大部分同行使用 badjs 日志系统最大的阻碍来自于后台的建设。根据 logstash 业界标准日志采集流程,前端只能完成第一步:采集。对于储存和展现是无能为力的,须要后台同窗的介入。

这里介绍一下京喜有关的一些背景,以供参考。

数据量级

咱们的日志数据除了 badjs,还有一些前端主动上报的业务日志信息。由于部门里的业务很是多,上报流程也比较规范,上报数据随着不断增多的业务节节攀升。如今天天的数据大概24亿条(2.5T)。筛掉业务上报,纯 badjs 大概只占了 0.67%。

因为咱们有大数据机器的资源,所以这些信息跑在咱们本身的数据机器上,没有额外开销。

性能优化

虽然机器资源比较足,可是也撑不住无节制的使用。咱们有一些性能优化方案,保证日志服务稳定。

  • 数据每5天清除一次
    • 数据按期清除,保证储存空间良性循环
    • 过时的日志再也不提供查询能力
  • 上报降级方案
    • 前端有接入配置,当数据井喷,自动调整采样率,放弃必定比例的上报
    • 后台也有相似配置,数据量太高,自动放弃一些数据

初版数据收集系统:webmonitor

咱们最初的日志系统的界面是本身搭建的,使用 url 做为关键字进行查询 badjs。

简单的报表系统

日志数据存在 hdfs 当中。当发起查询时,使用 impala 暴力扫描 hdfs。效率比较低,查一次大概半分钟。

这个系统没有更多的筛选能够选择,展现方式也十分粗糙。

第二版数据收集系统:ElasticSearch+Kibana

第二版的日志系统,大数据同窗接入了 ElasticSearch,创建了日志文件的索引,而后经过 Kibana 系统和它无缝对接。由于有了索引,查询速度快了;经过 Kibana,查询门槛低了,也有更多维度数据的分析了。

kibana系统

查询速度的提升,对快速定位和回应上级的效率有了质的飞跃。

咱们能够根据关键词进行查询,筛掉无用的,或者选择感兴趣的信息。

也能够方便的选择某个数据集中的区间,很快的分析问题和提出解决方案。

从未感受做为开发人员的幸福感能够如此之高。

小结

如何收集 badjs

  • 经过 errorEvent 或者 window.onerror
  • 特殊状况下能够考虑使用 try...catch

badjs 上报

  • 在 url 上拼接 error.stack 的信息
  • 用 img 等方式发送数据,注意长度

badjs 分析

  • 经过栈信息,人工回溯代码
  • 经过 UA 简单判断环境和用户身份
  • 对于 Script error 信息,得不到太多有用的价值,分析的时候能够跳过。可是要想办法在上报的时候把它"解构"

常见的致使 Script error 的场景

  • 跨域脚本,而且未设置 crossorigin 里发生异常
  • jsonp,未设置 crossorigin,其对于的 script 生成的 js 脚本里发生异常
  • iOS 下的 跨域脚本 异步代码 内发生异常
  • iOS 下 native 主动执行 js 代码发生异常

关于异步代码须要注意的点

  • ios 下的跨域异步代码没法解析 Script error
  • try catch 没法抓到异步代码的错误
  • jsonp 大可能是同步代码

必要性

最后回过头聊一聊有关这个系统的必要性。

对于京喜的业务,这么一套系统是必须的。由于安全很是重要,咱们根本没法承担较长时间,线上出问题后的责任。

下面分析一下它的优点和缺陷。

优点

不肯定本身的代码有没有问题,是一件很是不安的事情。要是有客户用户投诉过来,那真是一个头两个大。

绝大部分前端的业务代码都是通过测试把关的。若是经过了充分测试,线上代码业务逻辑出问题的几率比较低,因此关注点集中在有没有代码报错。

这么一个系统的出现,很大程度上增长了前端同窗的自信心。发版以后,出了问题,可以第一时间回退代码,定位问题。最重要的是给了老板们安全感

缺陷

然而这样一个系统不是万金油,还有不少其余维度的问题没法用它衡量。若是太过于依赖,没有充分测试业务,上线的时候就看了一眼 badjs 日志就算完事了,那线上可能会有不少头大的问题等着你发现。

另外这个资源的开销也是一个不得不提的问题。若是公司没有足够的资源,就须要额外的开销在租借服务器和存储设备上。

怎么选择

可能有的同窗就要问了:“咱们业务没有这个东西,何况又依赖那么多资源,我根本无法拿主意啊”。

不少在处于成长期的业务可能根本没精力去作这些基础建设,但我相信在将来的某个时间,你会迫切的须要这些数据。

问一问本身,它能够减小多少线上事故。

等到须要的时候,不妨回过头来看看这篇文章。

若是出现问题带来的损失大于建设成本和维护成本,我相信你必定能说服你所在的团队和你的老板。

参考资料

相关文章
相关标签/搜索