搭建前端錯誤監控系統
當我們完成一個業務系統的上線時,總是要觀察線上的執行情況,檢視日誌發現問題並進行優化迭代。
因為測試永遠無法做到100%覆蓋,使用者也不會總是按照我們所預期的進行操作,因此我們需要在系統異常時主動對其進行收集上報,以制定解決方案。
當生產環境中產生了一個 bug 時,如何做到迅速報警,找到問題原因,修復後又如何在線上驗證?此時我們需要一個高效的錯誤監控系統。
前端錯誤監控
後端有各種強大的監控服務,給我們的應用穩定性保駕護航,然而大多數情況只能記錄介面被請求之後所發生的錯誤。隨著 Web 應用越來越強大,客戶端的程式碼可就越來越複雜,很多不可預期錯誤發生在使用者端,因此前端錯誤監控就不可或缺了。
Why Sentry
我們需要一個成熟的監控系統,Sentry 就是一個這樣的工具。
Sentry - 正如其名「哨兵」,可以實時監控生產環境上的系統執行狀態,一旦發生異常會第一時間把報錯的路由路徑、錯誤所在檔案等詳細資訊以郵件形式通知我們,並且利用錯誤資訊的堆疊跟蹤快速定位到需要處理的問題。
選擇 Sentry 作為前端監控系統,還因為下幾點:
- 開源
- 對各種前端框架的友好支援 (Vue、React、Angular)
- 支援 SourceMap
Sentry 官方提供的免費服務有次數限制,達到一定限制後繼續使用就需要收費了,但是我們可以利用 Sentry 的開源庫在自己的伺服器上搭建服務,官方已經提供了完善的操作文件。
準備工作
Sentry 的搭建方式主要有兩種:
由於 Docker 更加方便易控,而且官方推薦的也是 Docker 部署,那下面就以 Docker 為例,在滴滴雲的伺服器上從零開始搭建一個 Sentry 服務,本示例中使用的伺服器配置 1核CPU,2G 記憶體,作業系統 CentOs 7.4。
注意事項
- 伺服器的記憶體至少 2G,否則在執行
sentry upgrade
命令時會出現問題。 - 以下所有命令在
sudo
許可權下執行,否則 Docker 無法執行
安裝 Docker
$ yum install docker -y
安裝 Docker-Compose
$ pip install docker-compose
啟動 Docker
$ systemctl start docker
開始搭建
1. 拉取 sentry-onpremise
倉庫
Onpremise 是官方提供的包含了使用 Docker 部署 Sentry 所需依賴的載入程式。
$ git clone https://github.com/getsentry/onpremise.git
2. 建立服務端服務
$ cd onpremise
# 新建本地資料庫和 Sentry 配置的目錄
$ docker volume create --name=sentry-data && docker volume create --name=sentry-postgres
# 建立環境變數配置檔案
$ cp -n .env.example .env
# 構建 Docker Services
$ docker-compose build
# 生成祕鑰
$ docker-compose run --rm web config generate-secret-key
# 複製祕鑰(即最後一行),編輯 .env 檔案,將 SENTRY_SECRET_KEY 對應的 value 修改為剛才複製下來的祕鑰
$ vim .env
# 建立資料庫,生成管理員賬號
$ docker-compose run --rm web upgrade
# 啟動 Sentry 服務
$ docker-compose up -d
# 檢視容器
$ docker-compose ps
如果執行過程中一切正常的話,在瀏覽器中輸入http://ip:9000
就進入 Sentry 的登入頁面了,使用上面建立的管理員使用者名稱和密碼登入系統。
整合客戶端 SDK
經過以上步驟我們 Sentry 服務端完成了,接下來就可以在應用中整合 Sentry 客戶端 SDK,用來在前端程式碼中實時上報錯誤。
登入系統後首先會進入配置頁面,簡單配置一下:
- Root URL: 繫結本 Sentry 服務的域名或者直接填寫本服務的 IP
- Admin Email: 系統管理員的郵件地址
配置好後,進入新增新專案介面,以 Vue 應用為例,點選 New Project 建立新專案,選擇 Vue。建立成功後,按照指引可以看到Sentry 推薦使用 Raven.js
及其 Vue 外掛,在我們的前端專案中引入監控的模組,注意監控模組要在 Vue 之後引入。
$ npm install raven-js
// main.js
// 先引入Vue
import Vue from 'vue'
import Raven from 'raven-js'
import RavenVue from 'raven-js/plugins/vue'
Raven
.config('https://<key>@sentry.io/<project>')
.addPlugin(RavenVue, Vue)
.install()
驗證
到這裡整個錯誤監控系統算是成型了,我們可以製造點錯誤,來驗證一下功能是否符合預期。可以直接在前端程式程式碼裡寫點 bug,觸發之後可以在前端除錯工具的 network 中看到上報錯誤的網路請求,同時在 Sentry Dashboard 會發現這個錯誤,檢視詳情可以看到這個錯誤的詳細資訊了。
// main.js
// 使用一個未宣告過的變數觸發錯誤
console.log(hello)
關聯 SourceMap
目前大多數前端程式基本都會使用 Webpack 之類的工具構建,上線之後的程式碼都是經過壓縮混淆的,所以這樣的報錯資訊其實意義不大,錯誤位置根本無從定位,異常堆疊也難以理解,排查非常耗時費力,此時就要使用 SourceMap 對錯誤進行定位了,而 Sentry 也正對此有很好的支援。
使用 sentry-cli
檢視官方文件對引入 SourceMaps 的說明,可以看到有兩種方式來上傳 SourceMap 檔案:
- sentry-webpack-plugin: Sentry 提供的 Webpack 外掛,靈活性不高
- sentry-cli: 靈活性比較高,可以針對不同專案進行單獨的配置
下面以 sentry-cli 的方式為例,看一下具體流程。
-
安裝 sentry-cli
$ npm install sentry-cli-binary -g
-
生成 token
在 Sentry Dashboard 頁面選擇 API,生成 token,注意要勾選 project:write,開啟專案的寫許可權。
-
登入 cli
$ sentry-cli login
執行後輸入上生成的 token
關於 Relese 控制
說到上傳 SourceMap 就要提一下 Release。在平時開發過程中我們希望不監控開發環境下的異常,也有區分測試環境和生產環境異常的需求,此時就需要 Release 將異常進行版本控制。
-
建立 Release
$ sentry-cli releases -o 組織名稱 -p 專案名稱 new 版本號
組織名稱、專案名稱可以在 Sentry Dashboard 中檢視,同時可以在 “Releases” 中檢視是否建立成功。這樣一來,通過生成不同的版本號,我們可以對異常進行分類。
-
本地應用 Release
回到前端專案,在 Raven config 中新增對應的 Release,並指定版本號,每次上報的異常就會被分類到該版本下。// main.js ... import Raven from 'raven-js'; Raven .config(DSN, { release: '版本號' }) .addPlugin(RavenVue, Vue) .install()
-
刪除 Release
在刪除 Release 時需要將其下的異常處理掉,並將該版本的 SourceMap 檔案清空,否則會報錯$ sentry-cli releases -o 組織名稱 -p 專案名稱 delete 版本號
上傳 SourceMap
以上概念瞭解之後,就可以開始上傳 SourceMap 檔案了。
sentry-cli releases -o 組織名稱 -p 專案名稱 files 版本號 upload-sourcemaps --url-prefix 線上js檔案所在目錄
注意:
- 通常來說
vendor.js
這種各種類庫打包後生成的程式碼,並不是我們程式的原始碼,不需要上傳 --url-prefix
對應的是線上JS檔案的目錄,例如~/static/js/
,其中~/
代表網站根目錄
SourceMap 上傳成功後,就可以在 Sentry 上直接看到報錯所在的原始碼了。
主動捕獲錯誤
現在我們已經能成功監控程式中的大多數錯誤,但是無法捕獲非同步錯誤(比如定時器、介面請求錯誤)此時可以利用 raven.caputureException()
進行主動上報。
// 介面請求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
window.$Raven.captureException(error);
})
// 定時器任務
setTimeout(()=>{
try {
// do something
} catch (err) {
window.$Raven.captureException(err);
}
}, 300)
郵箱設定
完成上面的所有步驟,我們的 Sentry 可以使用了,但是還有一個問題,上報錯誤時並沒有收到郵件,作為一個自動化的異常提醒工具,沒有了提醒功能,它價值就打大打折扣了。
在搭建好的 Sentry 的 Web 頁面中無法配置郵箱 smtp 資訊,需要修改配置檔案 config.yml。
###############
# Mail Server #
###############
mail.backend: 'smtp' # Use dummy if you want to disable email entirely
mail.host: 'smpt.gmail.com' # 輸入郵箱smpt伺服器地址
mail.port: 25
mail.username: 'your-email-address'
mail.password: 'your-email-password'
mail.use-tls: false
然後修改 docker-compose.yml,為其中的 Web 容器新增 volumes 卷,使剛才修改的 config.yml 配置檔案生效。
web:
volumes:
- ./config.yml:/etc/sentry/config.yml
....
然後重啟服務
$ docker-compose up
測試一下,去觸發一個錯誤,應該可以正常接收郵件了。
搭建過程中可能出現的錯誤
$ docker-compose run --rm web upgrade
在執行上面的命令過程中,可能會出現建立資料庫失敗、跳過了建立管理員賬號步驟等錯誤,導致 Web頁面無法使用。首先檢查伺服器的硬體配置,記憶體是否滿足要求 (2G 以上),否則大概率就是資料庫初始化時出錯,此時可以手動初始化資料可以及建立管理員使用者。
- 進入 sentry-web 的 shell
$ docker-compose run --rm web shell
- 初始化資料
$ from sentry.models import Project
$ from sentry.receivers.core import create_default_projects
$ create_default_projects([Project])
- 退出 sentry-web 的 shell,手動建立使用者
$ docker-compose run --rm web createuser
重新整理網頁就可以使用剛剛建立好的賬戶登入。