你知道本身的代碼在線上有多少問題嗎

本文做者@李逸君,京東前端開發工程師。html

前言

badjs,即前端異常的一個洋氣的統稱。指代那些「找不到對象」、「未定義」、「語法問題」等在前端拋出來的異常錯誤。前端

筆者負責的是京喜的前端某業務,長期受到大量異常的困擾,又經常找不到緣由。有時異常一下暴漲,又降了回去,定位不到問題,深受其擾。通過長時間的沉澱,分析總結出了一套結論和方法。node

文章篇幅較長,前半部分講述了爲何要作這件事以及怎麼收集和分析 badjs,適合於沒有系統接觸過 badjs 的同窗瞭解。ios

後半部分對 Script error、Hybrid、大數據系統進行了較爲深刻的分析,供你們參考和討論。有必定經驗的同窗能夠直接跳到這一部分開始閱讀。c++

但願對於屏幕面前的你能有所啓發。git

ps:此係列方法不適用於node.js程序員

京喜的 badjs

先來看下這張圖片:github

badjs

這是我所負責的一個線上業務的 badjs 走勢。不知道你看到這根刺是什麼感受,反正我看到是會很是緊張,不論手上有什麼事都得立馬撲向電腦檢查問題,分析日誌,跟老闆彙報原由...web

爲何要這樣一個系統

俗話說,技術服務於業務。咱們的 badjs 日誌系統誕生於必然。chrome

以微信小程序商品詳情業務爲例,日pv有千萬。

假如前端出了問題,有啥東西點不動,致使訪問的用戶變少了。最後的結果是單量少了,用戶丟了,還影響了整個部門同窗的飯碗。這個鍋,背不起。

面臨這些問題,試問一下:若是是你維護的頁面,你怕不怕?若是是你即將發佈的頁面,你手抖不抖?

爲了程序員的幸福生活,提早發現問題,把黑鍋扼殺在搖籃中,這樣一個系統是必須的選擇。

badjs原理和收集

咱們沒法預測哪一段代碼會出問題,成本最小的方案是在一個集中的地方統一處理,而後收集起來。

badjs 的由來

badjs 實質是 js 引擎執行了沒法識別的邏輯,出現了異常。

好比在 undefined 上進行操做、用錯了數據類型、解析異常、語法錯誤等:

花式錯誤

badjs的收集

當 JavaScript 運行發生異常時,而且未被捕獲,window 下會觸發一個 ErrorEvent 接口的 error 事件,並執行 window.onerror()

咱們能夠經過如下兩種方式在全局處理異常:

window.addEventListener('error', function(errorEvent) {
    const { message, filename, lineno, colno, error } = errorEvent
    ...
})
複製代碼
window.onerror = function(message, source, lineno, colno, error) { ... }
複製代碼

這兩種方式在寫法上前者更優一點,由於 error 事件能夠監聽多個,window.onerror 只能訂閱一個。

惋惜 ie8 之前不支持 ErrorEvent,只能使用 window.onerror。 因此看業務須要合理使用。

另外還有 try...catch 的方式,也能夠收集到特定場景下的全局錯誤信息,但這個方式有不少缺陷,關於它咱們後面再談。

經過 window 下的 error 事件,咱們正常狀況下能收集到五類信息:

屬性 含義 說明
message 錯誤信息 錯誤描述
filename(source) 發生錯誤的腳本URL ErrorEvent 中是 filename,在 onError 中是 source
lineno 錯誤行
colno 發生錯誤的腳本 URL
error Error 對象 error.stack 是很重要的信息

在 console 內打出來是這樣的:

error信息

有了這些,就已經給了咱們至關充分的信息來定位問題了。

這裏報錯的信息有一點挺有意思。message 裏會帶上 'Uncaught' 表示未捕獲。而 error.stack 內沒有這個前綴詞:

Uncaught

跨域場景

以上說到的是理想場景,真實環境可能會涉及跨域,這個背景下每每只能收集到一些不太有意義的信息。

跨域error信息

這個問題文章後半部分會深刻的談談。

tips

window 下的 error 事件也並非什麼異常都能捕獲的。

好比咱們直接在 console 裏觸發的異常、瀏覽器攔截的數據、資源 404 等都不會觸發。

小程序badjs

上面是用 html 的前端錯誤收集,小程序的有些差別。以微信小程序爲例,

小程序的全局異常捕獲,能夠在 App 註冊小程序的方法下訂閱。相關文檔點這裏

App({
  onLaunch (options) {
    // Do something initial when launch.
  },
  ...,
  onError (msg) {
    console.log(msg)
  }
})
複製代碼

這裏不太同樣的是,onError 訂閱函數裏只有一個 msg 參數。內容相似 error.stack

msg

小程序的場景比較集中,複雜的環境不多,沒有跨域腳本的問題,比 H5 簡單不少。後面介紹的內容基本通用,對於小程序部分再也不贅述。

badjs上報

要是出了問題,咱們得依靠上報的信息定位問題。那麼就要提早確認哪些信息是須要的。另外考慮到數據量級的問題,太多了服務器容易噎着,所以只上報一些必要的信息。

前端必要上報數據

類別 來源 示例
content message + error.stack "ReferenceError: test is not defined at HTMLLIElement. (wq.360buyimg.com/wecteam/bad…)
有幫助的業務信息 / balabalabala

除了 stack 信息,還有些業務信息(好比跟單、用戶id等)可能有助於定位,看業務背景自行選擇。

另外有些數據是包含在報文當中的,能夠在服務端一塊兒收集和展現,前端不須要再單獨上報。

報文自帶的數據

類別 來源 示例
ip / 58.20.191.9
time / May 20th 2020, 14:56:00.062
referer Headers.Referer wqsou.jd.com/wecTeam?key…
UA Headers.User-Agent Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.12(0x17000c2d) NetType/4G Language/zh_CN
Cookie Headers.Cookie pin=wecTeam;

數據拼接

前端把必要的數據拼接起來,準備發送到服務器。相似下面這條連接:

wq.jd.com/wecteam/bad…

數據傳輸用 get 方式便可。更簡單一些的用 img 傳遞。

var _img = new Image();
_img.src = url;
複製代碼

須要注意的是,get 有最大長度(2048個字符)限制,注意裁剪。若是須要突破限制,請用 post 方式上報。

實例:bj-report.js

可能你會問:道理我都懂,但我不太想努力了,有沒有現成的?

有。

推薦這款鵝廠出品的工具:bj-report.js 點擊這裏進入git主頁

它能夠幫你進行前端日誌上報與JS異常監控。經過簡單的初始化和傳入一些參數,就能夠跳過上文的全部步驟~

BJ_REPORT.init({
  id: 1,                                // 上報 id, 不指定 id 將不上報
  uin: 123,                             // 指定用戶 id, (默認已經讀取 qq uin)
  delay: 1000,                          // 延遲多少毫秒,合併緩衝區中的上報(默認)
  url: "//badjs2.qq.com/badjs",         // 指定上報地址
  ignore: [/Script error/i],            // 忽略某個錯誤
  random: 1,                            // 抽樣上報,1~0 之間數值,1爲100%上報(默認 1)
  repeat: 5,                            // 重複上報次數(對於同一個錯誤超過多少次不上報)
                                        // 避免出現單個用戶同一錯誤上報過多的狀況
  onReport: function(id, errObj){},     // 當上報的時候回調。 id: 上報的 id, errObj: 錯誤的對象
  submit: null,                         // 覆蓋原來的上報方式,能夠自行修改成 post 上報等
  ext: {},                              // 擴展屬性,後端作擴展處理屬性。例如:存在 msid 就會分發到 monitor,
  offlineLog : false,                   // 是否啓離線日誌 [默認 false]
  offlineLogExp : 5,                    // 離線有效時間,默認最近5天
});
複製代碼

數據分析

說完了數據上報,再說一下拿到數據怎麼分析。

數據提取

如今京喜所使用的日誌系統,是基於一個第三方的專業日誌分析系統 Kibana 定製而成的:

kibana系統

圖上簡單的標註了下它有的一些功能,重要的是咱們能夠經過它又快又準的查到想要的信息。

須要省事一些的話,也能夠搭建一個簡單的報表系統:

簡單的報表系統

若是想更省事,能夠直接連上數據服務器,導出一份文檔。

獲取上報數據不屬於本篇主題,這裏就不介紹了。簡而言之,咱們在這裏提取 badjs 日誌。

異常分析

有了數據,就能夠開始快(ku)樂(bi)的分析了。

badjs 能夠分爲兩種,一種是由於開發人員所寫的代碼缺陷而致使的,俗稱 bug。還有一種是人爲故意的,好比安全掃描、刷子、爬蟲、瀏覽器插件等嵌入的第三方的腳本,觸發了本不存在的異常。

咱們須要特別關注前者,分辨出來後者,並儘量不讓數據受後者的影響。

content 信息

content 信息即 messageerror.stack,裏面是錯誤的描述和堆棧信息。

message 是對 error.stack 的補充,而一段完整的 error.stack 信息,包含了錯誤代碼堆棧,文件和行列號。經過這些信息咱們基本能夠判定錯誤的位置和觸發緣由。

好比這一段 stack 信息(chrome 下):

badjs

咱們一行行來看:

  1. Uncaught TypeError: Cannot read property 'style' of undefined

沒法在 undefined 下訪問 style 這個屬性。就是說在某個對象下,某個屬性空了,在這個基礎上又訪問 style 這個屬性,所以報錯了。

  1. at removeAD (badjs.js:4)

該異常代碼位於badjs.js 這個文件裏的第 4 行,所屬的方法名叫作 removeAD

  1. at window.fireBadjs (badjs.js:8)

調用 removeAD 的代碼位於 badjs.js 第 8 行,所屬的方法名叫作 window.fireBadjs

  1. at badjs.html:16

調用 window.fireBadjs 的代碼位於 badjs.html 這個文件的第 16 行。

有這麼詳細的信息,對照源碼查一下,對於 badjs 的緣由內心基本就有底了。

還有一種比較短的堆棧信息:

anonymous

發生異常的位置很奇怪,第一行第一列,在 anonymous 中觸發。回去一查代碼,發現第一行第一列根本沒有這個方法。

這個實際上是在瀏覽器的匿名函數(即 anonymous)中執行的代碼,相似直接打在 console 中的代碼,或者經過 eval 等函數運行的代碼。

事實上,上面圖片裏的這個 badjs 出現的背景,是 App 的 native 用 js 代碼在 Webview 裏直接執行了一個 window 做用域下的 callback (即 getNetWrokCallback)。可是這個函數不存在,因此出現異常。

有了上面這個基礎,再來看看這個錯誤。

某錯誤

一看錯誤內容,這不是和上面那個例子同樣嗎,可是沒有 anonymous 的信息。但咱們代碼裏面沒有這個 SOHUZ 的屬性,因此先猜想多是某個 App 主動執行的 js 代碼致使的異常。

進一步的分析須要結合 UA 的信息。UA 裏有 kuaizhanAndroidWrapper 這個字段,經過萬能的互聯網,發現"快站" App 的身份信息裏包含這個字段。

所以結論出來了:"快站" App 裏訪問了這個頁面,但它沒有進行非空檢查,直接訪問了 SOHUZ,致使發生 badjs 報在咱們業務這裏了。非錯誤,結案。

UA

在上面一段的結尾提早介紹了下 UA 信息的做用。

咱們能經過 UA,推斷出環境:好比運行在某微信、某 App,某瀏覽器等。還能推斷出是誰在訪問咱們的頁面:好比百度爬蟲、xx爬蟲、阿里百川等。

UA 能夠簡單的做爲用戶指紋。京喜的頁面會常常被不知名的網友刷,天天還特別準時。由於不肯定來源,只能戲謔的稱之爲:刷子。有時咱們會遇到大量奇怪的報錯,可是背後的 UA 都是如出一轍的,基本能夠認定是有網友在刷咱們的的頁面。

須要強調的一點是,UA 能夠僞造。有些有經驗的刷子,每次的 UA 都是不同的,迷惑性很強。所以 UA 信息是否真實,還得具體狀況具體分析。

初見 Script error

京喜 H5 業務線上的 badjs 日誌,其實絕大部分詳情只展現了 'Script error'。

Script error

出現 Script error,是由於引入了跨域腳本。好比經過 script 標籤引入的其餘非同域 js 文件,裏面發生了 badjs,這時在 onerror 事件裏的 message 信息就只有一句 Script error,error 信息爲 null

跨域error信息

對於這些已經上報了的 Script error 數據,能獲得的數據很是有限,大多數時候只能放棄。

可是對於這個問題並非沒有任何解決方法,咱們能夠在上報的時候"打開" Script error 裏的信息,再上報它。好比跨域腳本設置信任策略等一些方案。

關於 Script error 詳細的應對策略,咱們將在下面的內容裏進一步討論它。

Script error

上面賣了很多關子,這一段深刻一下,討論怎麼能挖出 Script error 裏有效的信息。

來源

Script error 本質是由於瀏覽器的跨域安全策略行爲,保護非同域代碼內容的安全。

全部經過 script 標籤引入的跨域腳本,若是出現異常,window 下的 error 事件都只能獲得 'Script error'。

Script error

解決方案

對於 script 標籤引入的跨域腳本產生 'Script error' 的問題,業內有解決方案。

大概思路就是:不就是安全問題嘛,只要雙方都以爲可信,那我瀏覽器就給你放開這個限制。

具體實現以下:

1.response 頭增長 Access-Control-Allow-Origin,代表受信任的域名

Access-Control-Allow-Origin

2.請求的 script 標籤增長 crossorigin 屬性

crossorigin

crossorigin 有兩個值,分別是

  • anonymous
  • use-credentials

crossorigin='' ,或者其餘字符,和設置 anonymous 的效果同樣。anonymous 依賴 response 頭的 Access-Control-Allow-Origin。須要注意的是,使用 anonymous 時,request 頭不會帶上用戶信息,好比 cookie。相對的,使用 use-credentials 能夠帶上用戶信息。

use-credentials 須要和 response 頭的 Access-Control-Allow-Credentials 配合使用,當 Access-Control-Allow-Credentials 返回 true 的時候,瀏覽器纔會容許運行腳本和信任來源。另外在這種狀況下, Access-Control-Allow-Origin 不能設置爲 *,必須是具體的域名。

Access-Control-Allow-Credentials

經過以上的兩步,web 瀏覽器下的 window error 事件裏的 error 信息就不會被瀏覽器攔截成 'Script error' 了。

一個變種場景: jsonp

這裏提一個 script 標籤的變種場景:jsonp

jsonp自己解決的問題就是跨域接口請求,所以大部分使用場景自帶跨域光環。另外它經過 script 標籤運行,和 js 腳本的性質同樣。

所以當它出現了異常,也會存在 Script error 的狀況。

jsonp 出現異常的常見的場景就是:callback 未定義

callback 未定義

雖然能夠在 console 看到具體錯誤信息,可是在被捕獲的 error 中,由於 url 跨域,沒額外設置信任,只能抓到 Script error。

解決方式就是按照「常規解決方案」裏列舉的兩個步驟去"解構" Script error。

這裏有個問題在於大部分接口依賴用戶信息,前端須要使用 crossorigin='use-credentials' 方式將請求帶上 cookie 信息。所以後臺在 Access-Control-Allow-Origin 中要返回具體的域名,以及將 Access-Control-Allow-Credentials 設置爲 true 以讓瀏覽器經過驗證。

Access-Control-Allow-Credentials

use-credentials

額外提一點,jsonp 產生的腳本絕大部分是非異步代碼。跨域腳本異步代碼有一些坑,後面會介紹。

特殊的解決方案

使用 crossOrigin 是常規解決方案。還有種特殊的方法,就是使用 try...catch

舉個簡單的栗子:

// https://xxx.badjs.js
window.fireBadjs = function () {
    ops
}
複製代碼
<script src="xxx.badjs.js" ></script>
<script> window.addEventListener('error', function (errorEvent) { const { message, filename, lineno, colno, error } = errorEvent debugger }) fireBadjs() try { fireBadjs() } catch (error) { debugger } </script>
複製代碼

badjs.js 裏面往 window 下塞了一個名爲 fireBadjs 的函數,裏面運行了一行會出異常的代碼。

第一個 fireBadjs() 運行後,error 事件觸發,內容以下:

Script error

情理之中,error 事件只能抓到 Script error。

註釋掉第一個 fireBadjs(),讓在 try...catch 中的 fireBadjs() 執行,badjs 被 catch 捕獲。打在 console 裏,它的內容以下:

錯誤信息

神奇的是,本來是 Script error 的內容被挖了出來。

但這種方法終歸不優雅,對代碼侵入性較強。並且還有個小缺陷,就是不會觸發 window 下的 error 事件。

接下來咱們談談怎麼改善。

try catch 加強

try...catch 是一個讓人又愛又恨的工具。不少時候咱們爲了讓代碼有好的健壯性,會用它包裹起來。

可是一旦在 try 中發生了錯誤,瀏覽器不會把錯誤打在 console 裏,也不會觸發 error 事件。

來看一段簡單的代碼:

try {
    JSON.stringify(apiData)
} catch (e) {}
複製代碼

若是 JSON.stringify(apiData) 發生了錯誤,代碼多是運行正常,可是最後表現沒有達到預期,咱們也不知道這裏有問題。最後可能繞了一個大圈子纔回到這裏。

這裏的問題出在異常被捕獲之後,沒有給予咱們提示。那解決方案也很簡單,手動補上提示就好。

這時能夠在 catch 裏,把錯誤打在 console.error 裏面,並手動包裝 ErrorEvent,丟給 window 下的 error 事件捕獲。

try {
    JSON.stringify(apiData)
} catch (error) {
    console.error(error)
    if (ErrorEvent) {
        window.dispatchEvent(new ErrorEvent('error', { error, message: error.message })) // 這裏也會觸發window.onerror
    } else {
        window.onerror && window.onerror(null, null, null, null, error)
    }
}
複製代碼

須要額外注意的是,try...catch 有個缺點,不能捕獲異步代碼的異常。好比 setTimeoutpromise、事件等。由於這個問題,咱們沒法在代碼裏從頭至尾簡單的包裹一層 try...catch 解決全部問題。

Hybrid

開發人員每每在本身的象牙塔內進行改造和升級,可是真實的生產環境每每比預想的更復雜,好比 Hybrid。

這裏是廣義 Hybrid,除了原生 App 以外,瀏覽器也算。事實上 H5 真正的場景就是在 Hybrid 當中,這裏研究一下 H5 在手機 App 裏異常會有什麼表現。

Webview

不少 App 裏面都會內嵌 Webview 運行 H5 頁面。若是 H5 在 Webview 裏面發生了 badjs,各位猜一猜,Webview 的表現是什麼樣的?

這裏分兩個環境 iOS 和 Android。

1.iOS系統 (系統測試版本:9.0.2/11.0.3/13.4)

在 iOS 中的 Webview,跨域腳本的異步代碼若是發生了badjs(注意是異步代碼),無論有沒有按照常規方案去設置跨域頭和 crossOrigin,在 window 下的 error 事件裏都只能抓到 'Script error'。

你沒看錯,只要是 iOS 裏的 Webview,不管是你業務下的 App,仍是微信甚至是瀏覽器。

舉個栗子:在iPhone的微信裏打開了某個 H5 頁面,H5 頁面引用了某 (跨域)cdn 的 js 文件。當這個 js 文件內部某個事件產生 badjs 並上報了,咱們在日誌系統只能看到 'Script error'。

2.Android系統

在 Android 中的 Webview,和web端同樣的表現。設置好了跨域頭和 crossOrigin,若是跨域腳本發生 badjs,不論異步同步代碼,都能在 window 下的 error 事件裏抓到錯誤詳情。

拿一個京喜 h5 的線上的數據,簡單驗證下這個現象:

一個絕大部分流量跑在 微信 和 手機QQ 裏的業務,在 Android 環境下,有 8043 個 Script error

在 iOS 環境下有 25685 個 Script error

順便一提,iOS 下的非 Script error 只有 393 個。

解決方法,主要是針對 iOS 環境。由於 Android 表現和 web 端一致。

1.跨域腳本改成同域腳本。

同域腳本報錯不會顯示 Script error。

2.try...catch 包裹

把跨域的腳本里的異步方法用 try...catch 包裹一下,在 catch 中手動觸發事件。手動包裹比較麻煩,能夠考慮用工具打包的時候自動包裹一下。

3.用 Android 的 badjs 數據參考 iOS

由於兩個環境出異常的機率近似相等,在作好跨域工做之後,專一解決 Android 下的 badjs,就能夠覆蓋絕大部分 iOS 的異常了。

native 代碼注入

這裏和上面一節的區別是,上一節是 H5 在 App 裏"自由落體",這一節是由 App 主動干涉。

App native 往 Webview 注入代碼有兩種方式。第一種是 native 代碼轉換成 js 代碼,再發送到 Webview 經過 js 引擎執行。第二種是用 c++ 代碼,直接寫在引擎的底層,成爲 native code。

第一種方式實際上仍是經過 Webview 環境執行,和 js 代碼無異。常見於在 JSSDK 裏的一些 callback 參數。

App 通常會爲了不邏輯過於複雜,一般就是往 Webview 裏直接執行 window.callback,若是 callback 不存在,出了異常,Android 中和 web 端表現一致。

可是 iOS 裏只有 Script error。

若是你在日誌裏看處處於 anonymous 下第一行第一列的報錯,而且 UA 是 App 環境。那頗有多是由 App 主動的調用產生的 badjs:

App 主動調用

Android 沒什麼好說的,iOS 在 content 這裏則會顯示 Script error。因爲代碼是 App 生成發送到 Webview 當中的,所以前端沒辦法作什麼去解構這個 Script error,只能跟 App 的同窗商量作一層保護,好比調用前先檢查函數是否認義過了。

第二種用 c++ 代碼,直接寫在引擎的底層的方式,常見於 native 生成一段供 H5 使用原生 App 能力的接口,好比 jssdk。這種方式生成的代碼會成爲 native code。

native code 的示例:console.log

native

因爲運行在引擎當中,一旦出錯,Webview 可能整個都掛了。前端 badjs 事件對於這種狀況是無能力爲的,能夠忽略。

iOS stack 差別

實際上 iOS 下的 error stack 還有細節差別,這裏介紹一下。

測試機型:

  • iphone xs max 13.4
  • 榮耀V20 magicUI_3.0.0 andorid_10.0.0.194

測試代碼:

<script> window.addEventListener('error', function (errorEvent) { confirm(errorEvent.error && errorEvent.error.stack) }) nextLineBadjs // 觸發同域badjs </script>
<!-- 設置了跨域頭的腳本 -->
<script src="//wq.360buyimg.com/badjs.js" crossorigin="anonymous"></script>
複製代碼
// badjs.js
setTimeout(() => {
    asyncBadjs // 異步代碼
}, 1000);

syncBadjs // 同步代碼
複製代碼

微信 Webview

iOS 下:

iOS

Android 下:

Android

iOS 當中沒有顯示出來具體的報錯代碼,用 'global code' 指代。跨域異步腳本顯示的是 Script error。

Android 一切正常。

iOS 的 'global code' 增長了一點定位難度,可是由於指明瞭位置,因此問題也不大。額外提一句,這裏是 error.stack 裏面的信息,事實上 message 裏面是有包含 'global code' 裏面的詳細信息。

經過 confirm(errorEvent.message) 咱們把它打出來

'global code'

所以,上報信息的時候能夠把 errorEvent.message 一塊兒帶上去。

其餘 Webview

筆者還測試了其餘移動端的 App 和瀏覽器,有京東 App、百度瀏覽器、chrome、qq瀏覽器、firefox

由於結果類似,沒有本質的區別,這裏就很少展現了。

聊聊後臺和大數據的一些事

大部分同行使用 badjs 日誌系統最大的阻礙來自於後臺的建設。根據 logstash 業界標準日誌採集流程,前端只能完成第一步:採集。對於儲存和展現是無能爲力的,須要後臺同窗的介入。

這裏介紹一下京喜有關的一些背景,以供參考。

數據量級

咱們的日誌數據除了 badjs,還有一些前端主動上報的業務日誌信息。由於部門裏的業務很是多,上報流程也比較規範,上報數據隨着不斷增多的業務節節攀升。如今天天的數據大概24億條(2.5T)。篩掉業務上報,純 badjs 大概只佔了 0.67%。

因爲咱們有大數據機器的資源,所以這些信息跑在咱們本身的數據機器上,沒有額外開銷。

性能優化

雖然機器資源比較足,可是也撐不住無節制的使用。咱們有一些性能優化方案,保證日誌服務穩定。

  • 數據每5天清除一次
    • 數據按期清除,保證儲存空間良性循環
    • 過時的日誌再也不提供查詢能力
  • 上報降級方案
    • 前端有接入配置,當數據井噴,自動調整採樣率,放棄必定比例的上報
    • 後臺也有相似配置,數據量太高,自動放棄一些數據

初版數據收集系統:webmonitor

咱們最初的日誌系統的界面是本身搭建的,使用 url 做爲關鍵字進行查詢 badjs。

簡單的報表系統

日誌數據存在 hdfs 當中。當發起查詢時,使用 impala 暴力掃描 hdfs。效率比較低,查一次大概半分鐘。

這個系統沒有更多的篩選能夠選擇,展現方式也十分粗糙。

第二版數據收集系統:ElasticSearch+Kibana

第二版的日誌系統,大數據同窗接入了 ElasticSearch,創建了日誌文件的索引,而後經過 Kibana 系統和它無縫對接。由於有了索引,查詢速度快了;經過 Kibana,查詢門檻低了,也有更多維度數據的分析了。

kibana系統

查詢速度的提升,對快速定位和迴應上級的效率有了質的飛躍。

咱們能夠根據關鍵詞進行查詢,篩掉無用的,或者選擇感興趣的信息。

也能夠方便的選擇某個數據集中的區間,很快的分析問題和提出解決方案。

從未感受做爲開發人員的幸福感能夠如此之高。

小結

如何收集 badjs

  • 經過 errorEvent 或者 window.onerror
  • 特殊狀況下能夠考慮使用 try...catch

badjs 上報

  • 在 url 上拼接 error.stack 的信息
  • 用 img 等方式發送數據,注意長度

badjs 分析

  • 經過棧信息,人工回溯代碼
  • 經過 UA 簡單判斷環境和用戶身份
  • 對於 Script error 信息,得不到太多有用的價值,分析的時候能夠跳過。可是要想辦法在上報的時候把它"解構"

常見的致使 Script error 的場景

  • 跨域腳本,而且未設置 crossorigin 裏發生異常
  • jsonp,未設置 crossorigin,其對於的 script 生成的 js 腳本里發生異常
  • iOS 下的 跨域腳本 異步代碼 內發生異常
  • iOS 下 native 主動執行 js 代碼發生異常

關於異步代碼須要注意的點

  • ios 下的跨域異步代碼沒法解析 Script error
  • try catch 沒法抓到異步代碼的錯誤
  • jsonp 大可能是同步代碼

必要性

最後回過頭聊一聊有關這個系統的必要性。

對於京喜的業務,這麼一套系統是必須的。由於安全很是重要,咱們根本沒法承擔較長時間,線上出問題後的責任。

下面分析一下它的優點和缺陷。

優點

不肯定本身的代碼有沒有問題,是一件很是不安的事情。要是有客戶用戶投訴過來,那真是一個頭兩個大。

絕大部分前端的業務代碼都是通過測試把關的。若是經過了充分測試,線上代碼業務邏輯出問題的機率比較低,因此關注點集中在有沒有代碼報錯。

這麼一個系統的出現,很大程度上增長了前端同窗的自信心。發版以後,出了問題,可以第一時間回退代碼,定位問題。最重要的是給了老闆們安全感

缺陷

然而這樣一個系統不是萬金油,還有不少其餘維度的問題沒法用它衡量。若是太過於依賴,沒有充分測試業務,上線的時候就看了一眼 badjs 日誌就算完事了,那線上可能會有不少頭大的問題等着你發現。

另外這個資源的開銷也是一個不得不提的問題。若是公司沒有足夠的資源,就須要額外的開銷在租借服務器和存儲設備上。

怎麼選擇

可能有的同窗就要問了:「咱們業務沒有這個東西,何況又依賴那麼多資源,我根本無法拿主意啊」。

不少在處於成長期的業務可能根本沒精力去作這些基礎建設,但我相信在將來的某個時間,你會迫切的須要這些數據。

問一問本身,它能夠減小多少線上事故。

等到須要的時候,不妨回過頭來看看這篇文章。

若是出現問題帶來的損失大於建設成本和維護成本,我相信你必定能說服你所在的團隊和你的老闆。

參考資料

相關文章
相關標籤/搜索