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