前端异常监控-看这篇就够了

2次阅读

共计 5266 个字符,预计需要花费 14 分钟才能阅读完成。

前端异常监控
如果 debug 是移除 bug 的流程,那么编程就一定是将 bug 放进去的流程。如果没有用户反馈问题,那就代表我们的产品棒棒哒,对不对?
主要内容

Web 规范中相关前端异常
异常按照捕获方式分类
异常的捕获方式
日志上报的方式

前端异常类型 (Execption)

WebIDL 和 ecma-262 中的错误类型

ECMAScript exceptions <==> IDL 的简单异常
当脚本代码运行时发生的错误,会创建 Error 对象,并将其抛出,除了通用的 Error 构造函数外,以下是另外几个 ECMAScript 2015 中定义的错误构造函数。

EvalError eval 错误

RangeError 范围错误

ReferenceError 引用错误

TypeError 类型错误

URIError URI 错误

SyntaxError 语法错误 (这个错误 WebIDL 中故意省略,保留给 ES 解析器使用)

Error 通用错误(这个错误 WebIDL 中故意省略,保留给开发者使用使用)

DOMException 最新的 DOM 规范定义的错误类型集,兼容旧浏览的 DOMError 接口, 完善和规范化 DOM 错误类型。

IndexSizeError 索引不在允许的范围内

HierarchyRequestError 节点树层次结构是不正确的。

WrongDocumentError 对象是错误的

InvalidCharacterError 字符串包含无效字符。

NoModificationAllowedError 对象不能被修改。

NotFoundError 对象不能在这里被找到。

NotSupportedError 不支持的操作

InvalidStateError 对象是一个无效的状态。

SyntaxError 字符串不匹配预期的模式

InvalidModificationError 对象不能以这种方式被修改

NamespaceError 操作在 XML 命名空间内是不被允许的

InvalidAccessError 对象不支持这种操作或参数。

TypeMismatchError 对象的类型不匹配预期的类型。

SecurityError 此操作是不安全的。

NetworkError 发生网络错误

AbortError 操作被中止

URLMismatchError 给定的 URL 不匹配另一个 URL。

QuotaExceededError 已经超过给定配额。

TimeoutError 操作超时。

InvalidNodeTypeError 这个操作的 节点或节点祖先 是不正确的

DataCloneError 对象不能克隆。

前端错误异常按照捕获方式分类

[x] 运行时异常
语法错误

[x] 资源加载异常

img
script
link
audio
video
iframe
… 外链资源的 DOM 元素

[x] 异步请求异常

XMLHttpRequest
fetch

[x] Promise 异常

[] CSS 中资源异常

@font-face
background-image
… 暂时无法捕获

前端错误异常的捕获方式

try-catch (ES 提供基本的错误捕获语法)

只能捕获同步代码的异常
回调
setTimeout
promise

window.onerror = cb (DOM0)

img
script
link

window.addEventListener(‘error’, cb, true) (DOM2)
window.addEventListener(“unhandledrejection”, cb) (DOM4)
Promise.then().catch(cb)
封装 XMLHttpRequest&fetch | 覆写请求接口对象

try-catch-finally
将能引发异常的代码块放到 try 中,并对应一个响应,然后有异常会被捕获
try {
// 模拟一段可能有错误的代码
throw new Error(“ 会有错误的代码块 ”)
} catch(e){
// 捕获到 try 中代码块的错误得到一个错误对象 e,进行处理分析
report(e)
} finally {
console.log(“finally”)
}
onerror 事件
window.onerror
当 JavaScript 运行时错误(包括语法错误)发生时,window 会触发一个 ErrorEvent 接口的事件,并执行 window.onerror()
/**
* @description 运行时错误处理器
* @param {string} message 错误信息
* @param {string} source 发生错误的脚本 URL
* @param {number} lineno 发生错误的行号
* @param {number} colno 发生错误的列号
* @param {object} error Error 对象
*/
function err(message,source,lineno,colno,error) {…}
window.onerror = err
element.onerror
当一项资源(如 <img> 或 <script>)加载失败,加载资源的元素会触发一个 Event 接口的 error 事件,并执行该元素上的 onerror() 处理函数。
element.onerror = function(event) {…} // 注意和 window.onerror 的参数不同
注意:这些 error 事件不会向上冒泡到 window,不过能被单一的 window.addEventListener 捕获。
window.addEventListener
addEventListener 相关的一些内容
W3C DOM2 Events 规范中提供的注册事件监听器的方法, 在这之前均使用 el.onclick 的形式(DOM0 规范的基本内容,几乎所有浏览器都支持)。
注意:接口的几种语法
error 事件捕获资源加载错误
资源加载失败,不会冒泡,但是会被 addEventListener 捕获,所以我们可以指定在加载失败事件的捕获阶段捕获该错误。
注意: 接口同时也能捕获运行时错误。
window.addEventListener(“error”, function(e) {
var eventType = [].toString.call(e, e);
if (eventType === “[object Event]”) {// 过滤掉运行时错误
// 上报加载错误
report(e)
}
},
true
);
unhandledrejection 事件捕获 Promise 异常
最新的规范中定义了 unhandledrejection 事件用于全局捕获 promise 对象没有 rejection 处理器时异常情况。
window.addEventListener(“unhandledrejection”, function (event) {
// …your code here to handle the unhandled rejection…

// Prevent the default handling (error in console)
event.preventDefault();
});
Promise.then().catch(cb).finally()
Promise 中的错误会被 Promise.prototype.catch 捕获,所以我们通过这种方式捕获错误,这包括一些不支持 unhandledrejection 事件的环境中 promisede polyfill 实现。
new Promise(function(resolve, reject) {
throw ‘Uncaught Exception!’;
}).catch(function(e) {
console.log(e); // Uncaught Exception!
});
封装 XMLHttpRequest&fetch | 覆写请求接口对象
// 覆写 XMLHttpRequest API
if(!window.XMLHttpRequest) return;
var xmlhttp = window.XMLHttpRequest;
var _oldSend = xmlhttp.prototype.send;
var _handleEvent = function (event) {
if (event && event.currentTarget && event.currentTarget.status !== 200) {
report(event)
}
}
xmlhttp.prototype.send = function () {
if (this[‘addEventListener’]) {
this[‘addEventListener’](‘error’, _handleEvent);
this[‘addEventListener’](‘load’, _handleEvent);
this[‘addEventListener’](‘abort’, _handleEvent);
this[‘addEventListener’](‘close’, _handleEvent);
} else {
var _oldStateChange = this[‘onreadystatechange’];
this[‘onreadystatechange’] = function (event) {
if (this.readyState === 4) {
_handleEvent(event);
}
_oldStateChange && _oldStateChange.apply(this, arguments);
};
}
return _oldSend.apply(this, arguments);
}

// 覆写 fetch API
if (!window.fetch) return;
var _oldFetch = window.fetch;
window.fetch = function() {
return _oldFetch
.apply(this, arguments)
.then(function(res){
if (!res.ok) {
// True if status is HTTP 2xx
report(res)
}
return res;
})
.catch(function(error){
report(res)
});
}

日志上报的方式

异步请求上报, 后端提供接口,或者直接发到日志服务器

img 请求上报, url 参数带上错误信息
eg:(new Image()).src = ‘http://baidu.com/tesjk?r=tksjk’

注意跨源脚本异常
当加载自不同域的脚本中发生语法错误时,为避免信息泄露,语法错误的细节将不会报告,而代之简单的 “Script error.”
由于同源策略影响,浏览器限制跨源脚本的错误访问,这样跨源脚本错误报错信息如下图:

在 H5 的规定中,只要满足下面俩个条件,是允许获取跨源脚本的错误信息的。

客户端在 script 标签上增加 crossorigin 属性;
服务端设置 js 资源响应头 Access-Control-Origin:*(或者是域名)。

扩展阅读
业界已经有的监控平台

Sentry 开源
阿里的 ARMS
fundebug
FrontJS

几个异常监控的问题

如何保证大家提交的代码是符合预期的?如何了解前端项目的运行是否正常,是否存在错误?代码质量体系控制和错误监控以及性能分析

如果用户使用网页,发现白屏,现在联系上了你们,你们会向他询问什么信息呢?先想一下为什么会白屏?
我们以用户访问页面的过程为顺序,大致排查一下

用户没打开网络
DNS 域名劫持
http 劫持
cdn 或是其他资源文件访问出错
服务器错误
前端代码错误
前端兼容性问题
用户操作出错

通过以上可能发生错误的环节,我们需要向用户手机一下以下的用户信息

当前的网络状态
运营商
地理位置
访问时间
客户端的版本 (如果是通过客户端访问)
系统版本
浏览器信息
设备分辨率
页面的来源
用户的账号信息
通过 performance API 收集用户各个页面访问流程所消耗的时间
收集用户 js 代码报错的信息

如果我们使用了脚本代码压缩,然而我们又不想将 sourcemap 文件发布到线上,我们怎么捕获到错误的具体信息?
CSS 文件中也存在引用资源,@font-face, background-image … 等这些请求错误该如何进行错误捕获?

总结

Web 规范中相关前端异常

DOM 处理异常
ECMAScript 处理异常

异常按照捕获方式分类

运行时异常
资源加载异常
异步请求异常
Promise 异常

异常的捕获方式

try-catch (ES 提供基本的错误捕获语法)

只能捕获同步代码的异常
回调
setTimeout
promise

window.onerror = cb (DOM0)

img
script
link

window.addEventListener(‘error’, cb, true) (DOM2)
window.addEventListener(“unhandledrejection”, cb) (DOM4)
Promise.then().catch(cb)
封装 XMLHttpRequest&fetch | 覆写请求接口对象

注意点:跨源脚本异常的捕获

日志上报的方式

异步请求上报
new img 上报

扩展阅读

业界已有的异常监控平台
几个跟异常监控有关的问题

正文完
 0