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