前端监控之异常监控(一)
前言
当我们的项目中假设出现了下面几种场景:
- 点击按钮后,页面无响应
- 页面跳转后显示白屏
- 页面卡顿
- ......
这些情况都是非常影响用户体验的,对于用户来说,是难以接受的,用户可能就此流失掉了。
因此前端非常有必要针对异常做一下处理!!
一、什么是异常?
异常(Exception)。在程序中,因为语法的疏忽或其他原因而导致程序中途崩溃的情况,称之为异常。
二、异常的类型
从根本上来说,异常就是一个数据类型,它有一个Error类,而其他异常则是它的子类。
在JS运行时可能会发生的错误有很多类型,每一种错误都有对应的错误类型,而当错误发生的时候就会抛出响应的错误对象。
以下是ECMA-262 白皮书 13 版中描述了 8 种异常类型:
- Error:异常基类,其他错误都继承自该类型
- SyntaxError:语法异常
- ReferenceError:引用异常,尝试引用一个未被定义的变量时,将会抛出此异常
- RangeError:范围异常(数组越界)
- InternalError:内部异常
- TypeError: 类型异常,用来表示值的类型非预期类型时发生的错误
- EvalError: Eval方法异常
- URIError: URI 相关方法产生的异常
三、异常的捕获
1、try-catch
ECMA-262 第 3 版中引入了 try-catch 语句,作为 JavaScript 中处理异常的一种标准方式,基本的语法如下所示。这和 Java 中的 try-catch 语句是全完相同的。
try {// 可能会导致错误的代码
} catch (error) {// 在错误发生时怎么处理
}
如果 try 块中的任何代码发生了错误,就会立即退出代码执行过程,然后执行 catch 块。此时 catch 块会接收到一个包含错误信息的对象,这个对象中包含的信息因浏览器而异,但共同的是有一个保存着错误信息的 message 属性。
finally 子句在 try-catch 语句中是可选的,但是 finally 子句一经使用,其代码无论如何都会执行。换句话说,try 语句块中代码全部正常执行,finally 子句会执行;如果因为出错执行了 catch 语句,finally 子句照样会执行。只要代码中包含 finally 子句,则无论 try 或 catch 语句中包含什么代码——甚至是 return 语句,都不会阻止 finally 子句执行。来看下面函数的执行结果:
function testFinally {try {return "出去玩";} catch (error) {return "看电视";} finally {return "做作业";}return "睡觉";
}
表面上调用这个函数会返回 "出去玩",因为返回 "出去玩" 的语句位于 try 语句块中,而执行此语句又不会出错。实际上返回 "做作业",因为最后还有 finally 子句,结果就会导致 try 块里的 return 语句被忽略,也就是说调用的结果只能返回 "做作业"。如果把 finally 语句拿掉,这个函数将返回 "出去玩"。因此,在使用 finally 子句之前,一定要非常清楚你想让代码怎么样。(思考一下如果 catch 块和 finally 块都抛出异常,catch 块的异常是否能抛出)
但令人遗憾的是,try-catch 无法处理异步代码和一些其他场景。接下来让我具体分析几种异常场景及其处理方案。
2、window.onerror
当 JS 运行时错误发生时,window 会触发一个 ErrorEvent 接口的 error 事件,并执行window.onerror()。
/*** @param {String} message 错误信息* @param {String} source 出错文件* @param {Number} lineno 行号* @param {Number} colno 列号* @param {Object} error Error对象(对象)*/
window.onerror = function (message, source, lineno, colno, error) {console.log('捕获到异常:', { message, source, lineno, colno, error })
}
同步错误可以捕获到,但是,请注意 window.error 无法捕获静态资源异常和 JS 代码错误。
3、静态资源加载异常
方法一:onerror 来捕获
<script>
function errorHandler(error) {console.log('捕获到静态资源加载异常', error)
}
</script>
<script src="http://cdn.xxx.com/js/test.js" onerror="errorHandler(this)"></script>
<link rel="stylesheet" href="http://cdn.xxx.com/styles/test.css" onerror="errorHandler(this)" />
这样可以拿到静态资源的错误,但缺点很明显,代码的侵入性太强了,每一个静态资源标签都要加上 onerror 方法。
方法二:addEventListener("error")
<!DOCTYPE html>
<html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>error</title><script>window.addEventListener('error', (error) => {console.log('捕获到异常:', error);}, true)</script>
</head><body><img src="https://itemcdn.zcycdn.com/15af41ec-e6cb-4478-8fad-1a47402f0f25.png">
</body></html>
由于网络请求异常不会事件冒泡,因此必须在捕获阶段将其捕捉到才行,但是这种方式虽然可以捕捉到网络请求的异常,但是无法判断 HTTP 的状态是 404 还是其他比如 500 等等,所以还需要配合服务端日志才进行排查分析才可以。
4、Promise 异常
Promise 中的异常不能被 try-catch 和 window.onerror 捕获,这时候我们就需要监听 unhandledrejection 来帮我们捕获这部分错误。
window.addEventListener('unhandledrejection', function (e) {e.preventDefault()console.log('捕获到 promise 错误了')console.log('错误的原因是', e.reason)console.log('Promise 对象是', e.promise)return true
})Promise.reject('promise error')
new Promise((resolve, reject) => {reject('promise error')
})
new Promise((resolve) => {resolve()
}).then(() => {throw 'promise error'
})
5、请求异常
以最常用的 HTTP 请求库 axios 为例,模拟接口响应 401 的情况:
// 请求
axios.get('/api/test/401')
Uncaught (in promise) Error: Request failed with status code 401
at createError (axios.js:1207)
at settle (axios.js:1177)
at XMLHttpRequest.handleLoad (axios.js:1037)
可以看出来 axios 的异常可以当做 Promise 异常来处理:
// 请求
axios.get('http://localhost:3000/api/uitest/sentry/401')
.then((data) => console.log('接口请求成功', data))
.catch((e) => console.log('接口请求出错', e))
# 结果
接口请求出错 Error: Request failed with status code 401
at createError (createError.js:17)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:62)
一般接口 401 就代表用户未登录,就需要跳转到登录页,让用户进行重新登录,但如果每个请求方法都需要写一遍跳转登录页的逻辑就很麻烦了,这时候就会考虑使用 axios 的拦截器来做统一梳理,同理能统一处理的异常也可以在放在拦截器里处理。
// Add a response interceptor
axios.interceptors.response.use(function (response) {// Any status codes that falls outside the range of 2xx cause this function to trigger// Do something with response error},function (error) {if (error.response.status === 401) {goLogin() // 跳转登录页} else if (error.response.status === 502) {alert(error.response.data.message || '系统升级中,请稍后重试')}return Promise.reject(error.response)}
)
6、React 异常
React 处理异常的方式不同。虽然 try-catch 适用于许多非普通 JavaScript 应用程序,但它只适用于命令式代码。因为 React 组件是声明性的,所以 try-catch 不是一个可靠的选项。为了弥补这一点,React 实现了所谓的错误边界。错误边界是 React 组件,它“捕获子组件树中的任何地方的 JavaScript 错误”,同时还记录错误并显示回退用户界面。
class ErrorBoundary extends React.Component {constructor(props) {super(props)this.state = { hasError: false }}componentDidCatch(error, info) {// 展示出错的UIthis.setState({ hasError: true }) // 将错误信息上报到日志服务器logErrorToMyService(error, info)}render() {if (this.state.hasError) {// 可以展示自定义的错误样式return <h1>Something went wrong.</h1>}return this.props.children}
}
但是需要注意的是, error boundaries 并不会捕捉下面这些错误:
- 事件处理器
- 异步代码
- 服务端的渲染代码
- 在 error boundaries 区域内的错误
我们可以这样使用 ErrorBoundary:
<ErrorBoundary><MyWidget />
</ErrorBoundary>
7、Vue 异常
全局异常捕获
Vue.config.errorHandler
Vue.config.errorHandler = (err, vm, info) => {console.error('通过vue errorHandler捕获的错误')console.error(err)console.error(vm)console.error(info)
}
组件内异常捕获
组件内使用errorCaptured
8、总结
异常处理时需分清是致命错误还是非致命错误。
- 可疑区域增加 try-catch
- 全局监控 JS 异常 window.onerror
- 全局监控静态资源异常 window.addEventListener
- 捕获没有 catch 的 Promise 异常用 unhandledrejection
- Vue errorHandler 和 React componentDidCatch
- Axios 请求统一异常处理用拦截器 interceptors
- 使用日志监控服务收集用户错误信息
四、异常上报
异常捕获之后,接下来就是数据上报的工作了。
目前主要由以下3种上报信息方式:
| 方案 | 优点 | 缺点 |
| img请求 | 兼容性 | 部分浏览器丢点,延迟页面卸载; get请求长度限制 |
| Fetch/XHR | 兼容性 | Fetch丢点同步 XHR不丢点,但延迟页面卸载 |
| Navigator.sendBeacon() | 不丢点不会延迟页面卸载 | 兼容性 |
丢点:在浏览器点击跳转时,跳转前的点击上报请求都会进行一个三次握手,如果此时,网络较慢、服务器运行缓慢或者上报请求还在处理阶段,这时,如果页面被卸载了,浏览器都会自动对当前的请求进行abort。这样,这个http的请求就没有建立,导致上报没有真正发出。
下面的例子展示了一个理论上的统计代码——在卸载事件处理器中尝试通过一个同步的 XMLHttpRequest 向服务器发送数据。这导致了页面卸载被延迟。
window.addEventListener('unload', logData, false)
function logData() {var client = new XMLHttpRequest()client.open('POST', '/log', false) // 第三个参数表明是同步的 xhrclient.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8')client.send(analyticsData)
}
这就是 sendBeacon() 方法存在的意义。使用 sendBeacon() 方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。此外,代码实际上还要比其他技术简单许多!
下面的例子展示了一个理论上的统计代码模式——通过使用 sendBeacon() 方法向服务器发送数据。
window.addEventListener('unload', logData, false);function logData() {navigator.sendBeacon("/log", analyticsData);
}
所以这里的推荐是优先检查浏览器是否支持Navigator.sendBeacon(),不支持的话再使用其他上报方式。
参考资料:
ecma-262: https://www.ecma-international.org/publications-and-standards/standards/ecma-262/
ES6th 白皮书: https://262.ecma-international.org/6.0
https://juejin.cn/post/6932620551827488775
https://juejin.cn/post/6936562262480158728
相关文章:
前端监控之异常监控(一)
前言 当我们的项目中假设出现了下面几种场景: 点击按钮后,页面无响应页面跳转后显示白屏页面卡顿...... 这些情况都是非常影响用户体验的,对于用户来说,是难以接受的,用户可能就此流失掉了。 因此前端非常有必要针对…...
sql server 、mysql CTE 公用表表达式
sql server 详细 mysql CTE CTE 是一个命名的临时结果集,作用范围是当前语句。CTE可以理解成一个可以复用的子查询,当然跟子查询还是有点区别的,CTE可以引用其他CTE,但子查询不能引用其它子查询。所以,开发中建议…...
Oracle dataguard 和Oracle rac的区别和联系
RAC服务器共用一套存储,同时提供服务,没有主备之分.宕一个其它的可以继续服务. 双机热备,共用一套存储,一个提供服务一个备份,主机宕了切换到备份服务器提供服务. data guard 完全两套系统,存储是单独的,用日志同步. RAC: 实例层冗余 DG :数据库层冗…...
JUC工具类-LockSupport概述
前言 多线程并发场景中,时常需要线程协同,故而需要对当前线程进行阻塞,并唤醒需要协同的线程来一起完成任务。 通常处理方式有三种: 1)Synchronized加锁的线程 使用Object类下所提供的方法: wai…...
大数据:AI大模型对数据分析领域的颠覆(文末送书)
随着数字化时代的到来,大数据已经成为了各行各业中不可或缺的资源。然而,有效地分析和利用大数据仍然是一个挑战。在这个背景下,OpenAI推出的Code Interpreter正在对数据分析领域进行颠覆性的影响。 如何颠覆数据分析领域?带着这…...
CEdit 选中文字实时更新到另一个控件中
有时候,我们会遇到需求,软件中需要让选中一个CEdit控件中的文字实时更新到另一个控件中,实现效果如下所示: 代码如下: BOOL CEditDemoDlg::PreTranslateMessage(MSG* pMsg) { CEdit* pOldEdit (CEdit*)GetDlgIte…...
Word导出创建Adobe PDF其中emf图片公式马赛克化及文字缺失
软件版本 Word 2021 Visio 2019 Adobe Acrobat Pro 2020 问题描述 公式马赛克化,是指在Word中使用MathType编辑的公式,然后在Visio中使用图片(增强型图元文件)形式得到的粘贴对象,效果如下 文字缺失,是指Word导出→创建Adobe P…...
[matlab]matlab配置mingw64编译器
第一步:下载官方绿色版本mingw64编译器然后解压放到一个非中文空格路径下面 比如我mingw64-win是我随便改的文件名,然后添加环境变量,选择用户或者系统环境变量添加下面的变量 变量名: MW_MINGW64_LOC 变量值:自己的m…...
华为OD-非严格递增连续数字序列
题目描述 输入一个字符串仅包含大小写字母和数字 求字符串中包含的最长的非严格递增连续数字序列长度 比如: 12234属于非严格递增数字序列 输入描述 输入一个字符串仅包含大小写字母和数字 输出描述 输出字符串中包含的最长的非严格递增连续数字序列长度 示例…...
css滚动条样式这样修改下很漂亮
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>滚动条样式修改下很漂亮(不支持IE)</title> <style type"text/css"> * { margin: 0; padding: 0; } .box { width: 300px; height: 100px; margin…...
转置卷积的应用
目录 矩阵转置 一、转置卷积的背景 二、转置卷积的应用 三、转置卷积的区别 卷积 矩阵转置 矩阵的转置在信息处理中起到了重要的作用。在计算机科学领域,矩阵常用于表示图像、音频和视频等多媒体数据。当我们需要对这些数据进行处理时,常常需要进行…...
常见的移动端布局
流式布局(百分比布局) 使用百分比、相对单位(如 em、rem)等来设置元素的宽度,使页面元素根据视口大小的变化进行调整。这种方法可以实现基本的自适应效果,但可能在不同设备上显示不一致。 <!DOCTYPE ht…...
Typore 亲测有效(懂得都懂哈)
Typore 亲测从安装到使用,可以使用(具体是什么懂得都懂哈) 网盘下载地址:链接:https://pan.baidu.com/s/1w0UiS1szxnO9Lxz6sbXEKg?pwdqwe1 提取码:qwe1 第一步: 下载压缩包进行解压,解压过…...
Kyligence Copilot 登陆海外,斩获 Product Hunt 日榜 TOP 2
8月14日,AI 数智助理 Kyligence Copilot 在全球知名科技产品平台 Product Hunt 上线,其以出色的产品创新实力,在激烈的竞争中脱颖而出,仅仅在 24 小时内收获了超过 400 个投票和近 200 条支持评论,荣登当日产品榜排名第…...
【Docker】Docker 的基本概念和优势,基本命令及使用例子
Docker 是一种轻量级的容器化解决方案,能够快速地创建、部署和运行应用程序。以下是一些 Docker 的基本概念和优势: 基本概念: 1.镜像:一个 Docker 镜像是一个可执行的文件,其中包含了运行应用程序所需要的一切。 2.容…...
高并发内存池(回收)[4]
threadcache还给centralcache void ThreadCache::Deallocate(void* ptr, size_t size) {assert(ptr);assert(size < MAX_BYTES);// 找对映射的自由链表桶,对象插入进入size_t index SizeClass::Index(size);_freeLists[index].Push(ptr);// 当链表长度大于一次…...
分布式事务篇-2.4 Spring-Boot整合Seata
文章目录 前言一、pom jar导入:二、项目配置:2.1 配置 说明:2.1 .1 seata server 端:2.1 .2 seata client 端: 2.2 开启seata 对于数据源的代理:2.3 seata-client 的注册中心:2.4 seata-client 的配置中心:2.5 去掉手写的数据源代…...
718. 最长重复子数组
718. 最长重复子数组 原题链接:完成情况:题解:方法一:动态规划方法二:滑动窗口方法三:二分查找 哈希 原题链接: 718. 最长重复子数组 https://leetcode.cn/problems/maximum-length-of-repe…...
Mysql join加多条件与where的区别
最近在项目中遇到一个问题,感觉有点意思,在解决问题及查阅了相关资料后,打算写篇文章给朋友们分享一下。 问题现象: 问题是很常见的空指针问题,后端查询数据库数据,遍历进行相关业务处理时报空指针。通过…...
div滚动条自动滚动到底部
<div id"center"></div>// 滚动条到最底部scrollToBottom(){var box document.getElementById(center);this.$nextTick(() > {box.scrollTop box.scrollHeight})},...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
