前端異常監(jiān)控平臺(tái)之Sentry落地

Sentry介紹

Sentry 是一套開(kāi)源的實(shí)時(shí)的異常收集、追蹤、監(jiān)控系統(tǒng)。這套解決方案由對(duì)應(yīng)各種語(yǔ)言的 SDK 和一套龐大的數(shù)據(jù)后臺(tái)服務(wù)組成,通過(guò) Sentry SDK 的配置,還可以上報(bào)錯(cuò)誤關(guān)聯(lián)的版本信息、發(fā)布環(huán)境。同時(shí) Sentry SDK 會(huì)自動(dòng)捕捉異常發(fā)生前的相關(guān)操作,便于后續(xù)異常追蹤。異常數(shù)據(jù)上報(bào)到數(shù)據(jù)服務(wù)之后,會(huì)通過(guò)過(guò)濾、關(guān)鍵信息提取、歸納展示在數(shù)據(jù)后臺(tái)的 Web 界面中

支持如下語(yǔ)言

image.png

sentry功能架構(gòu)

image.png

sentry核心架構(gòu)

image.png

環(huán)境搭建配置

官方Sentry服務(wù)

sentry是開(kāi)源的,如果我們?cè)敢飧顿M(fèi)的話,sentry給我們提供了方便。省去了自己搭建和維護(hù) Python 服務(wù)的麻煩事

登錄官網(wǎng) https://sentry.io 注冊(cè)賬號(hào)后接入sdk即可使用

image.png

Sentry私有化部署

Sentry 的管理后臺(tái)是基于 Python Django 開(kāi)發(fā)的。這個(gè)管理后臺(tái)由背后的 Postgres 數(shù)據(jù)庫(kù)(管理后臺(tái)默認(rèn)的數(shù)據(jù)庫(kù))、ClickHouse(存數(shù)據(jù)特征的數(shù)據(jù)庫(kù))、relay、kafka、redis 等一些基礎(chǔ)服務(wù)或由 Sentry 官方維護(hù)的總共 23 個(gè)服務(wù)支撐運(yùn)行??梢?jiàn)的是,如果獨(dú)立的部署和維護(hù)這 23 個(gè)服務(wù)將是異常復(fù)雜和困難的。幸運(yùn)的是,官方提供了基于 docker 鏡像的一鍵部署實(shí)現(xiàn) getsentry/onpremise

sentry 本身是基于 Django 開(kāi)發(fā)的,而且也依賴(lài)到其他的如 Postgresql、 Redis 等組件,所以一般有兩種途徑進(jìn)行安裝:通過(guò) Docker 或用 Python 搭建

前置環(huán)境

需要安裝對(duì)應(yīng)版本,否則安裝會(huì)報(bào)錯(cuò)

  • Docker 19.03.6+
  • Docker-Compose 1.28.0+
  • 4 CPU Cores
  • 8 GB RAM
  • 20 GB Free Disk Space

安裝docker環(huán)境

安裝工具包

yum install yum-utils device-mapper-persistent-data lvm2 -y
image.png

設(shè)置阿里鏡像源

yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
image.png

安裝docker

yum install docker-ce docker-ce-cli containerd.io -y

啟動(dòng)docker

systemctl start docker

# 設(shè)為開(kāi)機(jī)啟動(dòng)
systemctl enable docker

docker 鏡像加速(重要)

在后續(xù)部署的過(guò)程中,需要拉取大量鏡像,官方源拉取較慢,可以修改 docker 鏡像源

登錄阿里云官網(wǎng),打開(kāi) 阿里云容器鏡像服務(wù)。點(diǎn)擊左側(cè)菜單最下面的 鏡像加速器 ,選擇 Centos

image.png
vi /etc/docker/daemon.json
{
  "registry-mirrors": ["https://l6of9ya6.mirror.aliyuncs.com"]
}

然后重啟docker即可

# 重新加載配置
systemctl daemon-reload

# 重啟docker
systemctl restart docker

安裝docker-compose

# 使用國(guó)內(nèi)源安裝
sudo curl -L "https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

設(shè)置docker-compose執(zhí)行權(quán)限

chmod +x /usr/local/bin/docker-compose

創(chuàng)建軟鏈

sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

測(cè)試是否安裝成功:

$ docker-compose --version

docker-compose version 1.22.0, build f46880fe

一鍵部署

git clone https://github.com/getsentry/onpremise

onpremise 的根路徑下有一個(gè) install.sh 文件,只需要執(zhí)行此腳本即可完成快速部署,腳本運(yùn)行的過(guò)程中,大致會(huì)經(jīng)歷以下步驟:

  • 環(huán)境檢查
  • 生成服務(wù)配置
  • docker volume 數(shù)據(jù)卷創(chuàng)建(可理解為 docker 運(yùn)行的應(yīng)用的數(shù)據(jù)存儲(chǔ)路徑的創(chuàng)建)
  • 拉取和升級(jí)基礎(chǔ)鏡像
  • 構(gòu)建鏡像
  • 服務(wù)初始化
  • 設(shè)置管理員賬號(hào)(如果跳過(guò)此步,可手動(dòng)創(chuàng)建)
cd onpremise

# 直接運(yùn)行 ./install.sh 將 Sentry 及其依賴(lài)都通過(guò) docker 安裝
./install.sh

后續(xù)一步一步安裝下來(lái)

image.png

設(shè)置管理員賬號(hào)(如果跳過(guò)此步,可手動(dòng)創(chuàng)建)

image.png

啟動(dòng)項(xiàng)目執(zhí)行

在執(zhí)行結(jié)束后,會(huì)提示創(chuàng)建完畢,運(yùn)行 docker-compose up -d 啟動(dòng)服務(wù)

image.png
docker-compose up -d
image.png

查看服務(wù)運(yùn)行狀態(tài)docker-compose ps

image.png

訪問(wèn)項(xiàng)目

所有服務(wù)都啟動(dòng)成功后,就可以訪問(wèn)sentry后臺(tái)了,后臺(tái)默認(rèn)運(yùn)行在服務(wù)器的9000端口,這里的賬戶(hù)密碼就是安裝時(shí)讓你設(shè)置的那個(gè)

image.png
image.png

設(shè)置語(yǔ)言和時(shí)區(qū)

點(diǎn)擊頭像User settings - Account Details的相應(yīng)菜單設(shè)置,刷新后生效

image.png

Vue2 + Sentry

創(chuàng)建一個(gè)vue項(xiàng)目

npm i @vue/cli -g

# 初始化vue2項(xiàng)目
vue create vue2-sentry

接入sentry

image.png
# Using npm
npm install --save @sentry/vue @sentry/tracing
// src/main.js
import Vue from "vue";
import Router from "vue-router";
import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";

Vue.use(Router);

const router = new Router({
  // ...
});

Sentry.init({
  Vue,
  dsn: "http://xdsdfafda21212@119.75.24.41:9000/2",
  integrations: [
    new Integrations.BrowserTracing({
      routingInstrumentation: Sentry.vueRouterInstrumentation(router),
      tracingOrigins: ["localhost", "my-site-url.com", /^\//],
    }),
  ],
  // 不同的環(huán)境上報(bào)到不同的 environment 分類(lèi)
  // environment: process.env.ENVIRONMENT,
  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
  //  高訪問(wèn)量應(yīng)用可以控制上報(bào)百分比
  tracesSampleRate: 1.0,
  release: process.env.SENTRY_VERSION || '0.0.1', // 版本號(hào),每次都npm run build上傳都修改版本號(hào)
});

// ...

new Vue({
  router,
  render: h => h(App),
}).$mount("#app");

我們手動(dòng)拋出異常,在控制臺(tái)可見(jiàn)捕獲了錯(cuò)誤

image.png

image.png

上傳sourceMap到sentry

為了方便查看具體的報(bào)錯(cuò)內(nèi)容,我們需要上傳sourceMapsentry平臺(tái)。一般有兩種方式 sentry-clisentry-webpack-plugin方式,這里為了方便采用webpack方式

  • source-map 是一個(gè)文件,可以讓已編譯過(guò)的代碼可以映射出原始源;
  • source-map 就是一個(gè)信息文件,里面儲(chǔ)存著位置信息。也就是說(shuō),轉(zhuǎn)換后的代碼的每一個(gè)位置,所對(duì)應(yīng)的轉(zhuǎn)換前的位置。

webpack方式上傳

npm i @sentry/webpack-plugin -D

修改vue.config.js配置文件

// vue.config.js

const SentryCliPlugin = require('@sentry/webpack-plugin')

module.exports = {
  // 打包生成sourcemap,打包完上傳到sentry之后在刪除,不要把sourcemao傳到生產(chǎn)環(huán)境
  productionSourceMap: process.env.NODE_ENV !== 'development',
  configureWebpack: config=> {
    if (process.env.NODE_ENV !== 'development') {
      config.plugins.push(
        new SentryCliPlugin({
          include: './dist/js', // 只上傳js
          ignore: ['node_modules', 'webpack.config.js'],
          ignoreFile: '.sentrycliignore',
          release: process.env.SENTRY_VERSION || '0.0.1', // 版本號(hào),每次都npm run build上傳都修改版本號(hào) 對(duì)應(yīng)main.js中設(shè)置的Sentry.init版本號(hào)
          cleanArtifacts: true, // Remove all the artifacts in the release before the upload.
          // URL prefix to add to the beginning of all filenames. Defaults to ~/ but you might want to set this to the full URL. This is also useful if your files are stored in a sub folder. eg: url-prefix '~/static/js'
          urlPrefix: '~/js', // 線上對(duì)應(yīng)的url資源的相對(duì)路徑 注意修改這里,否則上傳sourcemap還原錯(cuò)誤信息有問(wèn)題
          // urlPrefix: 關(guān)于這個(gè),是要看你線上項(xiàng)目的資源地址,比如
          // 怎么看資源地址呢, 例如谷歌瀏覽器, F12控制臺(tái), 或者去Application里面找到對(duì)應(yīng)資源打開(kāi)
        }),
      )
    }
  },
}

獲取TOKEN

image.png

image.png

image.png

獲取org

image.png

在項(xiàng)目根目錄創(chuàng)建.sentryclirc

  • url:sentry部署的地址,默認(rèn)是https://sentry.io/
  • org:控制臺(tái)查看組織名稱(chēng)
  • project:項(xiàng)目名稱(chēng)
  • token:生成token需要勾選project:write項(xiàng)目寫(xiě)入權(quán)限
# .sentryclirc

[auth]
token=填入控制臺(tái)創(chuàng)建的TOKEN

[defaults]
url=https://sentry.io/
org=sentry
project=vue

執(zhí)行項(xiàng)目打包命令,即可把js下的sourcemap相關(guān)文件上傳到sentry

npm run build

上傳后的sourcemap在這里可以看到

image.png

正確上傳過(guò) source-map 的項(xiàng)目,可以看到很清晰的報(bào)錯(cuò)位置

進(jìn)入本地打包的dist,http-server -p 6002 啟動(dòng)一個(gè)模擬正式環(huán)境部署的服務(wù)訪問(wèn)看看效果

image.png

還可以通過(guò) 面包屑 功能查看,報(bào)錯(cuò)前發(fā)生了什么操作

image.png

記得別把sourcemap文件傳到生產(chǎn)環(huán)境,又大又不安全 刪除sourcemap, 基于vue2演示的三種方式

// 方式1
"scripts": {
  "build": "vue-cli-service build && rimraf ./dist/js/*.map"
}

// 方式2 單獨(dú)生成map
// vue.config.js
configureWebpack(config) {
     config.output.sourceMapFilename('sourceMap/[name].[chunkhash].map.js')
     config.plugin('sentry').use(SentryCliPlugin, [{
        include: './dist/sourceMap', // 只上傳js
        ignore: ['node_modules'],
        configFile: 'sentry.properties',
        release: process.env.SENTRY_VERSION || '0.0.1', // 版本號(hào),每次都npm run build上傳都修改版本號(hào)
        cleanArtifacts: true, // 先清理再上傳
    }])
}

// 方式3 webpack插件清理
$ npm i webpack-delete-sourcemaps-plugin -D
// vue.config.js
const { DeleteSourceMapsPlugin } = require('webpack-delete-sourcemaps-plugin')

configureWebpack(config) {
    config.plugin.push(
        new DeleteSourceMapsPlugin(), // 清理sourcemap
    )
}

查看 Performance

image.png

Sentry.init() 中,new Integrations.BrowserTracing() 的功能是將瀏覽器頁(yè)面加載和導(dǎo)航檢測(cè)作為事物,并捕獲請(qǐng)求,指標(biāo)和錯(cuò)誤。

  • TPM: 每分鐘事務(wù)數(shù)
  • FCP:首次內(nèi)容繪制(瀏覽器第第一次開(kāi)始渲染 dom 的時(shí)間點(diǎn))
  • LCP:最大內(nèi)容渲染,代表 viewpoint 中最大頁(yè)面元素的加載時(shí)間
  • FID:用戶(hù)首次輸入延遲,可以衡量用戶(hù)首次與網(wǎng)站交互的時(shí)間
  • CLS:累計(jì)布局偏移,一個(gè)元素初始時(shí)和消失前的數(shù)據(jù)
  • TTFB:首字節(jié)時(shí)間,測(cè)量用戶(hù)瀏覽器接收頁(yè)面的第一個(gè)字節(jié)的時(shí)間(可以判斷緩慢來(lái)自網(wǎng)絡(luò)請(qǐng)求還是頁(yè)面加載問(wèn)題)
  • USERuv 數(shù)字
  • USER MISERY: 對(duì)響應(yīng)時(shí)間難以忍受的用戶(hù)指標(biāo),由 sentry 計(jì)算出來(lái),閾值可以動(dòng)態(tài)修改

Vue3 + Vite + Sentry

創(chuàng)建vue3項(xiàng)目

yarn create vite

安裝sentry依賴(lài)

npm i @sentry/vue @sentry/tracing

初始化sentry

src/main.js中修改

import { createApp } from "vue";
import { createRouter } from "vue-router";
import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";

const app = createApp({
  // ...
});
const router = createRouter({
  // ...
});

Sentry.init({
  app,
  dsn: "http://xdfada1212@12.715.204.41:9000/2",
  integrations: [
    new Integrations.BrowserTracing({
      routingInstrumentation: Sentry.vueRouterInstrumentation(router),
      tracingOrigins: ["localhost", "my-site-url.com", /^\//],
    }),
  ],
  // 不同的環(huán)境上報(bào)到不同的 environment 分類(lèi)
//   environment: process.env.ENVIRONMENT,
  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
    //  高訪問(wèn)量應(yīng)用可以控制上報(bào)百分比
  tracesSampleRate: 1.0,
  release: process.env.SENTRY_VERSION || '0.0.1', // 版本號(hào),每次都npm run build上傳都修改版本號(hào)
});

app.use(router);
app.mount("#app");

sourcemap上傳

修改vite.config.js配置

安裝npm i vite-plugin-sentry -D插件

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import viteSentry from 'vite-plugin-sentry'

const sentryConfig = {
  configFile: './.sentryclirc',
  release: process.env.SENTRY_VERSION || '0.0.1', // 版本號(hào),每次都npm run build上傳都修改版本號(hào)
  deploy: {
   env: 'production',
  },
  skipEnvironmentCheck: true, // 可以跳過(guò)環(huán)境檢查
  sourceMaps: {
   include: ['./dist/assets'],
   ignore: ['node_modules'],
   urlPrefix: '~/assets', // 注意這里設(shè)置正確,否則sourcemap上傳不正確
  },
}

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    process.env.NODE_ENV === 'production' ? viteSentry(sentryConfig) : null,
  ],
  build: {
    sourcemap: process.env.NODE_ENV === 'production',
  },
})

此時(shí)當(dāng)執(zhí)行vite build時(shí),viteSentry這個(gè)插件會(huì)將構(gòu)建的sourcemap文件上傳到sentry對(duì)應(yīng)的項(xiàng)目release之下。當(dāng)次版本新捕獲到錯(cuò)誤時(shí)就可以還原出錯(cuò)誤行,以及詳細(xì)的錯(cuò)誤信息。

image.png

React + Sentry

使用umi項(xiàng)目接入演示

創(chuàng)建一個(gè)umi項(xiàng)目

mkdir umi-sentry && cd  umi-sentry

yarn create umi
image.png
# Using npm
npm install --save @sentry/react @sentry/tracing

接入sentry

初始化sentry

// pages/index.ts

import * as Sentry from "@sentry/react";
import { BrowserTracing } from "@sentry/tracing";

Sentry.init({
  dsn: "https://xdfa@o1334810.ingest.sentry.io/121",
  integrations: [new BrowserTracing()],

  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
  release: '0.0.1',
  tracesSampleRate: 1.0,
});

手動(dòng)拋出異常查看是否能正確上報(bào)到sentry

image.png

sourcemap上傳

根目錄創(chuàng)建配置文件 .sentryclirc

[auth]
token=TOKEN控制臺(tái)獲取,TOKEN需要勾選project:write寫(xiě)入權(quán)限

[defaults]
url=https://sentry.io/ // 默認(rèn)地址
org=組織名稱(chēng),控制臺(tái)獲取
project=react // 項(xiàng)目名稱(chēng)

sourcemap配置上傳

npm i @sentry/webpack-plugin -D
// .umirc.ts 修改

const SentryPlugin = require('@sentry/webpack-plugin');

export default {
  devtool: process.env.NODE_ENV === 'production' ? 'source-map' : 'eval', // 開(kāi)啟sourcemao
  chainWebpack(config, { webpack }){
    if (process.env.NODE_ENV === 'production'){//當(dāng)為prod時(shí)候才進(jìn)行sourcemap的上傳,如果不判斷,在項(xiàng)目運(yùn)行的打包也會(huì)上傳
      config.plugin("sentry").use(SentryPlugin, [{
        ignore: ['node_modules'],
        include: './dist', //上傳dist文件的js
        configFile: './sentryclirc', //配置文件地址,這個(gè)一定要有,踩坑在這里,忘了寫(xiě)導(dǎo)致一直無(wú)法實(shí)現(xiàn)上傳sourcemap
        release:'1.0.1', //版本號(hào),自己定義的變量,整個(gè)版本號(hào)在項(xiàng)目里面一定要對(duì)應(yīng)
        deleteAfterCompile: true,
        urlPrefix: '~/' // js的代碼路徑前綴
       }])
    }
  },
};
# 執(zhí)行打包上傳sourcemap
npm run build

# 進(jìn)入dist文件,啟動(dòng)http-server 本地服務(wù)模擬線上效果
image.png

修改代碼拋出異常,查看控制臺(tái)sourcemap解析的效果

image.png

注意:npm run build之后,不要把sourcemap上傳到生產(chǎn)環(huán)境,記得刪除

進(jìn)階用法

識(shí)別用戶(hù)

在上傳的 issues 里面,我們可以借助 setUser 方法,設(shè)置讀取存在本地的用戶(hù)信息。(此信息需要持久化存儲(chǔ),否則刷新會(huì)消失)

// app/main.js
Sentry.setUser({
  id: 'dfar12e31', // userId cookie.get('userId')
  email: 'test@qq.com', // cookie.get('email')
  username: 'poetry', // cookie.get('username')
})
Vue.prototype.$Sentry = Sentry
image.png

錯(cuò)誤邊界

  • 定義錯(cuò)誤邊界,當(dāng)組件報(bào)錯(cuò)的時(shí)候,可以上報(bào)相關(guān)信息
  • 使用 Sentry.ErrorBoundary。加了錯(cuò)誤邊界,可以把錯(cuò)誤定位到組件上面。

rrweb 重播

npm i @sentry/rrweb rrweb -S
import SentryRRWeb from '@sentry/rrweb';

// app/main.js
Sentry.init({
    Vue,
    dsn: "xxx",
    integrations: [
      new Integrations.BrowserTracing({
        routingInstrumentation: Sentry.vueRouterInstrumentation(router),
        tracingOrigins: ["localhost", "my-site-url.com", /^//],
      }),
      new SentryRRWeb({
        checkoutEveryNms: 10 * 1000, // 每10秒重新制作快照
        checkoutEveryNth: 200, // 每 200 個(gè) event 重新制作快照
        maskAllInputs: false, // 將所有輸入內(nèi)容記錄為 *
      }),
    ],
    // 不同的環(huán)境上報(bào)到不同的 environment 分類(lèi)
    environment: process.env.ENVIRONMENT,
    //  高訪問(wèn)量應(yīng)用可以控制上報(bào)百分比
    tracesSampleRate: 1.0,
    release: process.env.SENTRY_VERSION || '0.0.1', // 版本號(hào),每次都npm run build上傳都修改版本號(hào)
    logErrors: true
});

在報(bào)錯(cuò)后,可以錄屏播放錯(cuò)誤發(fā)生的情況

image.png

手動(dòng)設(shè)置報(bào)警

  • 設(shè)置報(bào)警規(guī)則,當(dāng)我們某些情況,如 issuesperformance 超過(guò)我們?cè)O(shè)置的閾值,會(huì)觸發(fā) alert。
  • 我們可以通過(guò)提醒等功能來(lái)幫助我們即時(shí)發(fā)現(xiàn)問(wèn)題。
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容