把前端監控做到極致


https://juejin.im/post/5a52f138f265da3e5b32a41b

說到監控,大家第一時間想到的肯定是 Zabbix、Nagios 等各種強大的后端監控服務。誠然,這些強大的平台通過采集服務器以及鏈路上各種中間件的數據,為我們的應用穩定起到了不可或缺的保駕護航作用。

然而在互聯網的另一端,運行在用戶終端上的代碼卻缺少這樣強大的監控能力。

對於資深工程師來說,想到或者做出一個前端監控方案並不是什么難事 —— 通過監聽全局的 window.onerror 事件捕獲到運行時錯誤,然后上報到采集端,再做一個頁面展示數據 —— 看起來確實只需要寫一個簡單的 CRUD 應用就能搞定。

本文將從 采集、數據處理、分析、報警 4 個維度進一步闡述如何把前端監控做到極致。

小福利

如果你還沒有使用前端監控服務,那么可以先看看這個小福利。只用兩行代碼就能打造一個前端異常實時監控平台,還帶報錯數統計功能。

其實現思路正如開題所言,通過 window.onerror 采集到所有的未捕獲異常,並通過 new Image 的方式構造一個 404 的 HTTP 請求,最后在服務端實時過濾 access.log 中匹配的請求並計數即可。

實際運行效果如下:

瀏覽器端效果

服務端效果

當然,這個監控系統並不能直接應用在生產環境。要讓監控真正發揮價值,還需要從采集、處理、分析、報警等多個方面進行優化增強。

采集

Script Error

當我們采集前端報錯的時候,第一個遇到的問題就是 Script Error。Script Error 不是一種具體的錯誤,而是瀏覽器對跨域錯誤出於安全機制考慮的一種處理方式。

一個前端錯誤為什么涉及到了「安全」問題呢?2006 年一位安全研究人員發現第三方腳本可以通過頁面中報錯信息的不同判斷當前用戶是否登錄了指定的網站,並向 Webkit 項目提出了 相關的 issue。7 年之后,各大瀏覽器廠商基本都支持了這一安全設定。

Webkit 源碼中對 Script Error 的處理

簡單的說,如果你的頁面和頁面中引用的 JavaScript 文件不同源(協議、域名、端口不一致),那么這些腳本拋出的錯誤都屬於跨域錯誤。那么我們在做前端監控捕獲這些錯誤的時候,應該怎么避免采集到 Script Error 呢?

答案是 crossorigin 屬性。這是一個應用在 <script> 標簽上的屬性,添加之后即可保證即使是跨域錯誤也能捕獲到完整的錯誤信息。然而事情真的只有這么簡單嗎?

crossorigin 生效需要服務器端和瀏覽器端同時支持。服務器端支持比較簡單,即返回跨域腳本的服務器(一般為 CDN 服務器)正確的帶上 CORS 響應頭 —— Access-Control-Allow-Origin: * —— 即可,目前常見的 CDN 服務均支持這一特性。而瀏覽器端的支持情況就沒有這么樂觀了。

crossorigin 屬性前端支持情況

可以看到,crossorigin 前端支持問題的重災區發生在 IE 和 Safari 上。IE 這個拖油瓶出現問題是情理之中,Safari 在 9.0 之前的版本也不支持 crossorigin 就說不過去了。這也直接導致了許多運行在 iOS Webview 中的業務無法正確捕獲到錯誤。

突破跨域報錯限制

那么怎樣能突破 crossorigin 的這些限制,盡可能的捕獲到更詳細的錯誤呢?

首先最簡單也是最直白的方式,就是把頁面中所有的跨域資源放在跟頁面同樣的域下,這樣腳本拋出的錯誤不再是跨域錯誤,也就不存在 crossorigin 的使用場景了。當然同域化之后也會遇到很多問題,比如無法利用 CDN 的性能、頁面單域資源並發加載限制等等。

另一種解決方案是通過 Patch 原生方法來盡可能的捕獲到錯誤,這也是很多監控腳本默認提供的能力。比如說我們可以通過如下代碼來 Patch 原生的 setTimeout 方法:

const prevSetTimeout = window.setTimeout;

window.setTimeout = function(callback, timeout) { const self = this; return prevSetTimeout(function() { try { callback.call(this); } catch (e) { // 捕獲到詳細的錯誤,在這里處理日志上報等了邏輯 // ... throw e; } }, timeout); } 

同理,我們還可以 Patch 更多的原生方法,比如 Array.prototype.forEach、setInterval、requestAnimationFrame等等。

誠然這種方法能幫我們盡可能捕獲到更多異常,但是因為 Patch 了 JavaScript 原生的方法,總是感覺會存在很多的不確定性。

在這里還要提一下去年 QCon 上百姓網前端同學劉小傑提出的一種基於 Babel 的自動添加 try...catch... 的方法,感興趣的同學可以去深入看看,會有不少啟發。

框架層解決方案

在不少現代前端框架中,都提供了框架層的異常處理方案,比如 AngularJS 的 ErrorHandler 和 Vue 的 Vue.config.errorHandler。在這里我們以 React 16 的 componentDidCatch 為例,說明如何使用框架的能力采集錯誤。

以下是 React 官網中的示例:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false }; } componentDidCatch(error, info) { this.setState({ hasError: true }); // 在這里可以做異常的上報 logErrorToMyService(error, info); } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } } 
在使用時,用 ErrorBoundary 包裹你的業務組件即可:
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

數據處理

傳統的監控服務一般都會使用 MySQL 等數據庫進行數據持久化,但當數據量指數級增長時,MySQL 這種 OLTP 數據庫已經不再適合用來提供監控數據分析服務。

在大數據時代,搭建一套標准化的、針對監控業務的大數據解決方案已經不是什么難事,下圖即為一個簡單的數據架構示意圖:

在數據處理過程中,值得一提的是數據采樣率的功能設計。

不難看出目前的采樣率設計方案都或多或少存在缺陷和妥協,那么有沒有一種更優的解決方案呢?

經過大量的實踐后,我們認為在日志服務進入數據處理流程之前進行采樣率控制是比較理想的方案,理由如下:

  1. 日志寫入成本低
  2. rotate 機制保證存儲不會浪費
  3. 了解真實打點請求數據量
  4. 避免采集端繞過采樣率限制

分析

當故障發生時

解決了數據采集和處理的問題,我們應該怎么着手進行分析呢?讓我們先看一個真實案例:

當你吃着火鍋唱着歌的時候,突然看到實時監控數據暴漲,這個時候你的第一反應是什么呢?是不是手足無措不知道應該怎么處理?當線上出現緊急狀況時,我們的首要思路是找到問題觸發的特征,比如是否集在某個頁面或者某種瀏覽器等等。

通過監控平台提供的分析功能,初步定為到問題原因后,再進行深入的調查。

報錯數高一定是不穩定嗎

這里試舉兩個反例來說明報錯數高不一定就是前端不穩定。

如上圖所示,雖然該應用 1 天爆出了上萬的 JavaScript 異常,但是我們在分析過程中發現,95% 的報錯都集中在 3 個 userId 上。再對這 3 個 userId 進行深入的調查不難發現,這是 3 個爬取數據的爬蟲賬號,不巧爬數據的腳本寫的有 Bug,被前端監控系統忠實的捕捉到了。

又如上圖所示,某天的數據出現暴增,可能是因為頁面的訪問量出現暴增。

因此我們不難發現,僅僅通過報錯數的多少不足以判斷系統是否穩定。
 
異常波動一定有元凶

前端發生故障最常見的原因就是新發布的版本存在 Bug,那么這種問題在監控平台中如何提供分析思路呢?

當然,也並不是所有的波動都是前端變更引起。比如說后端接口突然故障,也會導致前端因為無法讀取到某個接口結果而報錯。

報警

說到報警,絕大多數的監控平台都提供規則報警的能力。然而規則報警最大的問題在於隨着業務的不斷發展,原本配置的規則將會出現閾值過低或過高的問題。若閾值配置過低,則會產生大量的誤報警,繼而引起整個監控能力的報警疲勞。

為了解決規則報警的問題,監控平台可以引入一些簡單的數學模型來解決時序數據的異常識別工作。以最常見的高斯分布(正態分布)為例,利用 3-sigma 原則可以快速判斷某一時刻的報錯數是否滿足概率分布,繼而可以產生報警:
 
當然,這樣的報警模型還存在非常大的優化空間,比如對數據周期性、季節性的處理,又比如過濾掉某些可能影響平均數的極高值等。

結語

前端監控看似簡單,但想要監控真正發揮價值,還需要從各個方面進行不斷的優化和打磨。當然,最重要的是,要意識到前端監控的必要性,及早開始進行監控,才能更好的避免線上故障的產生。


作者:前端新能源
鏈接:https://juejin.im/post/5a52f138f265da3e5b32a41b
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM