摘要:摘要徒手寫錯(cuò)誤監(jiān)控。為什么用定時(shí)器呢,因?yàn)樵趩雾搼?yīng)用中,路由的切換和地址欄的變化是無法被監(jiān)控的,我確實(shí)沒有想到特別好的辦法來監(jiān)控,所以用了這種方式,如果有人有更好的辦法,請(qǐng)給我留言,謝謝。
摘要: 徒手寫JS錯(cuò)誤監(jiān)控。
作者:一步一個(gè)腳印一個(gè)坑
原文:搭建前端監(jiān)控系統(tǒng)(二)JS錯(cuò)誤監(jiān)控篇
Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。
背景:市面上的監(jiān)控系統(tǒng)有很多,大多收費(fèi),對(duì)于小型前端項(xiàng)目來說,必然是痛點(diǎn)。另一點(diǎn)主要原因是,功能通用,卻未必能夠滿足我們自己的需求, 所以我們自給自足。
這是搭建前端監(jiān)控系統(tǒng)的第二章,主要是介紹如何統(tǒng)計(jì)js報(bào)錯(cuò),跟著我一步步做,你也能搭建出一個(gè)屬于自己的前端監(jiān)控系統(tǒng)。
請(qǐng)移步線上:前端監(jiān)控系統(tǒng)
對(duì)于前端應(yīng)用來說,Js錯(cuò)誤的發(fā)生直接影響前端應(yīng)用的質(zhì)量。對(duì)前端異常的監(jiān)控是整個(gè)前端監(jiān)控系統(tǒng)中的一個(gè)重要環(huán)節(jié)。前端異常包含很多種情況:1. js編譯時(shí)異常(開發(fā)階段就能排)2. js運(yùn)行時(shí)異常;3. 加載靜態(tài)資源異常(路徑寫錯(cuò)、資源服務(wù)器異常、CDN異常、跨域)4. 接口請(qǐng)求異常等。這一篇我們只介紹Js運(yùn)行時(shí)異常。
監(jiān)控流程:監(jiān)控錯(cuò)誤 -> 搜集錯(cuò)誤 -> 存儲(chǔ)錯(cuò)誤 -> 分析錯(cuò)誤 -> 錯(cuò)誤報(bào)警-> 定位錯(cuò)誤 -> 解決錯(cuò)誤
首先,我們應(yīng)該對(duì)Js報(bào)錯(cuò)情況有個(gè)大致的了解,這樣才能夠及時(shí)的了解前端項(xiàng)目的健康狀況。所以我們需要分析出一些必要的數(shù)據(jù)。
如:一段時(shí)間內(nèi),應(yīng)用JS報(bào)錯(cuò)的走勢(shì)(chart圖表)、JS錯(cuò)誤發(fā)生率、JS錯(cuò)誤在PC端發(fā)生的概率、JS錯(cuò)誤在IOS端發(fā)生的概率、JS錯(cuò)誤在Android端發(fā)生的概率,以及JS錯(cuò)誤的歸類。
然后,我們?cè)偃テ渲械腏s錯(cuò)誤進(jìn)行詳細(xì)的分析,輔助我們排查出錯(cuò)的位置和發(fā)生錯(cuò)誤的原因。
如:JS錯(cuò)誤類型、 JS錯(cuò)誤信息、JS錯(cuò)誤堆棧、JS錯(cuò)誤發(fā)生的位置以及相關(guān)位置的代碼;JS錯(cuò)誤發(fā)生的幾率、瀏覽器的類型,版本號(hào),設(shè)備機(jī)型等等輔助信息
一、JS Error 監(jiān)控功能 (數(shù)據(jù)概覽)為了得到這些數(shù)據(jù),我們需要在上傳的時(shí)候?qū)⑵浞治龀鰜?。在眾多日志分析中,很多字段及功能是重?fù)通用的,所以應(yīng)該將其封裝起來。
// 設(shè)置日志對(duì)象類的通用屬性 function setCommonProperty() { this.happenTime = new Date().getTime(); // 日志發(fā)生時(shí)間 this.webMonitorId = WEB_MONITOR_ID; // 用于區(qū)分應(yīng)用的唯一標(biāo)識(shí)(一個(gè)項(xiàng)目對(duì)應(yīng)一個(gè)) this.simpleUrl = window.location.href.split("?")[0].replace("#", ""); // 頁面的url this.customerKey = utils.getCustomerKey(); // 用于區(qū)分用戶,所對(duì)應(yīng)唯一的標(biāo)識(shí),清理本地?cái)?shù)據(jù)后失效 this.pageKey = utils.getPageKey(); // 用于區(qū)分頁面,所對(duì)應(yīng)唯一的標(biāo)識(shí),每個(gè)新頁面對(duì)應(yīng)一個(gè)值 this.deviceName = DEVICE_INFO.deviceName; this.os = DEVICE_INFO.os + (DEVICE_INFO.osVersion ? " " + DEVICE_INFO.osVersion : ""); this.browserName = DEVICE_INFO.browserName; this.browserVersion = DEVICE_INFO.browserVersion; // TODO 位置信息, 待處理 this.monitorIp = ""; // 用戶的IP地址 this.country = "china"; // 用戶所在國(guó)家 this.province = ""; // 用戶所在省份 this.city = ""; // 用戶所在城市 // 用戶自定義信息, 由開發(fā)者主動(dòng)傳入, 便于對(duì)線上進(jìn)行準(zhǔn)確定位 this.userId = USER_INFO.userId; this.firstUserParam = USER_INFO.firstUserParam; this.secondUserParam = USER_INFO.secondUserParam; } // JS錯(cuò)誤日志,繼承于日志基類MonitorBaseInfo function JavaScriptErrorInfo(uploadType, errorMsg, errorStack) { setCommonProperty.apply(this); this.uploadType = uploadType; this.errorMessage = encodeURIComponent(errorMsg); this.errorStack = errorStack; this.browserInfo = BROWSER_INFO; } JavaScriptErrorInfo.prototype = new MonitorBaseInfo();
封裝了一個(gè)Js錯(cuò)誤對(duì)象JavaScriptErrorInfo,用以保存頁面中產(chǎn)生的Js錯(cuò)誤。其中,setCommonProperty用以設(shè)置所有日志對(duì)象的通用屬性。
1)重寫window.onerror 方法, 大家熟知,監(jiān)控JS錯(cuò)誤必然離不開它,有人對(duì)他進(jìn)行了測(cè)試測(cè)試介紹感覺也是比較用心了
2)重寫console.error方法,為什么要重寫這個(gè)方法,我不能夠給出明確的答案,如果App首次向?yàn)g覽器注入的Js代碼報(bào)錯(cuò)了,window.onerror是無法監(jiān)控到的,所以只能重寫console.error的方式來進(jìn)行捕獲,也許會(huì)有更好的辦法。待window.onerror成功后,此方法便不再需要用了
3)重寫window.onunhandledrejection方法。 當(dāng)你用到Promise的時(shí)候,而你又忘記寫reject的捕獲方法的時(shí)候,系統(tǒng)總是會(huì)拋出一個(gè)叫 Unhandled Promise rejection. 沒有堆棧,沒有其他信息,特別是在寫fetch請(qǐng)求的時(shí)候很容易發(fā)生。 所以我們需要重寫這個(gè)方法,以幫助我們監(jiān)控此類錯(cuò)誤
下邊是啟動(dòng)JS錯(cuò)誤監(jiān)控代碼
/** * 頁面JS錯(cuò)誤監(jiān)控 */ function recordJavaScriptError() { // 重寫console.error, 可以捕獲更全面的報(bào)錯(cuò)信息 var oldError = console.error; console.error = function () { // arguments的長(zhǎng)度為2時(shí),才是error上報(bào)的時(shí)機(jī) // if (arguments.length < 2) return; var errorMsg = arguments[0] && arguments[0].message; var url = WEB_LOCATION; var lineNumber = 0; var columnNumber = 0; var errorObj = arguments[0] && arguments[0].stack; if (!errorObj) errorObj = arguments[0]; // 如果onerror重寫成功,就無需在這里進(jìn)行上報(bào)了 !jsMonitorStarted && siftAndMakeUpMessage(errorMsg, url, lineNumber, columnNumber, errorObj); return oldError.apply(console, arguments); }; // 重寫 onerror 進(jìn)行jsError的監(jiān)聽 window.onerror = function(errorMsg, url, lineNumber, columnNumber, errorObj) { jsMonitorStarted = true; var errorStack = errorObj ? errorObj.stack : null; siftAndMakeUpMessage(errorMsg, url, lineNumber, columnNumber, errorStack); }; function siftAndMakeUpMessage(origin_errorMsg, origin_url, origin_lineNumber, origin_columnNumber, origin_errorObj) { var errorMsg = origin_errorMsg ? origin_errorMsg : ""; var errorObj = origin_errorObj ? origin_errorObj : ""; var errorType = ""; if (errorMsg) { var errorStackStr = JSON.stringify(errorObj) errorType = errorStackStr.split(": ")[0].replace(""", ""); } var javaScriptErrorInfo = new JavaScriptErrorInfo(JS_ERROR, errorType + ": " + errorMsg, errorObj); javaScriptErrorInfo.handleLogInfo(JS_ERROR, javaScriptErrorInfo); }; };
OK, 錯(cuò)誤日志有了,該怎么計(jì)算錯(cuò)誤率呢?
JS錯(cuò)誤發(fā)生率 = JS錯(cuò)誤個(gè)數(shù)(一次訪問頁面中,所有的js錯(cuò)誤都算一次)/PV (PC,IOS,Android平臺(tái)同理)
所以我們需要記下頁面的PV記錄
/** * 添加一個(gè)定時(shí)器,進(jìn)行數(shù)據(jù)的上傳 * 2秒鐘進(jìn)行一次URL是否變化的檢測(cè) * 10秒鐘進(jìn)行一次數(shù)據(jù)的檢查并上傳 */ var timeCount = 0; setInterval(function () { checkUrlChange(); // 循環(huán)5后次進(jìn)行一次上傳 if (timeCount >= 25) { // 如果是本地的localhost, 就忽略,不進(jìn)行上傳 var logInfo = (localStorage[ELE_BEHAVIOR] || "") + (localStorage[JS_ERROR] || "") + (localStorage[HTTP_LOG] || "") + (localStorage[SCREEN_SHOT] || "") + (localStorage[CUSTOMER_PV] || "") + (localStorage[LOAD_PAGE] || "") + (localStorage[RESOURCE_LOAD] || ""); if (logInfo) { localStorage[ELE_BEHAVIOR] = ""; localStorage[JS_ERROR] = ""; localStorage[HTTP_LOG] = ""; localStorage[SCREEN_SHOT] = ""; localStorage[CUSTOMER_PV] = ""; localStorage[LOAD_PAGE] = ""; localStorage[RESOURCE_LOAD] = ""; utils.ajax("POST", HTTP_UPLOAD_LOG_INFO, {logInfo: logInfo}, function (res) {}, function () {}) } timeCount = 0; } timeCount ++; }, 200);
上邊的代碼我用了定時(shí)器,大概的意思是200毫秒進(jìn)行一次URL變化的檢查,5秒進(jìn)行一次數(shù)據(jù)的檢查,如果有數(shù)據(jù)就進(jìn)行上傳,并清空上一次的數(shù)據(jù)。為什么用定時(shí)器呢,因?yàn)樵趩雾搼?yīng)用中,路由的切換和地址欄的變化是無法被監(jiān)控的,我確實(shí)沒有想到特別好的辦法來監(jiān)控,所以用了這種方式,如果有人有更好的辦法,請(qǐng)給我留言,謝謝。
封裝簡(jiǎn)易的Ajax
為了將這些數(shù)據(jù)上傳到我們的服務(wù)器,我們總不能每次都用xmlHttpRequest來發(fā)送ajax請(qǐng)求吧,所以我們需要自己封裝一個(gè)簡(jiǎn)單的Ajax
/** * * @param method 請(qǐng)求類型(大寫) GET/POST * @param url 請(qǐng)求URL * @param param 請(qǐng)求參數(shù) * @param successCallback 成功回調(diào)方法 * @param failCallback 失敗回調(diào)方法 */ this.ajax = function(method, url, param, successCallback, failCallback) { var xmlHttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); xmlHttp.open(method, url, true); xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xmlHttp.onreadystatechange = function () { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { var res = JSON.parse(xmlHttp.responseText); typeof successCallback == "function" && successCallback(res); } else { typeof failCallback == "function" && failCallback(); } }; xmlHttp.send("data=" + JSON.stringify(param)); }二、JS Error 詳細(xì)信息解析
統(tǒng)計(jì)JS Error的目的,一、是為了了解線上項(xiàng)目的健康狀況,二、是為了分析錯(cuò)誤,幫助我們查找問題之所在,并且解決它。
所以,如何定位線上的問題,并解決問題,是我們現(xiàn)在要討論的重點(diǎn)。下面我們需要對(duì)幾個(gè)關(guān)鍵點(diǎn)進(jìn)行分析:
① 某種錯(cuò)誤發(fā)生的次數(shù)——發(fā)生次數(shù)跟影響用戶是成正比的, 如果發(fā)生次數(shù)跟影響用戶數(shù)量都很高,那么這是一個(gè)比較嚴(yán)重的bug, 需要立即解決。 反之, 如果次數(shù)很多,影響用戶數(shù)量很少。說明這種錯(cuò)誤只發(fā)生在少量設(shè)備中,優(yōu)先級(jí)相對(duì)較低,可以擇時(shí)對(duì)該類機(jī)型設(shè)備進(jìn)行兼容處理。當(dāng)然,ip地址訪問次數(shù)也能說明這個(gè)問題
② 頁面發(fā)生了哪些錯(cuò)誤——這個(gè)有利于我們縮小問題的范圍,方便我們排查,如:
③ 錯(cuò)誤堆棧——這點(diǎn)不用說,是定位錯(cuò)誤最重要的因素。正常情況下,代碼都是被壓縮的,所以我在后臺(tái)解析并截取出錯(cuò)代碼附近的一部分代碼,進(jìn)行展示,排查錯(cuò)誤。PS: 我看到網(wǎng)上有人利用jsMap反向找到代碼的具體位置,想法很不錯(cuò),后期我會(huì)加上。 另外,代碼雖然被壓縮,但是依然很輕松定位到出錯(cuò)的位置,如下圖所示, 所以這個(gè)功能暫時(shí)作為附加題,不用那么著急加上。
④ 設(shè)備信息——當(dāng)錯(cuò)誤發(fā)生是,分析出用戶當(dāng)時(shí)使用設(shè)備的瀏覽器信息,系統(tǒng)版本,設(shè)備機(jī)型等等,能夠幫我們快速的定位到需要兼容的設(shè)備,進(jìn)而提升解決問題的效率。
⑤ 用戶足跡——我個(gè)人覺得比較有用,但是代價(jià)太高。 因?yàn)檫@個(gè)需要記錄下用戶在頁面上的所有行為,需要上傳非常多的數(shù)據(jù),功能待定。
這個(gè)功能已經(jīng)在后邊進(jìn)行完善了,點(diǎn)擊 查看足跡 按鈕即可查出這個(gè)用的行為足跡,在定位線上問題方面,有很大的作用 , 我在后邊的篇幅中有介紹 搭建前端監(jiān)控系統(tǒng)(五)怎樣定位線上問題
到此,已經(jīng)收集到了JS錯(cuò)誤日志的大部分信息了,并且已經(jīng)分析出JS錯(cuò)誤的詳細(xì)信息了。
三、JS報(bào)錯(cuò)的實(shí)時(shí)監(jiān)控與報(bào)警既然我們已經(jīng)具有了搜集js報(bào)錯(cuò)和分析報(bào)錯(cuò)的能力了,那么我們也可以做到Js報(bào)錯(cuò)實(shí)時(shí)監(jiān)控,以及實(shí)時(shí)預(yù)警了,這樣可以防范線上事故于未然,及時(shí)的制止線上事故的持續(xù)發(fā)生, 減少損失。
如上圖所示,我展示了從當(dāng)前時(shí)間向前推算24小時(shí),每小時(shí)報(bào)錯(cuò)數(shù)量。另外展示了7天前同一時(shí)間段的報(bào)錯(cuò)數(shù)量,如果你的項(xiàng)目健康穩(wěn)定,那么在相同時(shí)間段的報(bào)錯(cuò)數(shù)量應(yīng)該不會(huì)相差太大。如果出現(xiàn)相差太大的情況發(fā)生,說明線上出現(xiàn)了問題,此刻應(yīng)該發(fā)出警告,避免線上事故的發(fā)生。demo上暫未加上警告功能,但是原理清楚了,后邊自然水到渠成。
關(guān)于FundebugFundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應(yīng)用實(shí)時(shí)BUG監(jiān)控。 自從2016年雙十一正式上線,F(xiàn)undebug累計(jì)處理了10億+錯(cuò)誤事件,付費(fèi)客戶有陽光保險(xiǎn)、核桃編程、荔枝FM、掌門1對(duì)1、微脈、青團(tuán)社等眾多品牌企業(yè)。歡迎大家免費(fèi)試用!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/105340.html
摘要:摘要通過記錄用戶行為,快速復(fù)現(xiàn)場(chǎng)景。這是搭建前端監(jiān)控系統(tǒng)的第二章,主要是介紹如何統(tǒng)計(jì)報(bào)錯(cuò),跟著我一步步做,你也能搭建出一個(gè)屬于自己的前端監(jiān)控系統(tǒng)。 摘要: 通過記錄用戶行為,快速復(fù)現(xiàn)BUG場(chǎng)景。 作者:一步一個(gè)腳印一個(gè)坑 原文:搭建前端監(jiān)控系統(tǒng)(備選)用戶行為統(tǒng)計(jì)和監(jiān)控篇(如何快速定位線上問題) Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。 一步一步搭建前端監(jiān)控系統(tǒng)系列博客: ...
摘要:一直以來,前端的線上問題很難定位,因?yàn)樗l(fā)生于用戶的一系列操作之后。當(dāng)然,這些問題并非不能克服,讓我們來一起看看如何去定位線上的問題吧。地址參考一步一步搭建前端監(jiān)控系統(tǒng)錯(cuò)誤監(jiān)控篇一步一步搭建前端監(jiān)控系統(tǒng)接口請(qǐng)求異常監(jiān)控篇 摘要: 記錄用戶行為,排查線上BUG。 作者:一步一個(gè)腳印一個(gè)坑 原文:如何定位前端線上問題(如何排查前端生產(chǎn)問題) Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所...
摘要:參考一步一步搭建前端監(jiān)控系統(tǒng)錯(cuò)誤監(jiān)控篇用插件記錄網(wǎng)絡(luò)請(qǐng)求異常關(guān)于專注于微信小程序微信小游戲支付寶小程序和線上應(yīng)用實(shí)時(shí)監(jiān)控。 摘要: 如何監(jiān)控HTTP請(qǐng)求錯(cuò)誤? 作者:一步一個(gè)腳印一個(gè)坑 原文:搭建前端監(jiān)控系統(tǒng)(四)接口請(qǐng)求異常監(jiān)控篇 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。 背景:市面上的監(jiān)控系統(tǒng)有很多,大多收費(fèi),對(duì)于小型前端項(xiàng)目來說,必然是痛點(diǎn)。另一點(diǎn)主要原因是,功能雖然...
摘要:目前已經(jīng)在運(yùn)行的線上前端監(jiān)控系統(tǒng)代碼和講解都放在這篇文章里監(jiān)控系統(tǒng)介紹及代碼用戶對(duì)前端程序員來說,就是一個(gè)黑匣子。 摘要: 通過錄屏或者截圖,快速復(fù)現(xiàn)BUG場(chǎng)景。 作者:一步一個(gè)腳印一個(gè)坑 原文:搭建前端監(jiān)控系統(tǒng)(備選)Js截圖上報(bào)篇 Fundebug經(jīng)授權(quán)轉(zhuǎn)載,版權(quán)歸原作者所有。 PS:本文關(guān)于Fundebug錄屏功能的內(nèi)容有些不準(zhǔn)確的地方,比如錄屏并非通過截圖實(shí)現(xiàn)的,錄屏插件...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。對(duì)該漏洞的綜合評(píng)級(jí)為高危。目前,相關(guān)利用方式已經(jīng)在互聯(lián)網(wǎng)上公開,近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡...
閱讀 2943·2021-09-22 15:20
閱讀 3026·2021-09-22 15:19
閱讀 3575·2021-09-22 15:15
閱讀 2486·2021-09-08 09:35
閱讀 2433·2019-08-30 15:44
閱讀 3074·2019-08-30 10:50
閱讀 3864·2019-08-29 16:25
閱讀 1654·2019-08-26 13:55