這篇文章也發在我的部落格,歡迎圍觀?
前端錯誤監控的必要性
前端監控是相對於後端監控而言的,以往的後端監控只能記錄介面被請求之後所發生的錯誤,但是現在隨著使用者複雜度不斷提高,很多錯誤是在後端介面被請求以前就發生的JS錯誤。對於這種錯誤,傳統的後端錯誤監控是無能為力的,因此前端監控應運而生。
為什麼選擇Sentry
最近想在公司的專案上部署前端監控,以主動捕捉使用者端的錯誤(滿足我們自己的控制慾?),不再等待使用者的反饋(事實上使用者的反饋少之又少?)。本來考慮自己寫一個,奈何水平有限,加之看到已經有不少成熟的方案,所以…你們懂得!目前較為成熟的前端監控方案,具體參考知乎帖子 如何做前端異常監控 ,大致上可供選擇的方案如下:
- Sentry
- Fundebug
- Bugsnag
- BetterJS
因為是想部署到公司自己的伺服器上,上述貌似只有Sentry和BetterJS有提供自行部署的方案。然後考慮到目前前端專案都是壓縮打包後再發布的,能夠結合sourcemaps對錯誤進行定位是非常有必要的,因此從兩者中選擇了Sentry。(上述方案中,Fundebug也是支援sourcemaps的)
如何使用
作為試用,我選擇使用Sentry提供的服務,註冊賬號之後,選擇使用免費版的服務,就可以開始在前端對我們的專案加入前端錯誤監控了。
初步引入錯誤監控模組
首先,當然是要看Sentry提供的docs了,建立專案的步驟可以說是介紹得非常清楚了。
- Create a new project
- Choose a language or framework
- Then follow the docs
OK,建立一個新專案之後,就應該到我的前端專案中引入監控的模組了。此處,因為我的專案是使用Vue的SPA專案,所以使用的是Sentry官方推薦的Raven.js。下載該模組之後,按照官方指引在專案中引入檔案,其中key和project在你註冊賬號之後官方都會自動幫你生成的,非常貼心。
// main.js
import Raven from `raven-js`
import RavenVue from `raven-js/plugins/vue`
Raven
.config(`https://<key>@sentry.io/<project>`)
.addPlugin(RavenVue, Vue)
.install()
複製程式碼
這時候,你只要在你專案中的一個不起眼的角落埋下一個錯誤,然後去觸發一下,就能看到呼叫了raven.js傳送錯誤,並且在你的sentry專案中也能看到相關報錯了。
但是這樣的報錯資訊其實意義不大,除了能夠看到具體的error原因外,錯誤位置根本無從定位。如果錯誤資訊沒有具體到我們足以馬上定位出錯誤位置,那麼我們只能一遍遍地對程式碼進行排查,耗時耗力。這時候就要使用sourcemaps對錯誤進行定位了,所幸Sentry對此的支援也是非常好的。
引入sourcemaps對錯誤進行定位
首先,我們來看官方文件對引入sourcemaps的說明。Sentry允許我們通過三種方式使用sourcemaps,releases API, sentry-cli, sentry-webpack-plugin。考慮到專案構建過程中就一併提交sourcemaps,我個人僅僅實踐了最後一種。
// 來自官方的步驟
1. Start by creating a new authentication token under **[Account] > API**.
2. Ensure you have project:write selected under scopes.
3. Install @sentry/webpack-plugin using npm
4. Create .sentryclirc file with necessary config (see Sentry Webpack Plugin docs below)
5. Update your webpack.config.json
複製程式碼
下面我們來實踐一下:
前兩步,非常簡單,略過略過。
對於第三步, 我個人發現僅僅install @sentry/webpack-plugin似乎還是不夠的,還需要install @sentry/cli,不知道是我個人問題,敬請指出。
第四步,可能是我對這種配置格式不太熟悉,折騰最久就是這一步了。給出官方配置說明,下面是我的基礎配置(其實就是將如defaults.url,auth.token等拆分,寫入配置檔案即可),在專案根目錄下:
// .sentryclirc
[defaults]
url = https://sentry.io/
<!-- 此處可以從你的sentry.io的全路徑看出,如:https://sentry.io/此處是你的org/此處是你的project/ -->
org =
project =
[auth]
token = 此處就是你第一步第二步建立的token
複製程式碼
第五步,更新我的webpack配置,配置我的監控模組
此處參考了一位兄弟的文章,他用了另外一個Plugin實現,請圍觀。
此處宣告,這位大兄弟文中的方法都很好,但是對於需要擷取7位數的引數作為版本號的問題,我個人實踐是擷取後Raven.js無法傳遞正確版本號了,反而不擷取能夠傳遞正確版本號!通過檢視Raven.js的post,發現release非常長,不止7位。
下面這個步驟的urlPrefix屬性特別關鍵,這個是給上傳的檔案新增字首的,因為sentry除了根據版本號,還要匹配特定版本號中的路徑。這個屬性的預設值是~/,而~是僅僅是去掉了網址前面的協議和域名,即拼接起來的map資源路徑是http://host/static/js/app.js.map。但是如果像我的站點,多了一級路徑,那麼就需要配置這個屬性了,否則上傳到Sentry的maps就無法訪問到了。
// webpack.prod.conf.js
const SentryPlugin = require(`@sentry/webpack-plugin`);
plugins: [
new SentryPlugin({
include: `./dist`,
release: process.env.RELEASE_VERSION,
configFile: `sentry.properties`,
urlPrefix: `~/TechPage/`, // 如果不需要也可以不傳這個參
}),
]
複製程式碼
就是下面這個步驟,不應該擷取
// prod.env.js
let gitSha = require(`child_process`).execSync(`git rev-parse HEAD`).toString().trim()
// process.env.RELEASE_VERSION = gitSha.substr(2, 9) 不應擷取
process.env.RELEASE_VERSION = gitSha
module.exports = {
RELEASE_VERSION: `"${gitSha}"`,
NODE_ENV: `"production"`
}
複製程式碼
// main.js
import Raven from `raven-js`
import RavenVue from `raven-js/plugins/vue`
Raven
.config(`https://<key>@sentry.io/<project>`, {
release: process.env.RELEASE_VERSION,
debug: true
})
.addPlugin(RavenVue, Vue)
.install()
複製程式碼
由於sentry是通過release版本匹配一致的方式,使用特定版本的sourcemaps去定位錯誤的,因此我們需要在專案構建的時候生成一個特定的版本號,將這個版本號賦值給Raven.js以及SentryPlugin,這樣就能形成對應關係。當然你也可以通過另外的方式生成相應的release版本號,這都是可以的,只要是能夠唯一對應就可以了。即使你沒有形成對應關係,但只要你釋出了source maps,Sentry預設是會根據js檔案中的sourceMappingURL去找到map檔案,然後定位錯誤的。
Raven.js的配置官方文件也是有詳細說明的,詳情點選。此處我僅僅配置了release以及debug,設定debug為true的話,Raven.js會將一些debug資訊輸出到控制檯。
至此,你觸發專案中的錯誤,就能獲得下面的效果,錯誤資訊也成功定位了,非常的清楚。
使用webpack-sentry-plugin上傳sourcemaps,並且在上傳後刪除,不讓終端使用者接觸maps檔案
此處是參照上面提到那位兄弟的做法了,不過他提到了要在上傳sourcemaps到sentry之後刪除,不讓終端使用者接觸,這是對的 。但是我這邊刪除之後,使用者端發生錯誤之後,沒有辦法把定位後的錯誤資訊返回了,返回的錯誤資訊都是編譯打包之後的程式碼的,可讀性一般。如果發現我哪裡配置錯誤,敬請指出。
經過踩坑,正是由於沒有正確配置urlPrefix(@sentry/webpack-plugin中的屬性,webpack-sentry-plugin中是配置filenameTransform方法),所以當我不釋出source maps時,無法定位錯誤。
const SentryPlugin = require(`webpack-sentry-plugin`);
plugins: [
new SentryPlugin(Object.assign({
release: process.env.RELEASE_VERSION,
deleteAfterCompile: true,
suppressErrors: true,
/** filenameTransform: function (filename) {
var pub = config.build.assetsPublicPath
if (/^///.test(pub)) pub = `http:` + pub
var urlObj = require(`url`).parse(pub)
return `~` + urlObj.pathname.replace(//+$/, ``) + `/` + filename
} */
filenameTransform: function (filename) {
return `~/TechPage/` + filename
},
}, require(`../sentry.conf.js`))),
]
複製程式碼
// sentry.conf.js(位於根目錄)
module.exports = {
// 此處可以從你的sentry.io的全路徑看出,如:https://sentry.io/此處是你的org/此處是你的project
organisation: ``,
project: ``,
apiKey: ``
}
複製程式碼
這種配置,我得到的報錯資訊是這樣的,如下圖:
經過配置,能夠在不釋出source maps的情況下,得到完整的錯誤定位!
最後的最後
如果想將Sentry部署到自己的伺服器,Sentry官方也有詳細的文件,各位請移步。
各位看官如果發現文中我的錯處,請一定不要嫌麻煩,請開展花式吐槽!謝謝!
也歡迎上我的Demo網站點選建立時間一欄觸發錯誤看看Sentry的效果,謝謝!
此處感謝 @時間是海 的提醒,參考了sentry社群裡的問題才注意到urlPrefix這個問題。附上社群的兩篇帖子: