實現一個簡易版的前端監控 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.pswp.cn/bicheng/75077.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75077.shtml
英文地址,請注明出處:http://en.pswp.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…