jjzjj

前端异常监控平台之Sentry落地

程序员poetry 2023-03-28 原文

Sentry介绍

Sentry 是一套开源的实时的异常收集、追踪、监控系统。这套解决方案由对应各种语言的 SDK 和一套庞大的数据后台服务组成,通过 Sentry SDK 的配置,还可以上报错误关联的版本信息、发布环境。同时 Sentry SDK 会自动捕捉异常发生前的相关操作,便于后续异常追踪。异常数据上报到数据服务之后,会通过过滤、关键信息提取、归纳展示在数据后台的 Web 界面中

支持如下语言

image.png

sentry功能架构

image.png

sentry核心架构

image.png

环境搭建配置

官方Sentry服务

sentry是开源的,如果我们愿意付费的话,sentry给我们提供了方便。省去了自己搭建和维护 Python 服务的麻烦事

登录官网 https://sentry.io 注册账号后接入sdk即可使用

image.png

Sentry私有化部署

Sentry 的管理后台是基于 Python Django 开发的。这个管理后台由背后的 Postgres 数据库(管理后台默认的数据库)、ClickHouse(存数据特征的数据库)、relay、kafka、redis 等一些基础服务或由 Sentry 官方维护的总共 23 个服务支撑运行。可见的是,如果独立的部署和维护这 23 个服务将是异常复杂和困难的。幸运的是,官方提供了基于 docker 镜像的一键部署实现 getsentry/onpremise

sentry 本身是基于 Django 开发的,而且也依赖到其他的如 Postgresql、 Redis 等组件,所以一般有两种途径进行安装:通过 Docker 或用 Python 搭建

前置环境

需要安装对应版本,否则安装会报错

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

安装docker环境

安装工具包

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

设置阿里镜像源

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

启动docker

systemctl start docker

# 设为开机启动
systemctl enable docker

docker 镜像加速(重要)

在后续部署的过程中,需要拉取大量镜像,官方源拉取较慢,可以修改 docker 镜像源

登录阿里云官网,打开 阿里云容器镜像服务。点击左侧菜单最下面的 镜像加速器 ,选择 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

# 使用国内源安装
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

设置docker-compose执行权限

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

创建软链

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

测试是否安装成功:

$ docker-compose --version

docker-compose version 1.22.0, build f46880fe

一键部署

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

onpremise 的根路径下有一个 install.sh 文件,只需要执行此脚本即可完成快速部署,脚本运行的过程中,大致会经历以下步骤:

  • 环境检查
  • 生成服务配置
  • docker volume 数据卷创建(可理解为 docker 运行的应用的数据存储路径的创建)
  • 拉取和升级基础镜像
  • 构建镜像
  • 服务初始化
  • 设置管理员账号(如果跳过此步,可手动创建)
cd onpremise

# 直接运行 ./install.sh 将 Sentry 及其依赖都通过 docker 安装
./install.sh

后续一步一步安装下来

image.png

设置管理员账号(如果跳过此步,可手动创建)

image.png

启动项目执行

在执行结束后,会提示创建完毕,运行 docker-compose up -d 启动服务

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

查看服务运行状态docker-compose ps

image.png

访问项目

所有服务都启动成功后,就可以访问sentry后台了,后台默认运行在服务器的9000端口,这里的账户密码就是安装时让你设置的那个

image.png
image.png

设置语言和时区

点击头像User settings - Account Details的相应菜单设置,刷新后生效

image.png

Vue2 + Sentry

创建一个vue项目

npm i @vue/cli -g

# 初始化vue2项目
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", /^\//],
    }),
  ],
  // 不同的环境上报到不同的 environment 分类
  // environment: process.env.ENVIRONMENT,
  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
  //  高访问量应用可以控制上报百分比
  tracesSampleRate: 1.0,
  release: process.env.SENTRY_VERSION || '0.0.1', // 版本号,每次都npm run build上传都修改版本号
});

// ...

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

我们手动抛出异常,在控制台可见捕获了错误

image.png

image.png

上传sourceMap到sentry

为了方便查看具体的报错内容,我们需要上传sourceMapsentry平台。一般有两种方式 sentry-clisentry-webpack-plugin方式,这里为了方便采用webpack方式

  • source-map 是一个文件,可以让已编译过的代码可以映射出原始源;
  • source-map 就是一个信息文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。

webpack方式上传

npm i @sentry/webpack-plugin -D

修改vue.config.js配置文件

// vue.config.js

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

module.exports = {
  // 打包生成sourcemap,打包完上传到sentry之后在删除,不要把sourcemao传到生产环境
  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', // 版本号,每次都npm run build上传都修改版本号 对应main.js中设置的Sentry.init版本号
          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', // 线上对应的url资源的相对路径 注意修改这里,否则上传sourcemap还原错误信息有问题
          // urlPrefix: 关于这个,是要看你线上项目的资源地址,比如
          // 怎么看资源地址呢, 例如谷歌浏览器, F12控制台, 或者去Application里面找到对应资源打开
        }),
      )
    }
  },
}

获取TOKEN

image.png

image.png

image.png

获取org

image.png

在项目根目录创建.sentryclirc

  • url:sentry部署的地址,默认是https://sentry.io/
  • org:控制台查看组织名称
  • project:项目名称
  • token:生成token需要勾选project:write项目写入权限
# .sentryclirc

[auth]
token=填入控制台创建的TOKEN

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

执行项目打包命令,即可把js下的sourcemap相关文件上传到sentry

npm run build

上传后的sourcemap在这里可以看到

image.png

正确上传过 source-map 的项目,可以看到很清晰的报错位置

进入本地打包的dist,http-server -p 6002 启动一个模拟正式环境部署的服务访问看看效果

image.png

还可以通过 面包屑 功能查看,报错前发生了什么操作

image.png

记得别把sourcemap文件传到生产环境,又大又不安全 删除sourcemap, 基于vue2演示的三种方式

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

// 方式2 单独生成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', // 版本号,每次都npm run build上传都修改版本号
        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() 的功能是将浏览器页面加载和导航检测作为事物,并捕获请求,指标和错误。

  • TPM: 每分钟事务数
  • FCP:首次内容绘制(浏览器第第一次开始渲染 dom 的时间点)
  • LCP:最大内容渲染,代表 viewpoint 中最大页面元素的加载时间
  • FID:用户首次输入延迟,可以衡量用户首次与网站交互的时间
  • CLS:累计布局偏移,一个元素初始时和消失前的数据
  • TTFB:首字节时间,测量用户浏览器接收页面的第一个字节的时间(可以判断缓慢来自网络请求还是页面加载问题)
  • USERuv 数字
  • USER MISERY: 对响应时间难以忍受的用户指标,由 sentry 计算出来,阈值可以动态修改

Vue3 + Vite + Sentry

创建vue3项目

yarn create vite

安装sentry依赖

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", /^\//],
    }),
  ],
  // 不同的环境上报到不同的 environment 分类
//   environment: process.env.ENVIRONMENT,
  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
    //  高访问量应用可以控制上报百分比
  tracesSampleRate: 1.0,
  release: process.env.SENTRY_VERSION || '0.0.1', // 版本号,每次都npm run build上传都修改版本号
});

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', // 版本号,每次都npm run build上传都修改版本号
  deploy: {
   env: 'production',
  },
  skipEnvironmentCheck: true, // 可以跳过环境检查
  sourceMaps: {
   include: ['./dist/assets'],
   ignore: ['node_modules'],
   urlPrefix: '~/assets', // 注意这里设置正确,否则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',
  },
})

此时当执行vite build时,viteSentry这个插件会将构建的sourcemap文件上传到sentry对应的项目release之下。当次版本新捕获到错误时就可以还原出错误行,以及详细的错误信息。

image.png

React + Sentry

使用umi项目接入演示

创建一个umi项目

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,
});

手动抛出异常查看是否能正确上报到sentry

image.png

sourcemap上传

根目录创建配置文件 .sentryclirc

[auth]
token=TOKEN控制台获取,TOKEN需要勾选project:write写入权限

[defaults]
url=https://sentry.io/ // 默认地址
org=组织名称,控制台获取
project=react // 项目名称

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', // 开启sourcemao
  chainWebpack(config, { webpack }){
    if (process.env.NODE_ENV === 'production'){//当为prod时候才进行sourcemap的上传,如果不判断,在项目运行的打包也会上传
      config.plugin("sentry").use(SentryPlugin, [{
        ignore: ['node_modules'],
        include: './dist', //上传dist文件的js
        configFile: './sentryclirc', //配置文件地址,这个一定要有,踩坑在这里,忘了写导致一直无法实现上传sourcemap
        release:'1.0.1', //版本号,自己定义的变量,整个版本号在项目里面一定要对应
        deleteAfterCompile: true,
        urlPrefix: '~/' // js的代码路径前缀
       }])
    }
  },
};
# 执行打包上传sourcemap
npm run build

# 进入dist文件,启动http-server 本地服务模拟线上效果
image.png

修改代码抛出异常,查看控制台sourcemap解析的效果

image.png

注意:npm run build之后,不要把sourcemap上传到生产环境,记得删除

进阶用法

识别用户

在上传的 issues 里面,我们可以借助 setUser 方法,设置读取存在本地的用户信息。(此信息需要持久化存储,否则刷新会消失)

// 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

错误边界

  • 定义错误边界,当组件报错的时候,可以上报相关信息
  • 使用 Sentry.ErrorBoundary。加了错误边界,可以把错误定位到组件上面。

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 个 event 重新制作快照
        maskAllInputs: false, // 将所有输入内容记录为 *
      }),
    ],
    // 不同的环境上报到不同的 environment 分类
    environment: process.env.ENVIRONMENT,
    //  高访问量应用可以控制上报百分比
    tracesSampleRate: 1.0,
    release: process.env.SENTRY_VERSION || '0.0.1', // 版本号,每次都npm run build上传都修改版本号
    logErrors: true
});

在报错后,可以录屏播放错误发生的情况

image.png

手动设置报警

  • 设置报警规则,当我们某些情况,如 issuesperformance 超过我们设置的阈值,会触发 alert
  • 我们可以通过提醒等功能来帮助我们即时发现问题。
image.png

有关前端异常监控平台之Sentry落地的更多相关文章

  1. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

    我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

  2. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  3. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

    我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

  4. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  5. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  6. ruby - 如何捕获 ruby​​ 中的所有异常? - 2

    我们如何捕获或/和处理ruby​​中所有未处理的异常?例如,这样做的动机可能是将某种异常记录到不同的文件或发送电子邮件给系统管理。在Java中我们会做Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandlerex);在Node.js中process.on('uncaughtException',function(error){/*code*/});在PHP中register_shutdown_function('errorHandler');functionerrorHandler(){$error=error_

  7. ruby - Sinatra 中的全局救援和日志记录异常 - 2

    如何在出现异常时指定全局救援,如果您将Sinatra用于API或应用程序,您将如何处理日志记录? 最佳答案 404可以在not_found方法的帮助下处理,例如:not_founddo'Sitedoesnotexist.'end500s可以通过调用带有block的错误方法来处理,例如:errordo"Applicationerror.Plstrylater."end错误的详细信息可以通过request.env中的sinatra.error访问,如下所示:errordo'Anerroroccured:'+request.env['si

  8. 多种方法期间的 Ruby 救援异常 - 2

    我构建了一个简单的银行应用程序,它能够执行通常的操作;充值、提现等我的Controller方法执行这些操作并拯救由帐户或其他实体引发的异常。以下是Controller代码中使用的一些方法:defopen(type,with:)account=createtype,(holders.findwith)addaccountinit_yearly_interest_foraccountboundary.renderAccountSuccessMessage.new(account)rescueItemExistError=>messageboundary.rendermessageendde

  9. ruby-on-rails - 使用 Ruby 正确处理 Stripe 错误和异常以实现一次性收费 - 2

    我查看了Stripedocumentationonerrors,但我仍然无法正确处理/重定向这些错误。基本上无论发生什么,我都希望他们返回到edit操作(通过edit_profile_path)并向他们显示一条消息(无论成功与否)。我在edit操作上有一个表单,它可以POST到update操作。使用有效的信用卡可以正常工作(费用在Stripe仪表板中)。我正在使用Stripe.js。classExtrasController5000,#amountincents:currency=>"usd",:card=>token,:description=>current_user.email)

  10. ruby - 在 ruby​​ 中使用正确的异常子类 - 2

    我可以访问ruby​​的异常层次结构(它在镐和蜂鸟中都提到过),但我不确定使用哪个异常,因为我没有找到关于每个术语含义的任何信息。使用正确的异常类重要吗? 最佳答案 创建您自己的异常时很重要。一个重要的警告是,继承自Exception而不是StandardError(常见错误)的异常不会被rescue捕获(没有任何参数)。 关于ruby-在ruby​​中使用正确的异常子类,我们在StackOverflow上找到一个类似的问题: https://stackove

随机推荐