怎樣定位前端的問題,一直以來,都是很頭疼的問題,因爲它發生於用戶的一系列操作之後。錯誤的原因可能源於機型,網絡環境,接口請求,複雜的操作行爲等等,在我們想要去解決的時候很難復現出來,自然也就無法解決。 當然,這些問題並非不能克服,讓我們來一起看看如何去監控並定位線上的問題吧。
我們把前端的問題分爲兩大類,JS代碼錯誤、和前端異常表現。代碼錯誤很容易理解,只要你的代碼報錯了,就會有錯誤日誌產生,你就可以根據錯誤日誌定位到問題所在。但是前端異常就比較麻煩一些,導致異常的原因可能是外界因素,我前面提到的,機型,網絡環境,接口請求,複雜的操作行爲等。
JS錯誤監控
圖一:js錯誤錯誤的監控和分類
首先,我們應該監控JS錯誤每日的數量。因爲錯誤每天都會發生,無法做到100%的避免,正常情況下報錯量會穩定在一個範圍裏,所以我們只需要從趨勢中看出比較突出的一天,集中精力去解決即可。
然後,我們根據錯誤類型對所有的JS錯誤進行分類聚合,這樣頁面上發生的錯誤就可以一目瞭然。根據監控的結果我們可以看到,JS代碼錯誤主要包含以下幾個類型,如下:
TypeError(類型錯誤)、
SyntaxError(語法錯誤)、
ReferenceError(引用錯誤)、
RangeError(範圍錯誤)、
Error、
Script error(第三方文件報錯)、
...
此類錯誤的的前幾個都屬於運行時,並且阻斷型的錯誤,它們會阻斷程序的運行,是需要優先解決的。後邊幾個屬於非阻斷型的錯誤,或者是屬於第三方文件,你無法處理的錯誤類型,可以考慮優化處理。此類錯誤的監控方法是:重寫window.onerror 方法, 研究過JS錯誤的人對此方法應該都有所瞭解,這個是最經常用到的JS錯誤監控方法。
window.onerror = function(errorMsg, url, lineNumber, columnNumber, errorObj) { //此處書寫錯誤監控的邏輯 }
再者,還有一種報錯方式,是通過console.error打印出來。這種錯誤不會阻斷程序運行,通常是由第三方插件通過警告的方式,打印出來提醒你的。在我的監控系統中,稱之爲「自定義錯誤」。在我們的程序中也可以用這種方式打印出警告信息,比如,接口返回異常信息、接口請求超時、網絡請求失敗等等,都可以用來幫我們判斷前端應用是否監控
圖二:自定義錯誤分類
此類錯誤從嚴格意義上來講,不能夠算是報錯,只是一種比較嚴重的警告方式。此類錯誤的監控方法是:通過重寫console.error方法獲取,這個方法,可能大多數小夥伴都會忽略。很多第三方的插件和庫都是用這種方式把異常打印出來,所以我們需要重寫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) { if (typeof errorMsg == "object") { try { errorMsg = JSON.stringify(errorMsg) } catch(e) { errorMsg = "錯誤無法解析" } } siftAndMakeUpMessage("console_error", errorMsg, WEB_LOCATION, lineNumber, columnNumber, "CustomizeError: " + errorMsg); } else { // 如果報錯中包含錯誤堆棧,可以認爲是JS報錯,而非自定義報錯 siftAndMakeUpMessage("on_error", errorMsg, WEB_LOCATION, lineNumber, columnNumber, errorObj); } return oldError.apply(console, arguments); };
最後,有一類錯誤叫:UncaughtinPromiseError;此錯誤也是非阻斷型的,當你用到Promise的時候,而你又忘記寫reject的捕獲方法的時候,系統總是會拋出一個叫 Unhandled Promise rejection. 沒有堆棧,沒有其他信息,特別是在寫fetch請求的時候很容易發生。此類錯誤的監控方法:通過重寫window.onunhandledrejection方法獲取。
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); }
JS錯誤分析
通過上文中介紹的三種方式,基本上可以將前端的大部分JS報錯進行監控。下邊來談談如何分析並解決這些報錯問題。
一、判斷錯誤的影響範圍,發生的設備有哪些、影響人數是多少、最近發生的時間等。
圖三(影響範圍):
二、對具體的錯誤細節進行分析,從而定位到問題的真正原因,系統,機型,設備,代碼位置。
圖四(錯誤的具體細節):
三、針對單個錯誤,可以查看它的報錯趨勢,精確到每分鐘
圖五(單個錯誤報錯趨勢):
四、針對單個錯誤,可以通過查看它的行爲軌跡,來確定js錯誤發生於用戶操作的哪一步之後
圖六(查看行爲軌跡):
具體錯誤具體分析
1. Script error
“Script error.”是一個常見錯誤,但由於該錯誤不提供完整的報錯信息(錯誤堆棧),問題排查往往無從下手,大部分都是發生在跨域情況下。參考解決方案
2. TypeError(類型錯誤)、SyntaxError(語法錯誤)、ReferenceError(引用錯誤)、RangeError(範圍錯誤)
這一類的錯誤必然都是有堆棧信息的,可以根據代碼解析定位到源碼的位置。
3. UncaughtinPromiseError(Unhandled Promise rejection 未處理的promise rejection)
這一類錯誤同樣的可以根據源碼定位,找到對應的promise, 然後去處理reject邏輯。