前端监控Sentry企业级实践

2,364 阅读4分钟

前言

本文将为大家带来Sentry的企业级实践,内容主要涉及如下:

  • Sentry的云部署
  • 结合Gitlab-CI的DevOps实践
  • 前端项目接入细节
  • 微前端架构下的Sentry接入
  • Sentry磁盘占用问题解决

介绍

前端应用 [稳定性] 没有保障 —— 测试永远无法做到100%覆盖, 用户也不会总是按照我们所预期的进行操作,因此我们需要在系统异常时主动对其进行收集上报,以制定解决方案。当生产环境中产生了一个 bug 时,如何做到迅速报警,找到问题原因,修复后又如何在线上验证?此时我们需要一个高效的错误监控系统。

搭建

环境

  • Docker 19.03.6+

  • Compose 1.24.1+

        # docker-compose
    
        # 1.安装
        sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    
        # 2.对二进制文件执行权限
        sudo chmod +x /usr/local/bin/docker-compose
    
        # 如果这一步遇到问题,可以创建/usr/bin路径建立链接
        sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
    
        # 3.检查版本
        docker-compose --version
    
  • 最小内存 2GB RAM

准备

  • 仓库下载

  • SMTP邮件服务

    • 此处以QQ邮箱为例 ( 设置 - 账户 ) , 开启SMTP 服务, 开启之后需要保存好密钥

      email-qq.png

      点击 “ 什么是 IMAP,它又是如何设置?” 查看详细设置说明

安装

    ./install.sh

创建用户

根据提示创建管理员账户

启动服务

    # 构建
    docker-compose build

    # 启动服务
    docker-compose up -d
    

搭建完成

  • 初始化信息填写

    welcome.png

  • 配置文件(初始化未填写或后期修改)

项目接入

Sentry & Gitlab 初始配置

Sentry AuthToken 创建

image.png

Sentry添加Gitlab拓展

点击 项目 -> 集成 -> GitLab 来查看 Sentry 中是否配置了 Gitlab 项目组。

image.png

点击右上角添加按钮

image.png

按照 Sentry 的提示在 GitLab 中完成相关操作

image.png

image.png

点击提交后并成功后,GitLab项目组就添加成功了。

Sentry 新建项目

image.png

image.png

image.png

Sentry 发布仓库关联

image.png

image.png

image.png ⚠注意:添加管理追踪的 GitLab 仓库时,需要对应仓库的管理员权限

钉钉群添加机器人

开发可自定义勾选,建议勾选issues events 、 release events、tag push events

image.png

前端工程操作

依赖安装

默认已科学上网

# SDK 安装
npm i -S @sentry/vue @sentry/tracing @sentry/integrations

# SourceMap 支持依赖安装
npm i -D @sentry/webpack-plugin

⚠️ 若@sentry/webpack-plugin安装失败,可执行以下指令重试

npm config set sentrycli_cdnurl="https://npm.taobao.org/mirrors/sentry-cli"
npm uninstall @sentry/webpack-plugin
npm i -D @sentry/webpack-plugin

commit-msg 提交脚本工具,建议全局安装 (git工作流交互式脚本,其中bugfix同步issue很nice)

npm i -g git-cz

.env.[环境] 工程环境变量配置

# sentry
SENTRY_HOST=http://192.168.5.59:9000/
VUE_APP_SENTRY_ORG=sentry

SENTRY_AUTH_TOKEN=<your sit auth token 粘贴token>
VUE_APP_SENTRY_DSN=http://xxxxxxxxxxxxxxxx@192.168.5.59:9000
VUE_APP_SENTRY_PROJECT_ID=<sit your-project-id>

Sentry SDK 集成

Sentry Vue Plugin

// > src/sentry/index.js
import pkg from '../../package.json'
import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'

const MateSentry = {
  install: (app, opts) => {
    const { router, tracingOrigins = ['localhost', process.env.VUE_APP_SENTRY_TRACING_ORIGIN, /^\//] } = opts
    
     app.config.globalProperties.$sentry = Sentry

    console.log(`[Sentry init release]${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || pkg.version}`)

    Sentry.init({
      app,
      // 项目监控api address
      dsn: `${process.env.VUE_APP_SENTRY_DSN}/${process.env.VUE_APP_SENTRY_PROJECT_ID}`,
      // 发布
      release: `${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || 'v' + pkg.version}`,
      // 环境, 可自定义支持多环境
      environment: process.env.NODE_ENV,
      // is optional and is true if it is not provided. If you set it to false, Sentry will suppress sending all Vue components' props for logging.
      attachProps: true,
      // is optional and is false if it is not provided. If you set it to true, Sentry will call Vue's original logError function as well.
      logErrors: true,
      // 打开或关闭调试模式。如果启用了调试,则在发送事件出错时,SDK将尝试打印出有用的调试信息。默认值始终为false。通常不建议在生产debug中将其打开,尽管打开模式不会引起任何安全隐患。
      debug: process.env.NODE_ENV === 'development',
      // 初始范围数据设置, 通常可用于设置应用、用户的初始数据
      initialScope: {
        tags: { tag: process.env.VUE_APP_DEPLOY_VERSION || 'v' + pkg.version }
        // userName: { id: 42, email: 'xxx@xxx.com' }
      },
      // ---性能追踪---
      // 错误异常采样率
      tracesSampleRate: 1.0,
      // 跟踪子组件,并查看有关渲染过程的更多详细信息
      trackComponents: true,
      // hook 默认为 ['mounted']
      hooks: ['created', 'mounted'],
      // 集成配置
      integrations: [
        // Vue Router 路由集成
        // 跟踪程序加载期间的性能
        new Integrations.BrowserTracing({
          routingInstrumentation: Sentry.vueRouterInstrumentation(router),
          tracingOrigins
        }),
        // 控制台日志捕获
        new CaptureConsoleIntegration(
          {
            // array of methods that should be captured
            // defaults to ['log', 'info', 'warn', 'error', 'debug', 'assert']
            levels: ['warn', 'error']
          }
        )
      ]
    })
  }
}

export default MateSentry

SDK 接入

import MateSentry from '@/sentry'

app.use(MateSentry, { router })

客户端用户数据携带

Sentry.setUser({ email: "john.doe@example.com" });

第三方SDK监控

<!-- script 标签添加 crossorigin="anonymous" 属性 -->

默认情况下(未指定crossOrigin), cors不会使用。 在非同源情况下, 设置 anonymous关键子将不会通过cookies , 客户端SSL证书或HTTP认证交换用户凭据。当需要获取用户凭据的manifest 时,即使是同源的情况下属性值必须设置为use-credentials

image.png

  • 基于上述的问题,有两种方案可以解决:
    • 把页面中所有的跨域资源放在跟页面同样的域下
    • 通过 Patch 原生方法来尽可能的捕获到错误, 比如我们可以Patch 原生的setTimeout:
    const prevSetTimeout = window.setTimeout;
      window.setTimeout = function(callback, timeout) {
          const self = this;
          return prevSetTimeout(function() {
              try {
                  callback.call(this);
              } catch (e) {
                  // 捕获到详细的错误,在这里处理日志上报等了逻辑
                  // ...
                  throw e;
              }
          }, timeout);
      } 
      // 但 Patch 原生方法有诸多的不确定性,建议视具体场景采用
    

主动上报

  • Sentry.captureException(err)

[Js中可以传入一个Error实例给 captureException() 以实现事件形式的主动捕获 —— 这种方式可能会抛出一个无法记录和回溯的错误字符串。](Usage for JavaScript)

// captureException
    try {
       aFunctionThatMightFail();
    } catch (err) {
        Sentry.captureException(err);
    }

  • Sentry.captureMessage('Something went wrong')

[另外一种常见的方式是捕获要发送给 Sentry 的纯文本信息——它们对某些团队是很有用的。](Usage for JavaScript)

  // captureMessage
    Sentry.captureMessage("Something went wrong");

SourceMap

// vue.config.js
const SentryPlugin = require("@sentry/webpack-plugin");
const pkg = require("./package.json");

const PUBLIC_PATH = '/xxx/xxx/'

module.exports = {
  productionSourceMap: true,  // 生产环境开启sourceMap
  configureWebpack: {
    devtool: 'source-map', // 开发环境开启
    plugins: [
      new SentryPlugin({
        // sentry-cli configuration
        authToken: process.env.SENTRY_AUTH_TOKEN,
        url: process.env.SENTRY_HOST,
        urlPrefix: `~${PUBLIC_PATH}`,
        org: process.env.VUE_APP_SENTRY_ORG,
        configFile: 'sentry.properties',
        project: pkg.name,
        release: `${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || 'v' + pkg.version}`,
        // webpack specific configuration
        include: "./dist",
        ignore: ["node_modules"],
        errorHandler (err){
         console.error('\n\n@sentry/webpack-plugin upload sourceMap error', err, '\n\n');
        },
      }),
    ],
  },
  // ... ...
};

CI Release 同步

⚠️ gitlab 需创建发布后才可同步sentry release

SPA单页应用集成

image.png

Gitlab Yaml准备

variables:
  DOCKER_DRIVER: "overlay"
  GROUP: "library"
  DC_REGISTRY_PROXY: "https://harbor-xx.xxx.cc"
  IMAGE_NAME: <YOUR-IMAGE-NAME>
  SPACE_NAME: '-xxx-xxx'
  TOKEN: ''
  DEPLOYMENT_ID: ''
  BUNDLE_SERVE: ''
  VUE_APP_SENTRY_PROJECT_ID_SIT: 10
  VUE_APP_SENTRY_PROJECT_ID_PRO: 2
  VUE_APP_SENTRY_DSN_SIT: http://<project-dsn-key>d@192.168.5.59:9000
  VUE_APP_SENTRY_DSN_PRO: https://<project-dsn-key>@sentry.xxxx-pro.net
  SENTRY_AUTH_TOKEN_SIT: <your-access-token-for-sit>
  SENTRY_AUTH_TOKEN_PRO: <your-access-token-for-pro>
  SENTRY_HOST_SIT: http://192.168.5.59:9000/
  SENTRY_HOST_PRO: https://sentry.xxxx-pro.net/


before_script:
  - export PROJECT_IMAGE_NAME=$(cat package.json | grep name | head -1 | awk -F "[\"]" '/name/{print$4}')
  - export VUE_APP_DEPLOY_VERSION=$CI_COMMIT_TAG
  - export VUE_APP_SENTRY_ORG=sentry;

Gitlab创建Release

⚠️ release.changelog.txt 为更新内容备注文件, 具体规范可同步项目经理版本更新要求

# 创建gitlab release 发布
ReleaseDeploy:
  stage: ReleaseDeploy
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  rules:
    - if: $CI_COMMIT_TAG                
  script:
    - echo "running release_job"
  release:
    name: $CI_COMMIT_TAG
    description: ./release.changelog.txt # 此次更新内容
    tag_name: '$CI_COMMIT_TAG'                               
    ref: '$CI_COMMIT_TAG'
  tags:
    - rancher_docker

同步release发布至sentry


# 同步release发布至sentry
SyncSentry:
  stage: SyncSentry
  only:
    - tags
  script:
    - echo $CI_COMMIT_REF_NAME
    - if [[ $(echo $CI_COMMIT_REF_NAME | grep "rc") != "" ]]; then
          export VUE_APP_SENTRY_TRACING_ORIGIN=https://activity-pre.9longe.net;
          export SENTRY_HOST=$SENTRY_HOST_PRO;
          export SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN_PRO;
          export VUE_APP_SENTRY_DSN=$VUE_APP_SENTRY_DSN_PRO;
          export VUE_APP_SENTRY_PROJECT_ID=$VUE_APP_SENTRY_PROJECT_ID_PRO;
          export VUE_APP_ENV="PRE"; 
        elif [[ $(echo $CI_COMMIT_REF_NAME | grep "master") != "" ]]; then
          export VUE_APP_SENTRY_TRACING_ORIGIN=https://activity.clktec.com;
          export SENTRY_HOST=$SENTRY_HOST_PRO;
          export SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN_PRO;
          export VUE_APP_SENTRY_DSN=$VUE_APP_SENTRY_DSN_PRO;
          export VUE_APP_SENTRY_PROJECT_ID=$VUE_APP_SENTRY_PROJECT_ID_PRO;
          export VUE_APP_ENV="PRO";
        else
          export VUE_APP_SENTRY_TRACING_ORIGIN=<你的服务地址>;
          export SENTRY_HOST=$SENTRY_HOST_SIT;
          export SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN_SIT;
          export VUE_APP_SENTRY_DSN=$VUE_APP_SENTRY_DSN_SIT;
          export VUE_APP_SENTRY_PROJECT_ID=$VUE_APP_SENTRY_PROJECT_ID_SIT;
          export VUE_APP_ENV=$CI_COMMIT_REF_NAME;
        fi;
    # SENTRY 发布同步
    - echo ''$SENTRY_HOST'api/0/organizations/'$VUE_APP_SENTRY_ORG'/releases/'$PROJECT_IMAGE_NAME'@'$VUE_APP_DEPLOY_VERSION'/deploys/' -H 'Authorization:Bearer '$SENTRY_AUTH_TOKEN'' -H 'Content-Type:application/json' -d '{"environment":"production"}'
    - curl ''$SENTRY_HOST'api/0/organizations/'$VUE_APP_SENTRY_ORG'/releases/'$PROJECT_IMAGE_NAME'@'$VUE_APP_DEPLOY_VERSION'/deploys/' -H 'Authorization:Bearer '$SENTRY_AUTH_TOKEN'' -H 'Content-Type:application/json' -d '{"environment":"production"}'
  tags:
    - rancher_docker

⚠️ 注意

  1. 环境变量注入策略需要根据项目自行制定
  2. Sentry 同步的版本$PROJECT_IMAGE_NAME@$VUE_APP_DEPLOY_VERSIONgitlab 创建的 releaseSourceMap 中传递 release 参数三者需保持一致

微前端应用集成

场景分析

  • sentry sdk在window 全局对象上注册属性
  • qiankun 加载子应用在沙箱中做脚本解析

子应用SentryVuePlugin

// Sentry 集成
import pkg from '../../package.json'
import * as OriginSentry from '@sentry/browser'
import {
  CaptureConsole as CaptureConsoleIntegration,
  Vue as VueIntegration
} from '@sentry/integrations'

// 获取主应用注入的SENTRY
const Sentry = window.$MainSentry || OriginSentry

const MateSentry = {
  install: (Vue) => {
    console.info(`[Sentry init release]${pkg.name}@${process.env.VUE_APP_DEPLOY_VERSION || pkg.version}`)

    const client = new Sentry.BrowserClient({
   
      beforeSend: (ev) => {
        if (window.__POWERED_BY_QIANKUN__ && ev.exception) {
          ev.exception.values = ev.exception.values.map(item => {
            const { stacktrace: { frames }, ...rest } = item
            // FIXME: 主应用加载时,qiankun 加载当前js资源会在首行添加 ;(function(window, self, globalThis){with(window){;
            // https://github.com/kuitos/import-html-entry/blob/master/src/index.js#L62
            frames[frames.length - 1].colno -= 51
            return {
              ...rest,
              stacktrace: {
                frames
              }
            }
          })
        }
        return ev
      },
    })

    const hub = new Sentry.Hub(client)

    // FIXME: 注册为主要实例才会生效,
    // Sentry会在下一版本改善这个问题
    // https://github.com/getsentry/sentry-javascript/issues/3271#issuecomment-792849150
    Sentry.makeMain(hub)

    Vue.prototype.$SentryHub = hub
  }
}

export {
  Sentry
}

export default MateSentry

主应用SentryVuePlugin

export function startQiankun () {
  registerMicroApps(apps, { // qiankun 生命周期钩子 - 微应用加载前
    beforeLoad: (app, globalRef) => {
      // FIXME: Sentry 注入
      globalRef.$MainSentry = Sentry
    },
    beforeUnmount: (app, globalRef) => {
      // FIXME: Sentry 删除
      globalRef.$MainSentry = null
    }
  })
}

异常监控服务

问题查看

image.png

版本追踪

image.png

image.png

Gitlab Issue关联创建

image.png

image.png

Issue 同步解决

查看

image.png

解决

此处 git-cz 集成到项目仓库中,已全局安装的, 项目中可不集成 git-cz

image.png

Sentry磁盘占用问题解决

Sentry服务运行一段时间后

磁盘会占满 86% +

image.png

进入数据库

docker exec -it 176e1501e871 /bin/bash

删除数据

vacuumdb -U postgres -d postgres -v -f --analyze

image.png

人工转自动

我们使用 crontab 在linux实现定时任务

crontab -e

在里面输入:

0 0 * * *  docker exec -it onpremise_web_1 sentry cleanup --days 7  && docker exec -it onpremise_postgres_1 vacuumdb -U postgres -d postgres -v -f --analyze复制代码

最后

以上内容为原创,转载请注明出处

你们的点赞👍是我的动力~

dianzan.png

微信公众号 “ 前端FeWeekly ” 每周分享 1篇+ 高质量技术文章,更有技术交流群等着你,扫码关注一下吧~

qr.jpg