前端监控想用Sentry?看这一篇就够了🤩

14,302 阅读9分钟

本文正在参加「金石计划」

对于一个大型项目来说,不管是后端还是前端,异常错误的监控都是非常重要的,拥有一套成熟完整的监控体系,能让我们在生产环境上发生错误时,可以第一时间定位错误发生的位置与原因,从而能更快地解决问题并重新发布

对于我们前端来讲,想拥有一套异常监控系统,无非两个方式

  • 自研
  • 使用第三方框架

首先想简单聊聊自研

ppx.jpg

自研

在前端领域,js给我们提供的错误捕获的方式总共有三种

  • try catch: 对于可能报错的地方,可以采取这种方式来进行捕获错误,非常灵活
  • window的error事件:全局兜底错误捕获,当js代码抛错时会触发,需要注意两点
    • 如果手动使用try catch捕获错误,那么window的error事件就不会触发,可以理解为错误冒泡被阻止了
    • 资源(js,css,img)的加载失败,不会触发window的error事件,而是触发它们自己的error事件
  • window的unhandledrejection事件:当Promise被reject且没有reject处理器的时候,会触发unhandledrejection事件

当我们需要搭建自研监控平台时,其核心就是利用这三个方式来进行异常的捕获并上报,由于本文重点并不是如何去搭建一个自研监控平台,因此就不再深入下去了,下面就来聊聊目前一些成熟的第三方框架吧

qidai.jpeg

使用第三方框架

目前市面上成熟的框架有很多,这里罗列一下我目前知道的

是不是好奇为啥这么多监控平台,偏偏Sentry目前是最流行的选择?

dog.jpg

其实原因有以下几点

  • 开源:有社区和官方一起维护,质量杠杠的
  • 支持私有化部署:这块后面会专门讲解
  • 免费:个人觉得也是最核心的原因
  • 文档完备:可以帮助用户更好地使用,避免踩坑

那么接下来就好好聊聊Sentry吧

fuzhu.jpg

Sentry

Sentry是一个基于 Django 构建的现代化实时事件日志监控、记录和聚合平台,主要用于快速地发现故障,支持几乎所有主流开发语言和平台,并提供了现代化UI界面

Snipaste_2023-03-16_17-15-54.png

Sentry可以搜集到客户端非常详细的信息,对于我们排查线上bug非常有帮助

Snipaste_2023-03-17_14-24-02.png

下面是它支持的一些主流语言与平台

Snipaste_2023-03-16_15-12-16.png

由此可见其功能的强大与完备,我猜你是不是已经摩拳擦掌,跃跃欲试了?

inte.jpg

那么就聊聊如何使用它吧,使用sentry的方式分为两种

  • sass:最方便的使用方式,只是官方提供的saas免费版只支持每天5000个event
  • 私有化部署:需要占用自己服务器的资源空间,但数据都存在本地,相对更安全

不管使用哪种方式,都需要在我们前端项目里接入Sentry后才可以运行起来,因此下面就聊聊其接入流程,但首先需要注意下面两个前提

  1. react 应用为例,对于其他平台的接入,可以查看 这个 地址进行查阅
  2. Sentry版本为 23.2.0

接入流程

  1. 安装相关依赖
# Using npm
npm install --save @sentry/react @sentry/tracing

# Using yarn
yarn add @sentry/react @sentry/tracing
  1. 配置sentry
import React from "react";
import ReactDOM from "react-dom";
import * as Sentry from "@sentry/react";
import { BrowserTracing } from "@sentry/tracing";
import App from "./App";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [new BrowserTracing()],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

ReactDOM.render(<App />, document.getElementById("root"));

需要注意这里一个dsn,全称 data source name,用于唯一标识创建的每一个项目,在后台管理界面可以拿到这个值

Snipaste_2023-03-16_17-26-56.png

需要注意的是,这一步的引入,是可以捕获 事件监听器 里的错误,但是对于组件 渲染期间的生命周期 里的错误是无法捕获的,因此需要借助下面的 错误边界 来进行辅助

  1. 引入错误边界,捕获渲染期间的错误
import React from "react";
import * as Sentry from "@sentry/react";

<Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
  <Example />
</Sentry.ErrorBoundary>;

//也可以使用高阶组件的形式
import React from "react";
import * as Sentry from "@sentry/react";

Sentry.withErrorBoundary(Example, { fallback: <p>an error has occurred</p> });
  1. Sentry也提供了上报api,供我们按需进行主动上报,支持的api如下

    • captureException: 上报一个错误,function captureException(exception: any): string;
    • captureMessage:上报一个文本消息,function captureMessage(message: string, level?: Severity): string;
    • captureEvent:上报一个手动创建的事件,function captureEvent(event: Event): string;

通过完成上述的接入流程,如果我们使用的是Sentry的sass,那么就可以愉快地监控项目啦

nice2.jpg

接入完成之后,下面就开始私有化部署的流程吧~

私有化部署

根据我之前的摸索踩坑经验,目前Sentry支持的私有化部署有两种方式

  • 利用脚手架一键部署
  • 自己手动部署Sentry的docker容器

下面分别说明

qidai.jpeg

一键部署

通过上文可以看到,手动部署是很麻烦的,官方也考虑到了这个问题,于是推出了 脚手架 进行一键部署

其方式非常简单,就是把仓库下载到我们服务器的指定位置后,进入项目根目录,然后执行 ./install.sh,之后的安装流程就完全是自动的,除了期间需要我们输入用户名和密码外,不需要做任何事情,非常简单便捷

666.jpg

但是通过这种方式进行部署,对机器的性能是有要求的,具体如下

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

所以部署前,一定要确认自己机器的配置,不然到时候就是白忙活了🤣

还有一点需要注意的是通过这种方式部署后,运行的容器很多,需要占用的端口也很多,如果对这方面有顾虑的话,也需要谨慎使用

手动部署

这种方式需要我们手动去拉取Sentry的官方镜像进行部署,但比较坑的是官方Sentry的镜像最后的更新停留在 2019年,因此部署出来的Sentry版本只有 9.1.2,非常老的版本,很多新功能都不支持,但好在 sourcemap 功能是支持的,因此还是具备可使用性,下面就来具体讲解下这种方式的步骤

  1. 拉取必需镜像
docker pull sentry  
docker pull redis
docker pull postgres
  1. 启动redis和postgres
docker run -d --name sentry-redis --restart=always redis   ###保证了,异常自动拉起
docker run -d --name sentry-postgres -e POSTGRES_PASSWORD=secret -e POSTGRES_USER=sentry --restart=always postgres
  1. 生成sentry秘钥,生成的秘钥后面步骤中都需要填写,很重要
docker run --rm sentry config generate-secret-key
  1. 数据库及账户初始化,过程中需要创建用户和密码,用于Sentry系统的登录
docker run -it --rm -e SENTRY_SECRET_KEY='xxxxx' --link sentry-postgres:postgres --link sentry-redis:redis sentry upgrade
  1. 启动sentry的web服务
docker run -d -p 9000:9000 --name my-sentry -e SENTRY_SECRET_KEY='xxxxx' --link sentry-redis:redis --link sentry-postgres:postgres --restart=always sentry
  1. 启动sentry-cron/work服务
docker run -d --name sentry-cron -e SENTRY_SECRET_KEY='xxxx' --link sentry-postgres:postgres --link sentry-redis:redis sentry run cron
docker run -d --name sentry-worker-1 -e SENTRY_SECRET_KEY='xxxxx' --link sentry-postgres:postgres --link sentry-redis:redis sentry run worker

通过上述步骤,就可以通过 ip或域名:9000 来访问Sentry了

niubi.jpg

关于手动部署,由于其镜像过于老旧,因此在接入流程中会跟最新版的有些区别,根据我之前的踩坑经验,区别点如下

  • 安装的依赖有所不同
# 新版
# 不能使用这些新版的库,不然无法捕获错误
# Using npm
npm install --save @sentry/react @sentry/tracing
# Using yarn
yarn add @sentry/react @sentry/tracing

# 旧版
# Using npm
npm install --save @sentry/browser@5.4.2
# Using yarn
yarn add @sentry/browser@5.4.2
  • 接入sentry库的写法有所不同
//新版,不能使用这些新库
import * as Sentry from "@sentry/react";
import { BrowserTracing } from "@sentry/tracing";
import App from "./App";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [new BrowserTracing()],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

//旧版
import * as Sentry from '@sentry/browser';

Sentry.init({
  dsn: 'abc',
  release: 'test',
});
  • 错误边界的捕获有所不同
//新版,不能用
import React from "react";
import * as Sentry from "@sentry/react";

<Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
  <Example />
</Sentry.ErrorBoundary>;

//旧版,需要自己实现错误边界组件,并在componentDidCatch里主动上报错误
import * as Sentry from '@sentry/browser';
......
componentDidCatch(error) {
  Sentry.captureException(error);
}
....

上传sourcemap

其实通过上述的接入和部署后,已经可以正常地去监控项目里的错误了,但是还可以再往前走一步,那就是上传sourcemap。现在前端项目上线前,都会经过打包编译的过程,这样导致了线上的代码无法去阅读,最终使我们无法去定位线上bug的具体原因

cat.jpg

在我们没有上传sourcemap前,在Sentry里只能看到 编译后 代码的报错行列信息,但这对于我们来说没有意义,我们需要的是定位到源码的位置,因此我们就需要把sourcemap上传到Sentry,从而支持查看源码报错位置信息的功能

上传sourcemap的方式总共两种

  • 利用 @sentry/cli 手动上传,不推荐,因为麻烦
  • 利用插件自动在打包项目时上传sourcemap,推荐,因为省心且与ci/cd高度契合,下文也会着重讲解这种方式的使用步骤

qidai.jpeg

目前前端项目的打包工具使用最多的就是 webpackvite,幸运的是Sentry官方都提供了对应插件进行支持

给个简单的使用示例

// vite.config.ts
import { sentryVitePlugin } from "@sentry/vite-plugin";

export default {
  plugins: [
    sentryVitePlugin({
      url: env.VITE_SENTRY_URL,
      org: env.VITE_SENTRY_ORG,
      project: env.VITE_SENTRY_PRO,
      include: './dist',
      authToken: env.VITE_SENTRY_AUTH,
      telemetry: false,
      releaseInjectionTargets: [join(__dirname, 'src', 'main.tsx')],
      release: info.version
    }),
  ],
};

//webpack.config.js
const SentryCliPlugin = require('@sentry/webpack-plugin');

const config = {
  plugins: [
    new SentryCliPlugin({
      include: '.',
      ignoreFile: '.sentrycliignore',
      ignore: ['node_modules', 'webpack.config.js'],
      configFile: 'sentry.properties',
      release: info.version
    }),
  ],
};

具体配置可以查看对应文档,写的都很详细,只是这里我想单独说明的一点是关于 release 选项的

inte.jpg

release可以理解为版本,项目的每一次部署都可以看做是一个版本

在Sentry里,如果想要支持sourcemap功能,那么 插件里的release 必须和 Sentry SDK里的release 相同,否则无法正常使用sourcemap功能,也就是说sourcemap功能是与版本号强绑定的

//webpack.config.js 需要与入口文件的release相同
new SentryCliPlugin({
    ....
    release: 'test'
    ....
})

//vite.config.ts 需要与入口文件的release相同
sentryVitePlugin({
    ....
    release: 'test'
    ....
})

//index.js 入口文件
....
import * as Sentry from "@sentry/react";
...
Sentry.init({
    ....
    release: 'test'
    ....
});

结语

相信通过本文的阅读,你一定对Sentry非常熟悉了,那么赶紧把这么好的东西用到你的项目里吧,早用早享受!

都看到这里啦,如果本篇文章对你有帮助,希望能 点个赞👍 支持下啦,你们的支持才是我最大的动力!😘

R-C.gif