背景
雖然在我們的項目上線前會有很多的測試流程,但是測試流程肯定無法保證 100%覆蓋所有操作場景,在用戶的使用過程中仍會有一些問題暴露出來。
但當線上用戶出現問題,我們需要收到用戶的一個反饋,才能去定位解決,這樣會導致我們的問題解決不夠及時。
并且有些疑難雜癥,我們無法復現定位,這時候我們需要得知用戶的環境、操作等信息,以便于對問題進行排查。
基于以上三點考慮,我認為在大部分項目中都需要接入一個異常監控系統,來實現收集異常、收集日志信息、及時警告、展示統計信息等功能。
為什么選擇 Sentry?
- 免費
- Sentry 可直接使用也可自行搭建
-
兼容性強,基本不受語言限制,搭建一套系統可用于多個項目。
Sentry可適用平臺
Sentry 簡介
Sentry's application monitoring platform helps every developer
diagnose, fix, and optimize the performance of their code.
Sentry 的應用程序監視平臺可幫助每個開發人員診斷,修復和優化其代碼的性能。
Sentry 部署及使用
Sentry 可直接使用也可自行搭建,下面將講解 Sentry 的搭建過程。想要直接體驗的可以跳過直接戳官網進行體驗。
官方提供安裝方式是使用 Docker 和 Docker Compose 以及基于 bash 的安裝和升級腳本。
環境要求
- Docker 19.03.6+
- Compose 1.24.1+
- 4 CPU Cores
- 8 GB RAM
- 20 GB Free Disk Space
- Python 3
這里就不對環境的配置進行詳細說明,想自己搭建的同學可以參考下以下文章。
部署步驟
- 從 github 上獲取 Sentry 最新代碼。
git clone https://github.com/getsentry/onpremise.git
- 進入 onpremise ,運行 install.sh 腳本。
cd onpremise
./install.sh
- 運行
docker-compose up -d
以啟動 Sentry。
按照默認配置構建后,Sentry 默認綁定 9000 端口,訪問 http://127.0.0.1:9000 即可進入登錄頁面。
- 打開頁面填寫信息后即可進入 Sentry 系統
[圖片上傳失敗...(image-709e9-1623860206487)]
-
自動發送警告郵件配置,可以從上圖配置,也可以從配置文件配置,下面為通過 config.yml 配置教程。
打開 onpremise/sentry 下的 config.yml 文件,修改 Mail Server 配置內容。
異常警告發送郵件配置
- mail.backend:郵件發送方式;
- mail.host: 郵件發送域名 ,使用的哪個郵箱可以去該郵箱文檔中找到 smtp 發送域名;
- mail.port: 郵件發送的端口號;
- mail.username:用于 smtp 郵箱的賬號;
- mail.password:用于 smtp 郵箱的密碼;
- mail.use-tls:是否使用 tls 安全協議,這里填寫 true 或 false,和 use-ssl 配置互斥;
- mail.use-ssl:是否使用 ssl 安全協議,這里填寫 true 或 false,和 use-tls 配置互斥;
- mail.from:收到郵件時的發送人名稱;
- 通過配置文件修改郵件服務后,需要運行
docker-compose up -d
更新 Sentry,如果更新失敗則需要關閉 docker-compose 后重新運行。
docker-compose down
docker-compose build
docker-compose up -d
重啟后可通過 頭像-> admin -> mail 進入 mail 配置頁,點擊發送測試郵件來檢測是否配置成功。
Sentry 設置通過 HTTPS 訪問
有沒有發現上面 Sentry 的啟動地址還有 DSN 地址都是 http,現在要給 Sentry 配置 ssl 讓我們能通過 https 使用。
首先要修改 onpremise/sentry/config.yml system.url-prefix
配置,將其設置為我們訪問的 Sentry 域名。 url-prefix
組成了項目的 DSN 地址,一定要保證格式正確。
system.url-prefix: 'https://sentry.xxxxx.com:60000'
然后是 onpremise/sentry/sentry.conf.py 文件下的 SSL/TLS 配置,將原來注釋的部分全部打開。
修改完后同樣要將 docker-compose 關掉重建。
docker-compose down
docker-compose build
docker-compose up -d
上面的配置只是讓 Sentry 允許通過 SSL 代理,下面我們需要在服務器內搭建一個 nginx 用來轉發 https 協議內容,搭建的過程不多說,百度或者請教運維大大。下面是我的 nginx 配置,配好 nginx 并重啟后就算大功告成。我們的 Sentry 終于可以通過 https 訪問了!
server {
# 配置監聽端口
listen 60000 ssl http2 default_server;
listen [::]:60000 ssl http2 default_server;
# 域名
server_name sentry.xxxxx.cn;
# nginx 默認根目錄
root /usr/share/nginx/html;
# 加載其他配置文件,這里是 nginx 默認配置,可以不需要
include /etc/nginx/default.d/*.conf;
# ssl 設置
ssl on;
# ssl 證書地址
ssl_certificate /etc/nginx/sslcert/server.crt;
# ssl 密鑰地址
ssl_certificate_key /etc/nginx/sslcert/server.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
client_max_body_size 200M;
client_body_buffer_size 1024k;
# 這一段是最重要的,將域名代理到本機 http://localhost:9000 服務上,對應的就是 docker 內的 sentry 服務
location / {
proxy_pass http://localhost:9000;
}
gzip on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 9;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/x-shockwave-flash image/png image/x-icon image/gif image/jpeg;
gzip_buffers 16 8k;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
Sentry 接入實例
通過上面的過程 Sentry 系統就搭建好了,訪問配置的地址即可進入 Sentry 系統。沒有搭建的朋友們,也可以直接通過官網體驗下面接入項目的過程。
創建項目
進入系統后點右上角的 New Project
創建項目。
[圖片上傳失敗...(image-7ff2b0-1623860206487)]
這里以 React 項目為例,建立一個 React 項目,按照頁面提示即可在項目中注冊 Sentry,對系項目執行中出現的異常進行捕捉。
[圖片上傳失敗...(image-8d0565-1623860206487)]
配置
Sentry 配置應在應用程序的生命周期中盡早進行。完成此操作后,Sentry 的 JavaScript SDK 會捕獲所有未處理的異常和事務。init 參數詳解:
Sentry.init({
// Sentry 項目的 dsn,可從項目設置中獲取
dsn: "https://xxxxxx@161.xxx.xxx.xxx:9000/2",
// 初始參數配置內容
integrations: [new Integrations.BrowserTracing()],
// 觸發異常后發送給 Sentry 的概率
tracesSampleRate: 1.0,
// 控制應捕獲的面包屑(行為棧)的總量
maxBreadcrumbs: 20,
// 規定上下文數據結構的深度,默認為 3
normalizeDepth: 100,
// 版本信息
release: "common@1.0.0",
// 環境信息
environment: process.env.NODE_ENV,
// 鉤子函數,在每次發送 event 前觸發
beforeSend(event) {
// 網頁應用刷新后設置的變量會消失,所以我選擇在 beforeSend 觸發時插入用戶信息
event.user = {
userNick: "xiaohu",
};
return event;
},
});
設置變量
在 Sentry 中,可以使用 set
+ tags
,extra
,contexts
,user
,level
,fingerprint
形式,設置變量。下面將簡單介紹幾種方式,詳情使用方法可見文檔。
通過 setUser 設置全局變量用戶信息示例(不建議使用)
捕捉異常還需要采集用戶信息,在用戶登錄后需要通過 setUser
設置一下用戶信息全局變量,如下所示。注意,通過這種方式設置的全局變量在頁面刷新后會消失。設置用戶信息代碼:
Sentry.setUser({
tenant: {
code: 12345,
name: '測試公司',
_id: 12345
},
orgAccount: {
_id: 54321,
orgName: '是機構啦'
},
user: {
_id: '8910JQ',
loginName: '測試人員小Q'
}
})
設置全局變量后觸發的 issue 中,可看到上傳的用戶數據。
通過 beforeSnd 插入用戶信息
通過 Sentry 機制設置的全局變量在頁面刷新后會消失,這邊我建議通過 beforeSend
函數,修改 event 中的數據來插入需要的全局變量。
Sentry.init({
...,
// 鉤子函數,在每次發送 event 前觸發
beforeSend(event) {
// 在這里可根據業務情況發送用戶信息
event.user = {
userNick: 'xiaohu'
};
return event;
}
});
設置全局變量
// 以下是 Sentry 定義的全局變量,可以直接使用 Sentry api 設置
Sentry.setUser(object);
Sentry.tags(object);
Sentry.extra(object);
Sentry.level(object);
Sentry.fingerprint(object);
// 通過 setContext,設置 key 值,可自定義隨事件傳遞的變量名
Sentry.setContext(key, context);
設置局部變量
captureException
是我常用的一種設置局部變量并上傳報錯的方式,Sentry.captureException(err[, obj])
第一個參數為拋出的異常,第二個參數可附加錯誤信息。
第二個參數為對象,key 值可為 tags
,extra
,contexts
,user
,level
,fingerprint
這 6 種。
Sentry.captureException(error, {
contexts: {
message: {
a: 1,
b: { b: 1 },
},
},
});
設置局部變量網絡報錯信息示例
根據目前前端框架所用的 axios,對網絡請求出錯的局部數據進行收集,代碼示例:
Sentry.captureException(error, {
contexts: {
message: {
url: error.response.config.baseURL + error.response.config.url,
data: error.response.config.data,
method: error.response.config.method,
status: error.response.status,
statusText: error.response.statusText,
responseData: JSON.stringify(error.response.data),
},
},
});
清除全局變量
在用戶退出登錄后需要清除該用戶的用戶信息,有以下兩種方式。
- 通過設置為空,可以清除設置過的數據。
Sentry.setUser();
- 通過
scope.clear()
清除全局變量。
Sentry.configureScope((scope) => scope.clear()); // 清除所有全局變量
Sentry.configureScope((scope) => scope.setUser(null)); // 清除 user 變量
上傳日志信息
有時我們不僅僅要收集異常信息,還需要在頁面中打 log 來收集頁面運行數據,這時可以用 Sentry.captureMessage(err[, obj])
api,進行傳輸日志。使用方法與 captureException
一致,建議將 level 設置為 Info,便于與異常區分開來,避免觸發我們設置的異常警報。
Sentry.captureMessage("Something went fundamentally wrong", {
contexts: {
text: {
hahah: 22,
},
},
level: Sentry.Severity.Info,
});
上傳 sourceMap
Sentry 可將前端項目打包后的 SourceMap 上傳,方便我們查看異常所在的代碼位置。
Sentry 集成sourcemap
卸載 sentry
需要卸載可參考以下鏈接。
如何卸載Sentry?
觸發一個異常看看
onClick={() => {
let a = {};
console.log(a.b.c);
}}
在這里我制造了一個異常代碼,點擊后大家可想而知會出現一個 js error,如下圖。
在 network 中我們可以看到一條請求,Sentry 就是通過這樣的方式上傳錯誤的。
然后轉到 Sentry 系統頁面上,可以看到 Issues 面板中多了一條信息:
點擊進入后可以看到關于這條異常的詳細信息。
可以看到,詳情里收集了大量的異常相關數據,如設備、瀏覽器版本、ip地址、error 數據、用戶操作棧等數據。但光有這些數據是不夠的,原始的上傳信息有時也不足以分析問題,很多時候我們需要根據業務場景定制所采集的數據。下面我們將從捕捉場景和采集內容這兩方面進行分析。
異常信息捕捉場景及采集內容
捕捉場景
針對前端常見業務場景,總結了以下幾個場景需要收集業務報錯信息。
- 接口錯誤
- 網絡錯誤
- js error 報錯
- 主要業務流程異常狀態
- iframe/webview 內部錯誤
以上異常如果觸發了 js error 或者 Promise.reject 并且沒有被拋出,會被 Sentry 自動捕捉到,如果沒有自動觸發可手動 new Error 或 Promise.reject(error) 拋出異常,即可被 Sentry 收集。或者使用 captureMessage Api 收集運行日志。
采集內容
盡管 Sentry SDK 會捕獲所有未處理的異常和事務匯報至系統上,并且會記錄產生異常的頁面及設備信息。但是有這些信息是遠遠不夠的,還需要額外的業務信息,用于更好的區別定位問題。下面是我對上報異常所需要收集的內容進行總結。
異常信息:拋出異常的 error 信息,Sentry 會自動捕捉。
用戶信息:用戶的信息(登錄名、id、level 等),所屬機構信息,所屬公司信息。
行為信息:用戶的操作過程,例如登陸后進入 xx 頁面,點擊 xx 按鈕,Sentry 會自動捕捉。
版本信息:運行項目的版本信息(測試、生產、灰度),以及版本號。
設備信息:項目使用的平臺,web 項目包括運行設備信息及瀏覽器版本信息。小程序項目包括運行手機的手機型號、系統版本及微信版本。
時間戳:記錄異常發生時間,Sentry 會自動捕捉。
異常等級:根據 Sentry 文檔分為以下幾個等級。
- Critical
- Debug
- Error
- Fatal
- Info
- Log
- Warning
平臺信息:記錄異常發生的項目。
有以上內容的補全,再加上原有的異常數據,對于一個報錯是不是就更清晰明了了呢。
小程序接入 Sentry
Sentry 無官方 api 用于提供給小程序,好在有第三方根據提供的庫,可使用原有 api 實現小程序端的異常監控。
安裝
- npm install sentry-mina --save
- yarn add sentry-mina
- 將 browser/sentry-mina.js 拷貝到項目中
使用
import * as Sentry from "sentry-mina/browser/sentry-mina.js";
// import * as Sentry from "sentry-mina";
// config Sentry
Sentry.init({
dsn: "",
});
// Set user information, as well as tags and further extras
Sentry.configureScope((scope) => {
scope.setUser({ id: "4711" });
scope.setTag("user_mode", "admin");
scope.setExtra("battery", 0.7);
// scope.clear();
});
// Add a breadcrumb for future events
Sentry.addBreadcrumb({
message: "My Breadcrumb",
// ...
});
// Capture exceptions, messages or manual events
Sentry.captureMessage("Hello, world!");
Sentry.captureException(new Error("Good bye"));
Sentry.captureEvent({
message: "Manual",
stacktrace: [
// ...
],
});
其他內容見文檔 Sentry 小程序 SDK。
問題總結
下面是我在接入 Sentry 及后續使用時遇到的問題。
consolelog 不顯示 sourmap 地址
這個問題產生的原因是,Sentry 會修改原生 console 方法收集 console,所以這邊不會顯示 sourcemap 地址,可以在開發環境下取消注冊 Sentry。
本文首發于我的博客 mogii'blog 歡迎大家光臨~