搭建前端監控系統(二)JS錯誤監控篇

怎樣定位前端線上問題,一直以來,都是很頭疼的問題,因為它發生於用戶的一系列操作之後。錯誤的原因可能源於機型,網絡環境,接口請求,複雜的操作行為等等,在我們想要去解決的時候很難復現出來,自然也就無法解決。 當然,這些問題並非不能克服,讓我們來一起看看如何去監控並定位線上的問題吧。

事到如今,你還是一個手無寸鐵的前端小白嗎?你想擁有一套屬於自己的監控系統嗎?跟著我一步步做,你也能搭建出一個屬於自己的前端監控系統。

這是我的開源項目,可以先看一下首頁的結果(也可直接訪問網站 www.webfunny.cn 看效果):

首頁概覽圖

一、JS Error 監控功能 (數據概覽)

  如果每天都去盯著前端的報錯數據,真的很耗費精力,而且很難看出是今天發生的,還是一直存在的報錯。其實前端項目每天都會有些報錯,比如:script error 。我們既不能控制,也不會影響我們的業務,只能讓它一直存在。所以我們的前端應用,每天都會有一定數量的報錯數據。只要日活量不會波動太大,那麼報錯數據就會比較平穩,所以我選擇跟7天前的報錯數據進行比較,如果出現大幅上升,那麼就需要我對這個項目進行關注了,而不是每天查看具體的報錯數據。

前端項目報錯統計圖

對於前端應用來說,Js錯誤的發生直接影響前端應用的質量。對前端異常的監控是整個前端監控系統中的一個重要環節。前端異常包含很多種情況:1. js編譯時異常(開發階段就能排);2. js運行時異常;3. 加載靜態資源異常(路徑寫錯、資源服務器異常、CDN異常、跨域);4. 接口請求異常等。這一篇我們只介紹Js運行時異常。

監控流程:監控錯誤 -> 蒐集錯誤 -> 存儲錯誤 -> 分析錯誤 -> 錯誤報警-> 定位錯誤 -> 解決錯誤

首先,我們應該對Js報錯情況有個大致的瞭解,這樣才能夠及時的瞭解前端項目的健康狀況。所以我們需要分析出一些必要的數據,如下圖所示:

前端項目報錯的分析數據

那麼我們該如何去監控這些數據呢?其實主要用到下邊三種方法:

1)重寫window.onerror 方法, 大家熟知,監控JS錯誤必然離不開它,有人對他進行了測試測試介紹感覺也是比較用心了

  2)重寫console.error方法,為什麼要重寫這個方法,我不能夠給出明確的答案,如果App首次向瀏覽器注入的Js代碼報錯了,window.onerror是無法監控到的,所以只能重寫console.error的方式來進行捕獲,也許會有更好的辦法。待window.onerror成功後,此方法便不再需要用了

  3)重寫window.onunhandledrejection方法。 當你用到Promise的時候,而你又忘記寫reject的捕獲方法的時候,系統總是會拋出一個叫 Unhandled Promise rejection. 沒有堆棧,沒有其他信息,特別是在寫fetch請求的時候很容易發生。 所以我們需要重寫這個方法,以幫助我們監控此類錯誤

  下邊是就是做JS錯誤監控代碼,大家可以參考一下:

// 重寫console.error, 可以捕獲更全面的報錯信息
var oldError = console.error;
console.error = function (tempErrorMsg) {
var errorMsg = (arguments[0] && arguments[0].message) || tempErrorMsg;
var lineNumber = 0;
var columnNumber = 0;


var errorObj = arguments[0] && arguments[0].stack;
if (!errorObj) {
siftAndMakeUpMessage("console_error", errorMsg, WEB_LOCATION, lineNumber, columnNumber, "CustomizeError: " + errorMsg);
} else {
siftAndMakeUpMessage("console_error", errorMsg, WEB_LOCATION, lineNumber, columnNumber, errorObj);
}
return oldError.apply(console, arguments);
};
// 重寫 onerror 進行jsError的監聽
window.onerror = function(errorMsg, url, lineNumber, columnNumber, errorObj) {
jsMonitorStarted = true;
var errorStack = errorObj ? errorObj.stack : null;
siftAndMakeUpMessage("on_error", errorMsg, url, lineNumber, columnNumber, errorStack);
};
// 重寫 onunhandledrejection 對未處理的rejection錯誤進行捕獲處理
window.onunhandledrejection = function(e) {
var errorMsg = "";
var errorStack = "";
if (typeof e.reason === "object") {
errorMsg = e.reason.message;
errorStack = e.reason.stack;
} else {
errorMsg = e.reason;
errorStack = "";
}
siftAndMakeUpMessage("on_error", errorMsg, WEB_LOCATION, 0, 0, "UncaughtInPromiseError: " + errorStack);
}

封裝簡易的Ajax

  為了將這些數據上傳到我們的服務器,我們總不能每次都用xmlHttpRequest來發送ajax請求吧,所以我們需要自己封裝一個簡單的Ajax。

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 詳細信息解析

統計JS Error的目的,一、是為了瞭解線上項目的健康狀況,二、是為了分析錯誤,幫助我們查找問題之所在,並且解決它。所以,如何定位線上的問題,並解決問題,是我們現在要討論的重點。下面我們需要對幾個關鍵點進行分析,先看一下總體的分析圖:

JS錯誤分析結果圖

① 某種錯誤發生的次數——發生次數跟影響用戶是成正比的, 如果發生次數跟影響用戶數量都很高,那麼這是一個比較嚴重的bug, 需要立即解決。 反之, 如果次數很多,影響用戶數量很少。說明這種錯誤只發生在少量設備中,優先級相對較低,可以擇時對該類機型設備進行兼容處理。當然,ip地址訪問次數也能說明這個問題。

  ② 頁面發生了哪些錯誤——這個有利於我們縮小問題的範圍,方便我們排查。

③ 錯誤堆棧——這點不用說,是定位錯誤最重要的因素。正常情況下,代碼都是被壓縮的,所以我在後臺解析並截取出錯代碼附近的一部分代碼,進行展示,排查錯誤。PS: 可以根據sourceMap解析壓縮混淆代碼的具體位置和代碼片段,想法很不錯,後期我會加上,目前已經完成測試版本。 另外,代碼雖然被壓縮,但是依然很輕鬆定位到出錯的位置,如下圖所示。

sourceMap解析源碼

④ 設備信息——當錯誤發生是,分析出用戶當時使用設備的瀏覽器信息,系統版本,設備機型等等,能夠幫我們快速的定位到需要兼容的設備,進而提升解決問題的效率。

⑤ 用戶足跡——通過記錄用戶的具體步驟,可以清晰瞭解到用戶出錯前後時間內發生的事情,對解決問題也有很大的幫助。如下圖:

用戶行為記錄

三、JS報錯的實時監控與報警

最後這個功能應該很容易理解,及時的發佈警報,能夠讓你更快知道前端的問題。

至此,我們的JS錯誤統計功能就完成了。