如何监控前端异常?
一个完善的异常监控平台,是一个非常复杂的项目。它需要包括异常监控、信息收集、信息归类、信息的统计分析、异常场景的重现、异常在源码中的定位等等,同时,也要考虑异常日志的存放以及服务器压力等问题。
本文只涉及异常的捕获、上传方面的内容,大致围绕下面几点展开讨论:
前端需要处理的异常
前端异常的捕获方式
异常信息的上报方式
异常监控常见问题
为什么要处理异常?
增强用户体验
远程定位问题
未雨绸缪,及早发现问题
无法复线问题,尤其是移动端,机型,系统都是问题
完善的前端方案,前端监控系统;
对于 JS
而言,异常的出现不会直接导致 JS
引擎崩溃,最多只会使当前执行的任务终止。
前端需要处理的异常
语法错误
运行时异常
EvalError eval错误
RangeError 范围错误
ReferenceError 引用错误
TypeError 类型错误
URIError URI错误
SyntaxError 语法错误
Error 通用错误
资源加载异常
img
script
link
audio
video
iframe
@font-face
外链资源的DOM元素。。。
Promise 异常
异步请求异常
XMLHttpRequest
fetch
前端异常的捕获方式
try-catch-finally
window.onerror = function () {}
img
script
link
window.addEventListener('error', function () {}, true)
window.addEventListener("unhandledrejection", function () {})
Promise.then().catch(function () {})
封装 XMLHttpRequest、fetch,覆写请求接口对象
try-catch-finally
try-catch
处理异常的能力有限,只能捕获捉到运行时非异步错误,对于语法错误(如:中文分号)和异步错误(如:回调、promise、setTimeout )就显得无能为力。
window.onerror
window.onerror
是一个全局变量,默认值为null。当 JS
运行时错误(包括语法错误)发生时,window
会触发一个 ErrorEvent
接口的事件,并执行 window.onerror();
。onerror
可以接受多个参数。
window.onerror
无法捕获 静态资源异常、接口异常。
**注意:**语法错误会导致出现语法错误的那个脚本块执行失败,所以语法错误会导致当前代码块运行终止,从而导致整个程序运行中断,如果语法错误这个发生在我们的错误监控语句块中,那么我们就什么也监控不到了。
window.onerror = function () {}
要比其他脚本先执行(注意这个前提!),才可以捕捉到语法错误。
window.onerror
函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示Uncaught Error: xxxxx
。
window.addEventListener
监听 js 运行时错误事件,会比 window.onerror
先触发,与 onerror
的功能大体类似,不过事件回调函数传参只有一个保存所有错误信息的参数,不能阻止默认事件处理函数的执行,但可以全局捕获资源加载异常的错误。
当资源(如img或script)加载失败,加载资源的元素会触发一个 Event 接口的 error 事件,并执行该元素上的 onerror() 处理函数。这些error事件不会向上冒泡到window,但可以在捕获阶段被捕获。因此如果要全局监听资源加载错误,需要在捕获阶段捕获事件
网络请求异常不会事件冒泡,因此必须在捕获阶段将其捕捉到才行,但是这种方式虽然可以捕捉到网络请求的异常,但是无法判断 HTTP
的状态是 404
还是其他比如 500
等等,所以还需要配合服务端日志才进行排查分析才可以。
需要注意:
不同浏览器下返回的
error
对象可能不同,需要注意兼容处理。需要注意避免
addEventListener
重复监听。
unhandledrejection
没有写 catch
的 Promise
中抛出的错误无法被 onerror
或 try-catch
捕获到,所以我们务必要在 Promise
中不要忘记写 catch
处理抛出的异常。
为了防止有漏掉的 Promise
异常,在全局增加一个对 unhandledrejection
的监听,用来全局监听Uncaught Promise Error
。
Promise.then().catch(function () {})
封装XMLHttpRequest、fetch
VUE errorHandler
vue本身有监听异常机制,我们可以在它提供的监听函数中,上传异常信息。
崩溃和卡顿
卡顿也就是网页暂时响应比较慢, JS
可能无法及时执行。但崩溃就不一样了,网页都崩溃了,JS
都不运行了,还有什么办法可以监控网页的崩溃,并将网页崩溃上报呢?
利用 window
对象的 load
和 beforeunload
事件实现了网页崩溃的监控。
以使用 Service Worker
来实现网页崩溃的监控:
Service Worker
有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker
一般情况下不会崩溃;Service Worker
生命周期一般要比网页还要长,可以用来监控网页的状态;网页可以通过
navigator.serviceWorker.controller.postMessage API
向掌管自己的SW
发送消息。
异常日记上报方式
异步请求上报, 后端提供接口,或者直接发到日志服务器
img请求上报,url参数带上错误信息
sentry 中文翻译是哨兵。它是一个款错误跟踪、性能监控工具。
Sentry 是一个开源的实时错误报告工具,支持 web 前后端、移动应用以及游戏,支持多种语言(JavaScript、Java、Go、Nodejs、Php、Python 等)和框架(React、Vue、Angular、Next.js 等),还提供了 GitHub、Slack、Trello 等常见开发工具的集成。
使用 sentry 需要结合两个部分:客户端与服务端。客户端就是你需要去监听的项目。而服务端就是一个数据管理平台,它会展示已收集到的错误信息和项目信息,并支持项目管理,组员管理、邮件报警等功能。
可以直接使用 sentry 官方平台,也可以利用 Sentry 的开源库在自己的服务器上搭建服务,官方已经提供了完善的操作文档。 Sentry 的搭建方式主要有两种:Python 安装、通过 Docker 安装。由于 Docker 更加方便易控,官方推荐 Docker 部署。
这里,我们以官方平台为例,简单讲一下接入步骤:
注册账号: 登录入 sentry 官网,注册一个账号。(注意翻墙,shadowsocks PAC 自动模式可能无效,请添加 【PAC 用户自定义规则】或者启用全局模式)。
创建项目,获取DSN: 注册后,在官网创建一个项目,后台会自动生成一个 DSN。DSN 是一个重要的值,用来告诉客户端将事件发送到哪里。
客户端监听项目: 通过 CDN 或者 npm 引入 sentry。
以 npm 引入为例:
客户端集成:这是客户端的核心部分,它告诉 sentry 怎么收集、收集哪些错误。
Sentry 默认情况下启用系统集成以集成到标准库或解释器本身。
defaultIntegrations 用来表示是否使用默认添加的集成。
integrations 用来标识启用集成的名称列表。列表应该包含所有启用的集成,包括默认的集成。包含默认集成是因为不同的 SDK 版本可能包含不同的默认集成。
integrations 可用来删除、或添加集成:
除引用 sentry 提供的集成,我们可以自定义一个集成,还可以主动捕获并上报错误。
主动上报的方式有两种: 一种是直接上报文本信息,参数为一个字符串;另一种是上报错误对象,参数为一个 error 对象或者类对象。
以下只是简单的使用示例,详细使用可查看下面链接:
https://docs.sentry.io/platforms/javascript/
Sentry For Vue 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!
常见问题
跨域脚本异常报错信息
生产环境的 JS 做静态资源 CDN 化,导致访问的页面跟脚本文件来自不同的域名,这时候如果没有进行额外的配置,就会容易产生 Script error
。
Script error
是浏览器在同源策略限制下产生的,浏览器处于对安全性上的考虑,当页面引用非同域名外部脚本文件时中抛出异常的话,此时本页面是没有权利知道这个报错信息的,取而代之的是输出 Script error
这样的信息。
在H5的规定中,只要满足下面两个条件,是允许获取跨源脚本的错误信息的。
客户端在 script 标签上增加 crossorigin 属性;
服务端设置 js 资源响应头
Access-Control-Origin: 指定域名 | *
。
window.error 和 window.addEventListener区别
window.onerror
含有详细的 error
信息(如:error.stack),而且兼容性更好,所以一般 JS 运行时错误使用 window.onerror
捕获处理。
window.addEventListener('error')
,可以捕获 JS 运行时的错误,也能捕获资源加载错误。为避免重复上报 js 运行时错误,此时应该只有event.srcElement inatanceof HTMLScriptElement、HTMLLinkElement、HTMLImageElement
时才上报
总结
使用场景分析
可疑区域增加
Try-Catch
全局监控
JS
异常window.onerror
全局监控静态资源异常
window.addEventListener
捕获没有
Catch
的Promise
异常:unhandledrejection
vue框架:
VUE errorHandler
、React 框架:React componentDidCatch
监控网页崩溃:
window
对象的load
和beforeunload
业界已经有的监控平台
Sentry 开源 (推荐)
阿里的 ARMS
FrontJS
另外还有一些轻量级的 BetterJS
参考资料
最后更新于