搭建sentry监控平台,实现前后端异常监控。——从零开始搭建一个高颜值后台管理系统全栈框架(十六)

10,676 阅读6分钟

背景

前面项目基本功能已经实现完了,这一篇集成一下监控平台,监控一下前后端异常。

监控平台选型

阿里云的应用实时监控服务ARMS和字节的应用性能监控全链路版这两个我都在公司里使用过,使用起来差不多,支持的功能也差不多,不过是收费的。

这里我推荐一个开源的监控平台sentry,功能比上面两个强大很多,支持多种语言。有saas版,也可以自己部署一套。

image.png

私有化部署sentry

环境准备

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

部署

执行下面命令,拉取项目

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

代码拉下来之后,项目根目录下,会有一个install.sh文件,执行这个文件。

cd onpremise
./install.sh

image.png

这里选n,继续安装,中间需要设置管理员帐号和密码,这里设置的帐号和密码要记住,后面登录需要用到。安装过程比较慢,要多等一会。

image.png

执行结束后,执行下面命令启动服务,启动也比较慢。

docker-compose up -d

启动项目

启动成功后,访问9000端口,输入刚才设置的帐号和密码登录。

image.png

设置语言

登录之后,点开用户设置,可以设置语言为中文。

image.png

小结

如果不想自己部署,可以到sentry saas平台注册一个帐号,不过只能免费试用30天。

新建前端项目

image.png

image.png image.png

前端项目集成sentry

安装依赖

pnpm i @sentry/react --save

测试验证

安装上面的教程把代码复制到项目里,改造main.tsx文件。

image.png

在登录方法里故意整个错,测试一下

image.png

进入项目后,可以发现报错

image.png

这个最牛的是,可以回放用户的操作,有利于定位问题,这个功能其它几个监控平台都不支持。

image.png

对接react-router

为了更精准的获取页面加载性能信息和页面报错信息,需要把sentry和react-router结合。

image.png

image.png

设置用户上下文

线上报错了,为了更好的调试,我们需要知道当前报错是哪个用户触发的,sentry支持在上传报错信息时注入当前用户信息。

登录成功后,调用setUser方法全局设置用户信息。

image.png

查看报错信息时,可以看到当前用户id了。

image.png

对接React Error Boundary

现在组件渲染的时候,如果有报错,会出现不友好的报错界面。

模拟组件渲染出现异常

image.png

image.png

这个报错页面,其实是react-router默认报错页面,react-router支持自定义报错页面,使用errorElement属性就行了。

image.png

image.png

如果这样写,因为异常被拦截了,没办法上报。还好sentry支持了异常组件,只需要用Sentry.ErrorBoundary组件包裹最外层组件就行了。

image.png

改造完测试了一下,发现不生效,因为上面我们拦截了异常,所以Sentry.ErrorBoundary监听不到。把errorElement去掉也不行,因为react-router内部会捕获异常,不设置errorElement会用内置的。这里我被卡了一段时间,后来在react-router官网中找到了useRouteError这个api,可以获取报错信息。

后来的解决方案是,写一个组件,组件里使用useRouteError获取到报错信息后,再抛出异常,这时候外面的Sentry.ErrorBoundary就能捕获到异常了。

import { useRouteError } from 'react-router-dom';

const RouterErrorElement = () => {
  const error = useRouteError();
  throw error;
}

export default RouterErrorElement;

image.png

image.png

异常也能正常上报了

image.png

美化一下报错页面

import React from 'react';
import { Button, Result } from 'antd';
import { router } from './router';

const ErrorPage: React.FC = () => (
  <Result
    status="error"
    title="出错了"
    subTitle="我们正在努力修复中,请稍后再试。"
    extra={[
      <Button onClick={() => { router.navigate('/') }} type="primary" key="console" >
        回到首页
      </Button>
    ]}
  />
);

export default ErrorPage;

使用刚才封装的报错页面

image.png

image.png

上传sourcemap

打包发布到线上后,因为代码压缩混淆,没办法定位报错的代码。

image.png转存失败,建议直接上传图片文件

sentry提供了命令,可以快速生成配置。

npx @sentry/wizard@latest -i sourcemaps

执行命令后,可以选择使用它的saas平台,还是自己搭建的,如果是自己搭建的选第二个,然后输入自己的平台地址。

image.png

我们已经创建了用户,这里选yes

image.png

选择后,会打开网页,让你授权,授权成功后,选择项目

image.png image.png

这里选择Vite

image.png

选择后,会使用pnpm帮你安装@sentry/vite-plugin依赖

image.png

这里选择yes,我们使用CICD发布

image.png

上面选择完yes后,会给你生成一个authToken,这个token要配置github环境变量里。下面继续选yes。 image.png

然后就完成了,有几个地方需要改造一下。

  • 删除env.sentry-build-plugin文件,因为这个文件里存了token。生成的token不能放在项目里,项目是公开的,token会泄漏。

  • 这里因为我们使用的是github workflow,可以把token配在github中,然后代码里从环境变量里去token。

image.png

  • 因为需要上传sourcemap,所以脚本把sourcemap打开了,打包出来的代码有sourcemap文件,这玩意不能上传到线上,不然源代码就泄漏了,所以在sourcemap上传到sentry平台后,需要给移除掉。

    image.png

打包发布后,这里可以查看到sourcemap。

image.png

报错也能定位源码了

image.png

到此前端异常监控搞定了,前端性能监控和埋点我还在研究中,等研究好了,再出一篇文章。

新建后端项目

sentry没有midway插件,但是支持koa项目,midway底层用的就是koa,所以这里选koa项目。

image.png

image.png

后端项目集成sentry

安装依赖

pnpm i --save @sentry/node @sentry/profiling-node @sentry/utils

初始化Sentry

src/configuration.ts文件中加入下面代码,初始化Sentry image.png

使用sentry捕获异常

普通的业务报错,我们不用上报,只上报500的异常。

改造src/filter/default.filter.ts文件

import { Catch } from '@midwayjs/core';
import { Context } from '@midwayjs/koa';
import * as Sentry from '@sentry/node';
@Catch()
export class DefaultErrorFilter {
  async catch(err: Error, ctx: Context) {
    // 捕获异常,并把异常和接口绑定一起上报
    Sentry.withScope(scope => {
      scope.addEventProcessor(event => {
        return Sentry.addRequestDataToEvent(event, ctx.request);
      });
      Sentry.captureException(err, { user: { id: ctx?.userInfo?.userId } });
    });

    ctx.status = 500;

    return {
      code: 500,
      message: '系统错误',
    };
  }
}

改造获取当前用户信息接口,故意写一段报错代码

image.png

前端访问一下接口,然后线上就能看到报错信息。

image.png

统计接口执行时间

编写中间件,这里的代码参考了官网koa项目的示例代码改造而来的,理解起来比较费劲。

// src/middleware/sentry.ts
import { Middleware, IMiddleware } from '@midwayjs/core';
import { NextFunction, Context } from '@midwayjs/koa';
import * as Sentry from '@sentry/node';
import { stripUrlQueryAndFragment } from '@sentry/utils';

@Middleware()
export class SentryMiddleware implements IMiddleware<Context, NextFunction> {
  resolve() {
    return async (ctx: Context, next: NextFunction) => {
      return new Promise<void>(resolve => {
        Sentry.runWithAsyncContext(() => {
          const hub = Sentry.getCurrentHub();
          hub.configureScope(async scope => {
            scope.addEventProcessor(event => {
              return Sentry.addRequestDataToEvent(event, ctx.request);
            });

            const reqMethod = (ctx.method || '').toUpperCase();
            const reqUrl = ctx.url && stripUrlQueryAndFragment(ctx.url);

            const transaction = Sentry.startTransaction({
              name: `${reqMethod} ${reqUrl}`,
              op: 'api',
            });

            Sentry.getCurrentHub().configureScope(scope => {
              scope.setSpan(transaction);
            });

            ctx.__sentry_transaction = transaction;

            await next();

            if (ctx._matchedRoute) {
              const mountPath = ctx.mountPath || '';
              transaction.setName(
                `${reqMethod} ${mountPath}${ctx._matchedRoute}`
              );
            }
            transaction.setHttpStatus(ctx.status);
            transaction.finish();

            resolve();
          });
        });
      });
    };
  }

  static getName(): string {
    return 'sentry';
  }
}

查看效果

image.png

上面有几个参数要说一下

p50: 所有用户请求这个接口请求时间的中位数,从小到大排序。

p95: 所有用户请求这个接口请求时间的95%的值,从小到大排序。假设有有100个人请求,从小到大排序后,第95个位置上的值。

这里为啥不使用请求时间平均值,因为如果某次接口出现异常调用时间特别长,会导致平均值和实际值差距会很大。

最后

到此最基本的前后端异常监控搞定了,性能分析和埋点我还在研究中,后面研究完再给大家分享吧。

项目体验地址:fluxyadmin.cn/user/login

前端仓库地址:github.com/dbfu/fluxy-…

后端仓库地址:github.com/dbfu/fluxy-…