实现一个简易版的前端监控 SDK

【简易版的前端监控系统】

1、Promise的错误如何监控?–promise不是所有都是接口请求
2、接口的报错如何监控?–全局监控sdk,不改动公共的请求方法、不改动业务代码;一般接口使用axios请求
3、资源的报错如何监控?
4、监控: 埋点上报报错

注意:
(1)埋点监控报错死循环报错 – 重试机制、另一个埋点
(2)运行监控代码如何判断Vue/React,Vue/React有无内部监控api直接调用?
(3)window.error?? 能否捕获到接口的错误?
(4)所有监控放到同一个SDK监控

答:
(2)判断是否存在 Vue/React 可以通过检查 window.Vuewindow.React 是否定义。
Vue: 有内部监控 API,可通过 Vue.config.errorHandler 捕获 Vue 实例中的错误。
React: 类组件可用 ErrorBoundary 捕获子组件错误,函数组件实验性地能用 useErrorBoundary Hook 。
(3)window.onerror 不能捕获接口的错误。接口请求通常使用 XMLHttpRequestfetch,其错误会在各自的回调或 Promise 中处理,不会触发 window.onerror

  • 【整体思路】:

SDK 监控错误是通过多种方式实现的,具体如下:

try...catch:用于在可预见的代码块中捕获特定错误,例如在模拟埋点上报时捕获可能出现的错误。

window.onerror:用于捕获预料之外的同步错误,不过不能捕获异步错误。

window.unhandledrejection:专门用于监听和捕获未处理的 Promise 错误。
(在业务代码里,通常:使用 Promise .catch() 处理 Promise 错误;使用 async/await 结合 try…catch 处理 Promise 错误)

网络错误捕获:
(1)XMLHttpRequest:重写 window.XMLHttpRequest 并监听其 error 事件,捕获 XMLHttpRequest 请求的网络错误。
(2)Axios:使用 Proxy 代理重写 axios.request 方法,捕获 Axios 请求的网络错误。

资源加载错误捕获:
重写 window.addEventListener 方法,监听 error 事件,捕获 HTML 资源(如脚本、样式表、图片)加载失败的错误。

// 定义前端监控 SDK 类
class FrontendMonitoringSDK {constructor(options) {this.options = options;this.init();this.monitorVueErrors();this.monitorReactErrors();}// 初始化监控init() {this.monitorPromiseErrors();this.monitorApiErrors();this.monitorResourceErrors();this.monitorWindowErrors();if (this.options.track) {this.monitorTrackErrors(this.options.track);}}// 监控 Promise 错误 -- Promise内部无需重试机制,上报前端监控仍然使用retryReport/*** 通常不建议对 Promise 错误使用重试机制。原因:Promise 错误一般是由代码逻辑错误、异步操作的异常(如数据库查询失败、函数调用参数错误)等引发的。重试并不能解决这些根源问题,反而可能导致程序陷入无限重试的循环,消耗大量资源。例如,在处理 Promise 时,如果是因为传入的参数不符合要求而抛出错误,重试同样的操作依旧会失败。*/monitorPromiseErrors() {window.addEventListener('unhandledrejection', (event) => {this.retryReport({type: 'promise',message: event.reason instanceof Error ? event.reason.message : String(event.reason),stack: event.reason instanceof Error ? event.reason.stack : null});});}// 监控接口错误monitorApiErrors() {const originalXHR = window.XMLHttpRequest;window.XMLHttpRequest = function () {const xhr = new originalXHR();const self = this;xhr.addEventListener('error', function () {self.retryReport({type: 'api',message: `API 请求错误: ${xhr.status} ${xhr.statusText}`,url: xhr.responseURL});});return xhr;}.bind(this);if (window.axios) {const originalAxios = window.axios;const maxRetries = 3;window.axios = new Proxy(originalAxios, {get(target, prop) {if (prop === 'request') {return function (config) {let retries = 0;const makeRequest = () => {return originalAxios.request(config).catch((error) => {if (retries < maxRetries) {retries++;return makeRequest();} else {this.retryReport({type: 'api',message: `Axios 请求错误: ${error.message}`,url: config.url});throw error;}});};return makeRequest();}.bind(this);}return target[prop];}});}}// 监控资源加载错误monitorResourceErrors() {const maxRetries = 3;const originalAddEventListener = window.addEventListener;window.addEventListener = function (type, listener, options) {if (type === 'error') {const newListener = (event) => {if (event.target instanceof HTMLScriptElement || event.target instanceof HTMLLinkElement || event.target instanceof HTMLImageElement) {let retries = 0;const retryResourceLoad = () => {if (retries < maxRetries) {if (event.target instanceof HTMLScriptElement) {const src = event.target.src;event.target.src = '';event.target.src = src;} else if (event.target instanceof HTMLLinkElement) {const href = event.target.href;event.target.href = '';event.target.href = href;} else if (event.target instanceof HTMLImageElement) {const src = event.target.src;event.target.src = '';event.target.src = src;}retries++;} else {this.retryReport({type: 'resource',message: `资源加载错误: ${event.target.src || event.target.href}`,url: event.target.src || event.target.href});}};retryResourceLoad();} else {listener.call(this, event);}};return originalAddEventListener.call(this, type, newListener, options);}return originalAddEventListener.call(this, type, listener, options);}.bind(this);}// 监控全局错误/**1. message: 错误的具体描述信息2. source: 发生错误的脚本文件的 URL;如果错误出现在内联脚本中,返回当前页面的 URL。3. lineno: 错误发生所在行的行号4. colno 错误发生所在列的列号5. error: 一个 Error 对象,它包含了更详尽的错误信息,像错误堆栈(stack)之类的。*/monitorWindowErrors() {window.onerror = (message, source, lineno, colno, error) => {this.retryReport({type: 'window',message: message,stack: error ? error.stack : null,source: source,lineno: lineno,colno: colno});return true;};}// 监控埋点库上报错误monitorTrackErrors(track) {const { Track, config, errorType } = track;const maxRetries = 3;const trackInstance = new Track(config);// 假设库有一个错误回调trackInstance.onError = (error) => {let retries = 0;const retryTrackReport = () => {if (retries < maxRetries) {// 这里需要根据埋点库具体逻辑实现重试上报// 假设埋点库有一个重新上报的方法 retryReportif (trackInstance.retryReport) {trackInstance.retryReport();}retries++;} else {this.retryReport({type: errorType,message: `${errorType} 埋点上报错误: ${error.message}`,stack: error.stack || null});}};retryTrackReport();};}// 监控 Vue 错误monitorVueErrors() {if (typeof window.Vue !== 'undefined') {window.Vue.config.errorHandler = (err, vm, info) => {this.retryReport({type: 'vue',message: err.message,stack: err.stack,info: info});};}}// 监控 React 错误monitorReactErrors() {if (typeof window.React !== 'undefined' && typeof window.ReactDOM !== 'undefined') {const sdk = this;const { useErrorBoundary } = window.React;const ErrorBoundary = ({ children }) => {const { error, resetErrorBoundary } = useErrorBoundary({onError: (error, errorInfo) => {sdk.retryReport({type: 'react',message: error.message,stack: error.stack,info: errorInfo.componentStack});}});if (error) {return window.React.createElement('div', null, 'Something went wrong.');}return children;};// 可以考虑在这里将 ErrorBoundary 包裹在根组件上// 假设根组件是 RootComponentconst originalRender = window.ReactDOM.render;window.ReactDOM.render = function (element, container, callback) {const errorBoundaryWrappedElement = window.React.createElement(ErrorBoundary, null, element);return originalRender.call(this, errorBoundaryWrappedElement, container, callback);};}}// 上报错误reportError(errorData) {const xhr = new XMLHttpRequest();xhr.open('POST', this.options.reportUrl, true);xhr.setRequestHeader('Content-Type', 'application/json');xhr.onreadystatechange = () => {if (xhr.readyState === 4) {if (xhr.status === 200) {console.log('错误上报成功');} else {console.error('错误上报失败');}}};xhr.send(JSON.stringify(errorData));}// 重试上报错误retryReport(errorData) {const maxRetries = 3;let retries = 0;const sendReport = () => {const xhr = new XMLHttpRequest();xhr.open('POST', this.options.reportUrl, true);xhr.setRequestHeader('Content-Type', 'application/json');xhr.onreadystatechange = () => {if (xhr.readyState === 4) {if (xhr.status === 200) {console.log('错误上报成功');} else {if (retries < maxRetries) {retries++;sendReport();} else {console.error('错误上报失败,达到最大重试次数');}}}};xhr.send(JSON.stringify(errorData));};sendReport();}
}// 【使用示例】
// 假设已经引入了 @company/example-tracking 库(业务埋点库)
import Tracking from '@company/example-tracking';const sdk = new FrontendMonitoringSDK({// 错误上报接口地址reportUrl: 'https://your-report-url.com',// 业务埋点track: {Track: Tracking,config: {enable: true,// 业务埋点上报地址domain: 'https://test-maidian.company.cn',mdParams: {cv: new URLSearchParams(window.location.search).get('cv'),md_etype: 'h5log',},},errorType: 'Tracking'}
});
  • 【模拟报错】
// 模拟业务埋点库
class MockTracking {constructor(config) {this.config = config;}// 模拟上报方法report() {try {// 模拟上报失败throw new Error('埋点上报失败');} catch (error) {if (this.onError) {this.onError(error);}}}// 模拟重试上报方法retryReport() {this.report();}// 定义 onError 方法onError(error) {console.log('MockTracking 捕获到错误:', error.message);// 可以在这里添加更多的错误处理逻辑}
}// 初始化 SDK
const sdk = new FrontendMonitoringSDK({// 错误上报接口地址reportUrl: 'https://your-report-url.com',// 业务埋点track: {Track: MockTracking,config: {enable: true,// 业务埋点上报地址domain: 'https://test-maidian.company.cn',mdParams: {cv: new URLSearchParams(window.location.search).get('cv'),md_etype: 'h5log',},},errorType: 'Tracking'}
});// 1. 模拟 Promise 的错误
const promiseError = new Promise((_, reject) => {reject(new Error('Promise 错误'));
});// 2. 模拟接口的报错 -- 使用 axios 请求
import axios from 'axios';
// 模拟一个不存在的接口地址
const apiError = axios.get('https://nonexistent-api-url.com');// 3. 模拟资源的报错
const script = document.createElement('script');
script.src = 'https://nonexistent-script-url.js';
document.body.appendChild(script);// 4. 模拟埋点上报报错
const trackInstance = new MockTracking({enable: true,domain: 'https://test-maidian.company.cn',mdParams: {cv: new URLSearchParams(window.location.search).get('cv'),md_etype: 'h5log',},
});
// 业务代码调用时无需再写 try...catch
trackInstance.report();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75077.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【操作系统】软中断vs硬中断

在操作系统中&#xff0c;中断&#xff08;Interrupt&#xff09; 是 CPU 响应外部事件的重要机制&#xff0c;分为 硬中断&#xff08;Hardware Interrupt&#xff09; 和 软中断&#xff08;Software Interrupt&#xff09;。它们的核心区别在于 触发方式 和 处理机制。 1. 硬…

力扣刷题-热题100题-第27题(c++、python)

21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/merge-two-sorted-lists/description/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 创建一个新链表&#xff0c;遍历list1与list2&#xff0c;将新链表指向list1与list2…

Python包下载路径 Chrome用户数据 修改到非C盘

查看 site-packages 是否能通过命令行完成&#xff1f; 可以&#xff0c;使用以下命令&#xff08;不需写脚本&#xff09;&#xff1a; python -m site输出包含&#xff1a; sys.path site-packages 路径&#xff08;全局和用户级&#xff09; 如果只想看安装路径&#…

【鸿蒙5.0】鸿蒙登录界面 web嵌入(隐私页面加载)

在鸿蒙应用中嵌入 Web 页面并加载隐私页面&#xff0c;可借助 WebView 组件来实现。以下是一个完整示例&#xff0c;展示如何在鸿蒙 ArkTS 里嵌入 Web 页面并加载隐私政策页面。 在 HarmonyOS 应用开发中&#xff0c;如果你希望嵌入一个网页&#xff0c;并且特别关注隐私页面加…

AI加Python的文本数据情感分析流程效果展示与代码实现

本文所使用数据来自于梯田景区评价数据。 一、数据预处理 数据清洗 去除重复值、空值及无关字符(如表情符号、特殊符号等)。 提取中文文本,过滤非中文字符。 统一文本格式(如全角转半角、繁体转简体)。 中文分词与去停用词 使用 jieba 分词工具进行分词。 加载自定义词…

Microi吾码界面设计引擎之基础组件用法大全【内置组件篇·上】

&#x1f380;&#x1f380;&#x1f380; microi-pageengine 界面引擎系列 &#x1f380;&#x1f380;&#x1f380; 一、Microi吾码&#xff1a;一款高效、灵活的低代码开发开源框架【低代码框架】 二、Vue3项目快速集成界面引擎 三、Vue3 界面设计插件 microi-pageengine …

【多线程】单例模式和阻塞队列

目录 一.单例模式 1. 饿汉模式 2. 懒汉模式 二.阻塞队列 1. 阻塞队列的概念 2. BlockingQueue接口 3.生产者-消费者模型 4.模拟生产者-消费者模型 一.单例模式 单例模式&#xff08;Singleton Pattern&#xff09;是一种常用的软件设计模式&#xff0c;其核心思想是确保…

终值定理的推导与理解

终值定理的推导与理解 终值定理是控制理论和信号处理中的一个重要工具&#xff0c;它通过频域的拉普拉斯变换来分析时间域函数的最终稳态值。具体来说&#xff0c;终值定理提供了一个简便的方法&#xff0c;利用 F ( s ) F(s) F(s)&#xff08; f ( t ) f(t) f(t) 的拉普拉斯…

每日c/c++题 备战蓝桥杯(二分答案模版)

在算法学习中&#xff0c;二分答案算法是一种非常高效且常用的技巧。它的核心思想是通过不断缩小搜索范围&#xff0c;逐步逼近目标答案。相比传统的暴力搜索&#xff0c;二分答案算法的时间复杂度通常为 O(logn)&#xff0c;特别适合处理大规模数据的查找问题。 本文将详细介…

NLP高频面试题(二十六)——RAG的retriever模块作用,原理和目前存在的挑战

在自然语言处理领域&#xff0c;检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;简称RAG&#xff09;是一种将信息检索与文本生成相结合的技术&#xff0c;旨在提升模型的回答准确性和信息丰富度。其中&#xff0c;Retriever在RAG架构中扮演着关键角色&am…

第30周Java分布式入门 分布式基础

分布式基础课程笔记 一、什么是分布式&#xff1f; 1. 权威定义 分布式系统定义为&#xff1a;“利用物理架构形成多个自治的处理元素&#xff0c;不共享主内存&#xff0c;通过发送消息合作”。 2. 核心解释 物理架构与处理元素 &#x1f31f; 多台独立服务器/电脑&#x…

Vuex状态管理

Vuex Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式管理应用的所有组件状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。&#xff08;类似于在前端的数据库&#xff0c;这里的数据存储在内存当中&#xff09; 一、安装并配置 在项目的…

从代码学习深度学习 - 使用块的网络(VGG)PyTorch版

文章目录 前言一、VGG网络简介1.1 VGG的核心特点1.2 VGG的典型结构1.3 优点与局限性1.4 本文的实现目标二、搭建VGG网络2.1 数据准备2.2 定义VGG块2.3 构建VGG网络2.4 辅助工具2.4.1 计时器和累加器2.4.2 准确率计算2.4.3 可视化工具2.5 训练模型2.6 运行实验总结前言 深度学习…

Baklib激活企业知识管理新动能

Baklib核心技术架构解析 Baklib的底层架构以模块化设计为核心&#xff0c;融合知识中台的核心理念&#xff0c;通过分布式存储引擎与智能语义分析系统构建三层技术体系。数据层采用多源异构数据接入协议&#xff0c;支持文档、音视频、代码片段等非结构化数据的实时解析与分类…

小智机器人中的部分关键函数,FreeRTOS中`xEventGroupWaitBits`函数的详细解析

以下是对FreeRTOS中xEventGroupWaitBits函数的详细解析&#xff1a; 函数功能 xEventGroupWaitBits用于在事件组中等待指定的位被设置。它可以配置为等待任意一个位或所有位&#xff0c;并支持超时机制。 注意&#xff1a;该函数不能在中断中调用。 函数原型 EventBits_t xEv…

关注分离(Separation of Concerns)在前端开发中的实践演进:从 XMLHttpRequest 到 Fetch API

关注分离&#xff08;Separation of Concerns&#xff09;在前端开发中的实践演进&#xff1a;从 XMLHttpRequest 到 Fetch API 一、关注分离的核心价值 关注分离&#xff08;SoC&#xff09;是软件工程领域的重要设计原则&#xff0c;强调将系统分解为不同维度的功能模块&am…

C之(16)scan-build与clang-tidy使用

C之(16)scan-build与clang-tidy使用 Author: Once Day Date: 2025年3月29日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践记录_Once_da…

在 Vue 项目中快速集成 Vant 组件库

目录 引言一、找到 src 下的App.js 写入代码。二、安装Vant三、解决 polyfill 问题四、查看依赖五、配置webpack六、引入 Vant七、在组件中使用 Vant八、在浏览器中查看样式总结 引言 在开发移动端 Vue 项目时&#xff0c;选择一个高效、轻量且功能丰富的组件库是提升开发效率…

“GPU 挤不动了?”——聊聊基于 GPU 的计算资源管理

“GPU 挤不动了?”——聊聊基于 GPU 的计算资源管理 作者:Echo_Wish “老板:为什么 GPU 服务器卡得跟 PPT 一样?” “运维:我们任务队列爆炸了,得优化资源管理!” 在 AI 训练、深度学习、科学计算的场景下,GPU 计算资源已经成为香饽饽。但 GPU 服务器贵得离谱,一台 A…

AI渗透测试:网络安全的“黑魔法”还是“白魔法”?

引言&#xff1a;AI渗透测试&#xff0c;安全圈的“新魔法师” 想象一下&#xff0c;你是个网络安全新手&#xff0c;手里攥着一堆工具&#xff0c;正准备硬着头皮上阵。这时&#xff0c;AI蹦出来&#xff0c;拍着胸脯说&#xff1a;“别慌&#xff0c;我3秒扫完漏洞&#xff0…