使用 Sentry 做异常监控 - 如何优雅的解决 Qiankun 下 release 配置的问题

2,351 阅读6分钟

本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

前言

之前,小编写过一篇关于使用 Sentry 做异常监控的文章 - 使用 Sentry 做异常监控 - 如何优雅的解决 Qiankun 下 Sentry 异常上报无法自动区分项目的问题 ?。在这篇文章中,小编就 求教一下 主子应用的sentry应该如何实践 #1088 这个问题,提出了最终解决方案,并得到了不错的反响。

在这篇文章发表以后,陆续有小伙伴询问使用该方案后,Source Maps 自定义版本号的问题该怎么处理。说来惭愧,关于这个问题,小编一开始并没有怎么正视,只是提供了一种比较粗暴的解决方案 - 根据项目写死 release 配置。后来随着询问的小伙伴越来越多,小编觉得以前提供的解决方案不太好,就想着看能不能用一种更优雅的方式来解决问题。

经过一番思考和实践,小编 get 到了一种比较好的解决方案,然后通过本文展示给大家,希望能给到有同样困扰的小伙伴们一些启发。

本文的目录结构如下:

Sentry 上传 Source Maps

在使用 Sentry 做异常监控时,为了能更准确的定位异常在源文件中发生的行列位置,我们通常需要将项目打包时生成 Source Maps 文件上传到 Sentry

在上传 Source Maps 文件时,Sentry 提供了两种方式: webpack pluginsentry cli

  • webpack plugin

    webpack plugin 的使用方式非常简单,我们只需要安装一个 @sentry/webpack-plugin 插件,然后在项目的 webpack 配置中使用这个插件就好了。

    具体如下:

    // 安装插件
    yarn add @sentry/webpack-plugin --dev
    
    npm install @sentry/webpack-plugin --save-dev
    
    // 使用插件
    const SentryCliPlugin = require('@sentry/webpack-plugin');
    
    const config = {
      plugins: [
        new SentryCliPlugin({
          include: path.resolve(__dirname, './build/static/js'),
          ignore: ['node_modules', 'webpack.config.js'],
          release: 'xxx',
          project: 'xxx',
          urlPrefix: 'xxxx',
          url: 'xxx',
          ...
        }),
      ],
    };
    

    更多情况详见: 官网:webpack plugin

  • sentry cli

    sentry cli 的方式也非常简单, 只要在需要上传的时候,执行以下命令即可:

    npx sentry-cli releases files <release_name> upload-sourcemaps xxx --url-prefix=xxx
    

    更多情况详见: 官网: sentry cli

有了这两种方法,我们就在可以上传 Source Maps 文件时,根据项目情况做出灵活选择。如果我们的项目是使用 Webpack 工具打包,那么我们可以使用 webpack plugin 的方式去上传 sourcemap 文件;而如果项目是使用非 Webpack 工具打包,比如说 ParcelVite,那我们就可以采用 sentry cli 的方式去上传 Source Maps 文件。

在上传 Source Maps 文件的过程中,有一个点要特别注意,那就是上传时的 release 必须和 Sentry.init 方法执行时传入的的 release 配置保持一致,否则上传的 Source Maps 文件将无法起作用。

如果我们的应用是一个简单的 spa 架构,那么这一点很容易做到。我们只需要将 Sentry.init 中的 release 配置项和 webpack pluginrelease 配置项(或者是 sentry clirelease_name) 保持一致就可以了。

但如果我们的应用是一个复杂的微前端架构,比如使用 Qiankun,那要做到这一点就比较麻烦。在 使用 Sentry 做异常监控 - 如何优雅的解决 Qiankun 下 Sentry 异常上报无法自动区分项目的问题 ? 中,我们将 Sentry.init 动作放在主应用中,而上传 Source Maps 的动作放在了子应用中, 那怎样能保证这两者的 release 配置项保持一致呢?

这个问题,其实也好解决。接下来,小编就通过下面这个章节给大家梳理一下。

解决方案

其实,Sentry.init 方法执行时传入的 release 参数,最终是在通过接口上报异常时作为入参来使用的。因此我们可以在主应用对异常上报做拦截时,根据异常对应的追踪栈信息找到异常属于哪个子应用,然后重写这次异常上报的 release 参数。

为了能保证异常上报时的 releaseSource Maps 文件上传的 release 保持一致,小编在这里提供了两种方案:固定版本号和自定义版本号。

方案一: 固定版本号

固定版本号的方案非常简单,即写死 release 配置项即可。通常我们可以使用项目在 Sentry 中的 Project ID 作为 release

举个 🌰,我们的某个子应用对应的 Project IDby-aicc, 那我们可以在子应用上传 Source Maps 文件时,将 release 设置为 by-aicc。当异常上报被拦截,确认异常属于项目 by-aicc 时,我们可以将异常上报接口中的 release 参数也设置为 by-aicc。这样,我们就可以保证两者的 release 一致。

不过这种方式有点简单粗暴。通常,我们会根据项目发布的日期、版本等信息来动态配置 release,这样可以帮助我们快速定位问题。如果简单的设置为项目 ID,相反会给我们排查定位问题带来负担,有点聪明反被聪明误了。

方案二: 自定义版本号

既然固定版本号的方案不可取,那我们就来尝试一下自定义版本号的方案。

自定义版本号方案的难点在于:主应用如何获取到子应用当前的自定义 release。只要解决了这个痛点问题,那么对应的方案也就可行了。

关于这一点,Qiankunhtml entry 给了小编灵感。

Qiankun 在首次加载子应用时,会根据路由注册表中的 entry 配置项,去读取子应用的入口文件 - index.html,然后解析入口文件,拿到子应用的入口 js 文件,然后通过 sandbox 的方式去执行 js 文件,拿到子应用的生命周期方法。

既然这样,那我们可以将子应用的自定义 release 以内联 script 的方式添加到 index.html 中,等到 Qiankun 在处理子应用的 index.html 文件时,执行这段 script 脚本,把自定义 release 添加到 window 中。等接口被拦截需要重新设置 release 时,直接从 window 中读取就可以了。

举个 🌰,我们可以通过 html-webpack-plugin,在 index.html 中动态添加自定义 release,如下:

const HtmlWebpackPlugin = require('html-webpack-plugin');

const config = {
    plugins: [
        new HtmlWebpackPlugin({
            templateContent: `
              <html>
                <body>
                  <h1>Hello World</h1>
                  <script type="text/javascript">
                      window["by-aicc-release"] = 'xxxxx'
                  </script>
                </body>
              </html>
            `
          })
      ],
    };

其他类型的构建工具,也提供了自定义 index.html 的方式。实在不行,我们写个 gulp 任务,自动给 index.html 中添加 script 脚本。

当主应用拦截异常上报,并确定这个异常属于 by-aicc 时,可以通过 window["by-aicc-release"] 的方式去获取自定义 release,然后重新设置接口的 release 的参数,保证两者的 release 一致。

这样,整个问题就完美解决了,👍🏻。

结束语

到这里,关于如何解决 Qiankunsoucemap 自定义版本号问题的梳理就结束了。

简单总结一下,为了解决这个问题,小编提出了固定版本号和自定义版本号两种解决方案。其中,固定版本号方案比较简单粗暴,不是很推荐使用;自定义版本号方案相比更优秀,适用于实际项目使用。

如果有小伙伴们也遇到了同样的问题,不妨试试自定义版本号方案吧,😄。