基于Sentry搭建一個前端異常監控系統

背景

雖然在我們的項目上線前會有很多的測試流程,但是測試流程肯定無法保證 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 的搭建過程。想要直接體驗的可以跳過直接戳官網進行體驗。

官方提供安裝方式是使用 DockerDocker Compose 以及基于 bash 的安裝和升級腳本。

環境要求

  • Docker 19.03.6+
  • Compose 1.24.1+
  • 4 CPU Cores
  • 8 GB RAM
  • 20 GB Free Disk Space
  • Python 3

這里就不對環境的配置進行詳細說明,想自己搭建的同學可以參考下以下文章。

部署步驟

  1. 從 github 上獲取 Sentry 最新代碼。
git clone https://github.com/getsentry/onpremise.git
  1. 進入 onpremise ,運行 install.sh 腳本。
cd onpremise
./install.sh
  1. 運行 docker-compose up -d 以啟動 Sentry。

按照默認配置構建后,Sentry 默認綁定 9000 端口,訪問 http://127.0.0.1:9000 即可進入登錄頁面。

  1. 打開頁面填寫信息后即可進入 Sentry 系統

[圖片上傳失敗...(image-709e9-1623860206487)]

  1. 自動發送警告郵件配置,可以從上圖配置,也可以從配置文件配置,下面為通過 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:收到郵件時的發送人名稱;
  1. 通過配置文件修改郵件服務后,需要運行 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 配置,將原來注釋的部分全部打開。


修改 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 + tagsextracontextsuserlevelfingerprint 形式,設置變量。下面將簡單介紹幾種方式,詳情使用方法可見文檔

通過 setUser 設置全局變量用戶信息示例(不建議使用)

捕捉異常還需要采集用戶信息,在用戶登錄后需要通過 setUser 設置一下用戶信息全局變量,如下所示。注意,通過這種方式設置的全局變量在頁面刷新后會消失。設置用戶信息代碼:

Sentry.setUser({
    tenant: {
        code: 12345,
        name: '測試公司',
        _id: 12345
    },
    orgAccount: {
        _id: 54321,
        orgName: '是機構啦'
    },
    user: {
        _id: '8910JQ',
        loginName: '測試人員小Q'
    } 
})

設置全局變量后觸發的 issue 中,可看到上傳的用戶數據。

自定義 user 數據展示

通過 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 值可為 tagsextracontextsuserlevelfingerprint 這 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),
    },
  },
});

清除全局變量

在用戶退出登錄后需要清除該用戶的用戶信息,有以下兩種方式。

  1. 通過設置為空,可以清除設置過的數據。
Sentry.setUser();
  1. 通過 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,如下圖。

image

在 network 中我們可以看到一條請求,Sentry 就是通過這樣的方式上傳錯誤的。

image

然后轉到 Sentry 系統頁面上,可以看到 Issues 面板中多了一條信息:

image

點擊進入后可以看到關于這條異常的詳細信息。

image
image
image

可以看到,詳情里收集了大量的異常相關數據,如設備、瀏覽器版本、ip地址、error 數據、用戶操作棧等數據。但光有這些數據是不夠的,原始的上傳信息有時也不足以分析問題,很多時候我們需要根據業務場景定制所采集的數據。下面我們將從捕捉場景和采集內容這兩方面進行分析。

異常信息捕捉場景及采集內容

捕捉場景

針對前端常見業務場景,總結了以下幾個場景需要收集業務報錯信息。

  1. 接口錯誤
  2. 網絡錯誤
  3. js error 報錯
  4. 主要業務流程異常狀態
  5. 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 地址

consolelog 不顯示 sourmap 地址

這個問題產生的原因是,Sentry 會修改原生 console 方法收集 console,所以這邊不會顯示 sourcemap 地址,可以在開發環境下取消注冊 Sentry。


本文首發于我的博客 mogii'blog 歡迎大家光臨~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容