如何搭建前端异常监控系统
原文地址:https://segmentfault.com/a/1190000023096077 原文作者:发声的沉默者
什么是异常
是指用户在使用应用时,无法得到预期的结果。不同的异常带来的后果程度不同,轻则引起用户使用不悦,重则导致产品无法使用,从而使用户丧失对产品的认可。
为什么要处理异常
- 增强用户体验
- 远程定位问题
- 无法复现问题,特别是移动端,各种原因,可能是系统版本,机型等等
前端有哪些异常
如何捕获异常
try-catch
try-catch 只能捕获同步运行错误,对语法和异步错误却捕获不到。
1、同步运行错误
try {
kill;
} catch(err) {
console.error('try: ', err);
}
结果:try: ReferenceError: kill is not defined
2、无法捕获语法错误
try {
let name = '1;
} catch(err) {
console.error('try: ', err);
}
结果:Unterminated string constant
编译器能够阻止运行语法错误。
3、无法捕获异步错误
try {
setTimeout(() => {
undefined.map(v => v);
}, 1000);
} catch(err) {
console.error('try: ', err);
}
结果:Uncaught TypeError: Cannot read property 'map' of undefined
window.onerror
当JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的error事件,并执行window.onerror()。若该函数返回true,则阻止执行默认事件处理函数。
1、同步运行错误
/**
* @param {String} message 错误信息
* @param {String} source 出错文件
* @param {Number} lineno 行号
* @param {Number} colno 列号
* @param {Object} error error对象
*/
window.onerror = (message, source, lineno, colno, error) => {
console.error('捕获异常:', message, source, lineno, colno, error);
return true;
};
kill;
结果:捕获异常:Uncaught ReferenceError: kill is not defined
2、无法捕获语法错误
/**
* @param {String} message 错误信息
* @param {String} source 出错文件
* @param {Number} lineno 行号
* @param {Number} colno 列号
* @param {Object} error error对象
*/
window.onerror = (message, source, lineno, colno, error) => {
console.error('捕获异常:', message, source, lineno, colno, error);
return true;
};
let name = '1;
结果:Unterminated string constant
编译器能够阻止运行语法错误。
3、异步错误
/**
* @param {String} message 错误信息
* @param {String} source 出错文件
* @param {Number} lineno 行号
* @param {Number} colno 列号
* @param {Object} error error对象
*/
window.onerror = (message, source, lineno, colno, error) => {
console.error('捕获异常:', message, source, lineno, colno, error);
return true;
};
setTimeout(() => {
undefined.map(v => v);
}, 1000);
结果:捕获异常:Uncaught TypeError: Cannot read property 'map' of undefined
window.addEventListener('error')
当一项资源(如<img>或<script>)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过(至少在Firefox中)能被单一的window.addEventListener捕获。
<script>
window.addEventListener('error', (err) => {
console.error('捕获异常:', err);
}, true);
</script>
<img src="./test.jpg" />
结果:捕获异常:Event {isTrusted: true, type: "error", target: img, currentTarget: Window, eventPhase: 1, …}
window.addEventListener('unhandledrejection')
当Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件;这可能发生在 window 下,但也可能发生在 Worker 中。这对于调试回退错误处理非常有用。
window.addEventListener("unhandledrejection", (err) => {
err.preventDefault();
console.error('捕获异常:', err);
});
Promise.reject('promise');
结果:捕获异常:PromiseRejectionEvent {isTrusted: true, promise: Promise, reason: "promise", type: "unhandledrejection", target: Window, …}
Vue
Vue.config.errorHandler = (err, vm, info) => {
console.error('捕获异常:', err, vm, info);
}
React
React 16,提供了一个内置函数componentDidCatch,使用它可以非常简单的获取到React下的错误信息。
componentDidCatch(error, info) {
console.error('捕获异常:', error, info);
}
但是,推荐ErrorBoundary
用户界面中的JavaScript错误不应破坏整个应用程序。为了为React用户解决此问题,React 16引入了“错误边界”的新概念。
新建ErrorBoundary.jsx组件:
import React from 'react';
import { Result, Button } from 'antd';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, info: '' };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
this.setState({
info: error + ''
});
}
render() {
if (this.state.hasError) {
// 你可以渲染任何自定义的降级 UI
return (
<Result
status="500"
title="500"
subTitle={this.state.info}
extra={<Button type="primary">Report feedback</Button>}
/>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
使用:
<ErrorBoundary>
<App />
</ErrorBoundary>
注意
错误边界不会捕获以下方面的错误:
- 事件处理程序
- 异步代码(例如setTimeout或requestAnimationFrame回调)
- 服务器端渲染
- 在错误边界本身(而不是其子级)中引发的错误
iframe
由于浏览器设置的“同源策略”,无法非常优雅的处理iframe异常,除了基本属性(例如其宽度和高度)之外,无法从iFrame获得很多信息。
<script>
document.getElementById("myiframe").onload = () => {
const self = document.getElementById('myiframe');
try {
(self.contentWindow || self.contentDocument).location.href;
} catch(err) {
console.log('捕获异常:' + err);
}
};
</script>
<iframe id="myiframe" src="https://nibuzhidao.com" frameBorder="0" />
Sentry
业界非常优秀的一款监控异常的产品,作者也是用的这款,文档齐全。
需要上报哪些信息
- 错误id
- 用户id
- 用户名
- 用户IP
- 设备
- 错误信息
- 游览器
- 系统版本
- 应用版本
- 机型
- 时间戳
- 异常级别(error、warning、info)
异常上报
- 1、Ajax发送数据
- 2、动态创建img标签
如果异常数据量大,导致服务器负载高,调整发送频率(可以考虑把异常信息存储在客户端,设定时间阀值,进行上报)或设置采集率(采集率应该通过实际情况来设定,随机数,或者某些用户特征都是不错的选择)。
- 如何用Wireshark捕获USB数据?
- QEMU 2: 参数解析
- [WCF安全系列]认证与凭证:X.509证书
- 深度学习:能击败欧洲围棋冠军,还能防恶意软件
- Metasploit中的JAVA反向TCP做法的研究
- 商业级别Fortify白盒神器介绍与使用分析
- [WCF安全系列]消息的保护等级[上篇]
- QEMU 1: 使用QEMU创建虚拟机
- [WCF安全系列]绑定、安全模式与客户端凭证类型:NetNamedPipeBinding、NetTcpBinding与NetMsmqBinding
- 操作系统级虚拟化概述
- 让javascript中的异步请求同步起来
- [WCF REST] WebHttpBinding与消息编码
- React 概要
- [WCF REST] UriTemplate、UriTemplateTable与WebHttpDispatchOperationSelector
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- java堆内存详解
- springBoot 入门(一)—— 使用idea创建第一个springBoot项目
- “dddb超级”工具包——高效、快速开发JavaWeb项目后端结构
- 在Java Web中设计的编解码
- 怎么让用一行代码实现页面的定时强制刷新?脚本刷流量再也不用愁了!
- 什么是Javac
- springBoot 入门(二)—— 使用 spring.profiles.active来区分配置
- Kubernetes 1.19.0——deployment(2)
- JavaWeb新手进阶经典项目 & 半小时高效开发 & 海量知识点涵盖 (二)
- Java web 开发 Session超时设置
- JavaWeb第四讲 会话跟踪技术HttpSession、Cookie、url、隐藏表单域
- springBoot 入门(三)—— 使用 RestController
- SSM第一讲 Spring概述和基础知识详解
- springBoot 入门(四)—— 使用 纯注解方式的junit整合测试
- ClassLoader工作机制