天天看點

釘釘前端-如何設計前端實時分析及報警系統

關于釘釘前端

釘釘從 2014 年底創業至今,發展極其迅速,釘釘前端監控也在相應的演進。我們有億級的使用者和千萬級的企業使用者,前端産品有安卓、iOS、桌面端、小程式、 H5等,前端應用的釋出也涵蓋全量釋出、灰階釋出的情況。

億級流量的挑戰

對于這樣一個億級平台,除了做前端監控系統之外,我相信很多小夥伴也有體感,要保證整體釘釘前端的穩定性,還需要有一些技術營運的手段,包括人的一些情況。我們現在整體有 100 多個前端開發成員,然後我們的技術子產品上面有 IM、通訊錄、直播、教育、文檔、硬體等等非常具有 B 端屬性的業務。

釘釘前端-如何設計前端實時分析及報警系統

成果

釘釘前端-如何設計前端實時分析及報警系統

我先說一下我們的成果: 100% 覆寫了我們今天所有的 h5 和小程式、支撐了 100 多個前端人員的監控需求。前端監控的日志量達到百億,監控大盤個數超過 100 個,能做到線上問題一分鐘感覺和一分鐘模糊問題定位。在人力投入上面,始終維持在兩個負責人員以内,大部分情況下是我一個人為主在負責整體的一個監控情況,是以在人力投入上面,我們的成本是相對比較低的。

上圖中的兩張趨勢圖是我們監控的主要産品結構,一張是我們的監控趨勢圖,另一張是我們的業務大盤檔案夾用于承載各個業務,同時,我們有一個生産環境的統一小程式、H5 監控大盤。

演進之路

接下來我會講一下,關于釘釘前端監控,我們是如何對系統進行演進,拿到一個不錯的結果。

釘釘前端-如何設計前端實時分析及報警系統

考慮到有很多小夥伴們不是搞前端監控的,是以這邊我會先講一些基礎知識來展開如何設計一個前端監控系統。

我們來看一下上面這段代碼,const 建立一個對象,然後 foot.a.b = c。可以看到這是段非常經典的 NPE 代碼,就是 null point exception,在前端代碼中非常容易出現。這邊會抛出一個錯誤:** Uncaught TypeError: Cannot set property 'b' of undefined**。

對于這樣一個錯誤,在使用者側發生之後,我們的前端監控系統是怎樣去捕獲這個錯誤,并且在一分鐘之内發現?我們來看一下一傳統的做法是怎麼做的:

  • 首先寫一個前端監控 SDK,用于進行資料采集
  • 選型一個通知方案,将這條前端日志通知到服務端

我這邊示範的是用 image 标簽,建立一個 image 标簽,設定它的 src 指向對應的日志伺服器來發送對應的日志。

我們對錯誤的采集采用的是 window.onerror 來捕獲全局錯誤。然後将捕獲的錯誤通過建立 image 标簽形式發送到上圖右側的前端監控服務端。

如上的代碼隻是一個僞代碼示範,我寫的比較簡單。

對于一個傳統的基于日志分析的監控系統,你首先要知道這條日志到底是來自哪裡,是以我們對每一條日志在前端采集的時候,都有一個應用 id,姑且我這邊稱之為 spmId ,通過 spmId 來辨別日志源,然後将這條日志存儲到對應的監控服務端,這樣就完成了一個非常簡單的從前到後的一個鍊路。

從日志發生、采集,然後再到存儲的一個閉環,非常簡單。其實見微知著,看到這麼一個簡單的實作,再把日志類型進行豐富,采集和存儲做的強大一點,基本上就可以去搭建一個比較簡單的前端監控系統了。

釘釘前端-如何設計前端實時分析及報警系統

一般而言,一個簡單的前端監控分析系統需要包含如下三個次元:

  • 第 1 個是穩定性相關的 js error
  • 第 2 個是性能相關的 performance
  • 第 3 個是 api 成功率相關的

在監控平台,我們需要做一些日志存儲,将監控日志提供給可視化平台伺服器,通過提供一些 API 服務就可以畫出上面這樣一個圖。比如第 1 個是接口成功率。

我覺得在技術選型上面,對于很多稍微有點 Node 或者服務端基礎的前端同學來說,基本上能做出一個簡單的 Demo。然而,這樣一個看似功能很完備的系統,對于做前端監控來說,有沒有什麼問題?是不是能夠滿足釘釘這樣一個億級流量平台的監控需求?

釘釘前端-如何設計前端實時分析及報警系統

上圖左邊展示的是我們的開發人員接入前端監控的過程,包括開發階段、測試階段、上線階段。 在前端監控推行的過程中,我們要求所有的開發人員在應用疊代上線後,要主動觀察監控大盤至少 30 分鐘,觀察三個名額:

  1. js error
  2. performance
  3. api 成功率

對于目前我們 100 多個前端同學的團隊規模來說,人力成本是 100 乘以 30 分鐘,同時對于釘釘這個企業級産品而言,我們對線上的穩定性要求是非常高的,對線上故障容忍度極低,是以還要求每日對線上應用進行巡檢,是以人力成本非常高。

從開發人員的體驗角度看,一個開發人員檢視監控的時候:第 1 個他會去可視化分析平台上去看有沒有錯誤日志。這邊有一個非常重要的點,就是說我們監控分析平台看到的日志,是不是"前端頁面"的日志?

不一定是。為什麼?因為對于使用者來說,它不僅僅是打開了前端頁面,這個前端頁面背後還有容器的 webview、應用容器、營運商等。

舉個例子,我們一個頁面可以在微信的容器裡面打開、可以在頭條的容器裡面打開、可以在釘釘容器裡面打開。是以你采集的日志源不僅僅是一個前端頁面,還有容器的 webview,同時我們還會面臨很多的營運商。比如說我們經常看到前端頁面裡插了一段廣告,然後我們還有一些手機的制造商,比如vivo、華為等,也會在我們的頁面裡面插入相關的腳本。是以監控分析平台采集到的日志不僅僅是前端日志,他采集到的範圍實際上是前端頁面對應的使用者終端日志。

一般我們會碰到如下三種幹擾日志:

  • 第 1 個是第三方腳本注入
  • 第 2 個是容器腳本的注入
  • 第 3 個是由手機制造商腳本注入
釘釘前端-如何設計前端實時分析及報警系統

舉個例子,如上是我們線上的一個應用,大概 js error 率是 0.08%, 對于釘釘這樣的體量來說,這個錯誤率影響使用者的數量已經非常大了。

我們來看一下它對應的錯誤實際上是什麼?Script error,WeixinJSBridge is not defined, toutiaoJSBridge is not defined, 20 vivoNewsDetailPage,這些東西從錯誤資訊基本上可以判斷跟業務錯誤基本沒啥關系。

是以我們可以得到第 1 個結論,就是前端監控産生的一部分錯誤實際上跟業務無關,這個可能跟很多人的認知是相悖的。

釘釘前端-如何設計前端實時分析及報警系統

我們再來看一個問題,左圖是我們桌面端的釋出曲線,釘釘是國内甚至是全球為數不多的非常重桌面端的平台。釘釘桌面端基本上是一個禮拜或者兩個禮拜一個疊代,由于桌面端的前端代碼是采用離線包的形式,是以代碼的更新修複是比較困難的,對前端穩定性的要求非常的高。

對于我們今天的桌面端而言,已經有 100 多個線上釋出版本了,這麼多的版本上報的日志采用的是同一個應用id,我們如何去做分層監控,線上流量的不均如何做好分層監控,避免小流量的發版監控被淹沒?

這些問題在釘釘的業務場景是經常碰到的,我們的監控顆粒度需要和前端的發版相适應,并且監控的日志需要支援更多的次元。比如說以應用和釋出版本,這兩個變量為機關進行監控。

釘釘前端-如何設計前端實時分析及報警系統

我們再看一個案例,釘釘有幾百個前端應用,每個應用報警 1 次就非常誇張了,基本上一天報警群就有 500 多條日志,刷屏現象非常嚴重,而且很多錯誤是線上的長尾錯誤。也就是它雖然有報警,但是不需要去修改等等。長尾錯誤出現的原因是我雖然修複了問題,但是使用者那邊不一定完全通路的是最新的版本。

是以結論 3 就是我們監控營運的人力成本非常高,對于前端監控的要求不僅僅是要報警報出來,還需要你的報警是直覺的、實時的,同時要支援一些短時關閉和錯誤過濾等等手段。

看完上面這三個案例後,我們來看一下究竟該如何設計一個能夠服務 3 億體量的監控系統。

釘釘前端-如何設計前端實時分析及報警系統

首先,我們先界定監控設計的目标,釘釘企業級前端監控需要做到的事情是: 一分鐘感覺、5 分鐘定位、10 分鐘恢複。姑且稱這個監控系統為 2.0 系統。

釘釘前端-如何設計前端實時分析及報警系統

我們對于前端監控 2.0 在 1.0 的基礎上定義了如下的能力水位。

第 1 個是要貼近實際業務,降低人力營運成本、業務方能夠低成本介入。同時對于報警體系,要求做到快報警、準報警,并且支援自定義報警。我們内部定了一個基準線,就是前端監控精度必須達到 90% 以上,人力成本必須減少 20 分鐘每一個人,并且報警和大盤需要能夠支援自定義配置。

釘釘前端-如何設計前端實時分析及報警系統

上圖是整體的監控的元件編排方案。左邊是一個圖例,藍色部分代表的是 1.0 的監控元件,墨綠色的部分代表 2.0新增的監控元件。

自定義采集

第 1 個在日志采集端,除了采集正常的業務資料和監控資料之外,支援自定義采集。

分析智能化

分析智能化這一塊增加了分析可自定義的能力。

報警實時化

在報警實時化這一塊,增加了線上1分鐘報警和5分鐘定位的要求。

最關鍵的技術實作

釘釘前端-如何設計前端實時分析及報警系統

同樣,藍色部分是原有的 1.0 的一個體系,墨綠色部分是我們新增的體系。我們會發現在日志采集在和日志消費端,我們增加了一個子產品叫做日志雙寫。

一份日志被兩個系統所消費,一份系統用于實時去報警,一份系統用于去做分析:

  • 伺服器拿到日志後,一塊去做存儲分析以便做一些監控報表服務;
  • 第二塊引入了日志分鐘計算系統去做實時的報警。

很多同學會覺得日志雙寫其實是一個非常大的系統的浪費,一份日志被兩個系統所消費了。實際上釘釘前端監控借助了阿裡非常成熟的日志消費系統和基礎設施。通過日志分發兩路被快速消費,讓分鐘計算系統在整個監控體系裡面的編排是前置的,達到 1 分鐘報警的要求,這是我們在這一塊裡面最核心的一個技術思路。

在上圖的紫色虛線下方,是我們的使用者視角。使用者側觸碰到的是兩塊,第 1 塊是前端監控 SDK,我們有 H5 和小程式的 SDK,第二塊是平台,包括分析平台和報警平台。

真實案例

釘釘前端-如何設計前端實時分析及報警系統

我們來看一個真實的案例。使用者碰到了兩個 js error 。這兩個 js error 都是前端經典的 NPE 錯誤。

第 1 個是發生在 iPad + 百度浏覽器。第 2 個是發生在安卓 + 頭條 webview ,結果我們會發現,我們用戶端上報過來的錯誤有兩種:

  1. 真實錯誤: Uncaught TypeError: Cannot set property 'b' of undefined。
  2. 宿主注入的很多幹擾資訊,比如說百度浏覽器會注入 MyAppHrefLink is not defined。

可能很多同學沒有觀察過。我們是仔細去排查過的。百度浏覽器會注入 MyAppHrefLink is not defined。頭條的也會去注入一些頭條 jsBridge 。

日志到達服務端後,我們先對日志進行清洗,把所有宿主的幹擾日志都過濾掉,確定我們的報警系統是消費的真正的業務發生的日志錯誤。這是黃色區域的第一個子產品: 日志清洗

釘釘前端-如何設計前端實時分析及報警系統

接下來我們進行日志分組,将應用 A spmId=A 和應用 B 的日志進行分組,通過應用辨別 A 和 B 進行分組。将過濾過來的日志進行實時計算。

經過這一步後,再将日志流轉到報警名額項進行實時計算,這個報警規則引擎下發相關的指令到 Map Reduce 對應的機器上去做一些處理。

比如 JS Error 失敗率= JS Error 日志條數除以 PV 條數。當對日志進行計算的結果大于 6%,則進行釘釘群報警 ,當失敗率大于 15% 則進行短信報警。

釘釘前端監控 2.0

監控日志

釘釘前端-如何設計前端實時分析及報警系統

通過将同樣的流程應用到各個不同的名額項,比如 api 成功率、js error 失敗率、pv 資料等我們就可以在分鐘計算系統搭建出一套滿足 1 分鐘感覺的監控系統。

報警系統架構

釘釘前端-如何設計前端實時分析及報警系統

關于報警系統,上圖我們阿裡研發事業部那邊的一個非常經典的監控系統,有興趣的同學可以在 infoQ 上搜尋 sunfire 看到更詳細的架構介紹,這裡不做過多展開。

整體日志架構總結

釘釘前端-如何設計前端實時分析及報警系統

基本上這就是我今天想要分享的,釘釘前端監控在從 1.0 演進到 2.0 的過程中,我們是如何思考和如何落地的。這邊的話我給大家稍微簡單總結一下:

  1. 最關鍵的技術思路是将日志報警元件的編排進行前置,我們的實作是采用日志雙寫到分析系統和報警系統。
  2. 在報警平台支援報警規則引擎,真正做到報警自定義、報警可分級等。
  3. 對于前端而言,我們不僅僅是前端頁面,我們更多的面對的是使用者終端。

繼續閱讀