微前端接入Sentry

Sentry 在微前端的支持

官方文档 # Micro Frontend Support

依赖

注意: 我们说一下功能点对版本有一定的要求, 如果版本低的话会不生效

  • @sentry/webpack-plugin@2.5.0
  • @sentry/react@7.59.0

安装依赖

js 复制代码
yarn add -D @sentry/webpack-plugin
yarn add @sentry/react

识别来源

要确定错误的来源,必须注入元数据,以帮助确定哪些捆绑包对错误负责。您可以通过启用 @sentry/webpack-plugin 的 _experiments.moduleMetadata 选项,使用任何 Sentry bundler 插件来实现这一点。

这样的话我们就可以在微前端的主项目和的子应用配置 moduleMetadata, 然后传入子应用的 dsn 动态区分确认的来源

一旦元数据被注入到模块中, moduleMetadata 集成就可以用来查找元数据,并将其附加到具有匹配文件名的堆栈帧中。然后,该元数据在其他集成中 比如 beforeSend 回调中可用,作为每个 StackFrame 上的 module_metadata 属性。这可用于识别哪些捆绑包可能对错误负责,并用于标记或路由事件。

例子:

js 复制代码
// webpack.config.js
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");

module.exports = {
  devtool: "source-map",
  plugins: [
    sentryWebpackPlugin({
      /* Other plugin config */
      // 我们在配置 @sentry/webpack-plugin 的时候 可以自定义一些 moduleMetadata 数据
      // 这些数据可以在 sentry.init下的 beforeSend 拿到
      _experiments: {
        moduleMetadata: ({ org, project, release }) => {
          return { team: 'frontend', release },
        }
      },
    }),
  ],
};
js 复制代码
import * as Sentry from "@sentry/browser";
import { ModuleMetadata } from "@sentry/core";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [ new ModuleMetadata() ]
  // 这里就可以拿到上面 @sentry/webpack-plugin 配置的 module_metadata 数据
  // 通过动态过滤就可以添加额外参数
  // 当然这样直接添加额外参数一般情况是不需要用到的 所以下面要提到自定义多虑传输 Transports
  beforeSend: (event) => {
    const frames = event?.exception?.values?.[0].stacktrace.frames || [];
    // Get all team names in the stack frames
    const teams = frames.filter(frame => frame.module_metadata && frame.module_metadata.team)
                        .map(frame => frame.module_metadata.team);
    // If there are teams, add them as extra data to the event
    if(teams.length > 0){
      event.extra = {
        ...event.extra,
        teams
      };
    }

    return event;
  },
});

Sentry.captureException(new Error("oh no!"));

但是我们在识别了不同子应用的报错后应该怎么准备的将错误上报到对应的子应用的 sentry 呢

这个时候我们就需要用到 sentry 为我们提供的自定义多路传输 Transports

例子:

ts 复制代码
import { captureException, init, makeFetchTransport } from "@sentry/browser";
import { makeMultiplexedTransport } from "@sentry/core";
import { Envelope, EnvelopeItemType } from "@sentry/types";

interface MatchParam {
  /** The envelope to be sent */
  envelope: Envelope;
  /**
   * A function that returns an event from the envelope if one exists. You can optionally pass an array of envelope item
   * types to filter by - only envelopes matching the given types will be returned.
   *
   * @param types Defaults to ['event'] (only error events will be returned)
   */
  getEvent(types?: EnvelopeItemType[]): Event | undefined;
}

function dsnFromFeature({ getEvent }: MatchParam) {
  const event = getEvent();
  switch (event?.tags?.feature) {
    case "cart":
      return [{ dsn: "__CART_DSN__", release: "cart@1.0.0" }];
    case "gallery":
      return [{ dsn: "__GALLERY_DSN__", release: "gallery@1.2.0" }];
    default:
  }
  return [];
}

init({
  dsn: "__FALLBACK_DSN__",
  // 我们可以通过 transport 自定义多虑上传, 将我们前面自定义的数据动态传递给其他sentry项目
  transport: makeMultiplexedTransport(makeFetchTransport, dsnFromFeature),
});

captureException(new Error("oh no!"), (scope) => {
  scope.setTag("feature", "cart");
  return scope;
});

微前端配置

那我们接下来看下成果吧

主应用:

js 复制代码
// webpack.config.js
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");

sentryWebpackPlugin({
  url: sourceMappingURL, // 上传 sourcemap 的cdn地址
  org: "组织名称",
  project: projectName, // 项目名称
  authToken: process.env.SENTRY_AUTH_TOKEN, // 用户的授权token
  // TODO: 主应用必须要有 不然子应用的 _experiments 不能触发拿到
  _experiments: {
    moduleMetadata: ({ org, project, release }) => {
      // 这里参数可以随便写
      return { team: projectName, release };
    },
  },
});
js 复制代码
const EXTRA_KEY = "ROUTE_TO"; // 这个可以写死 也就是在当前js使用 用于 beforeSend 和 transport 传递值

// 自定义多路上传
const transport = makeMultiplexedTransport(
  Sentry.makeFetchTransport,
  (args) => {
    const event = args.getEvent();
    if (
      event &&
      event.extra &&
      EXTRA_KEY in event.extra &&
      Array.isArray(event.extra[EXTRA_KEY])
    ) {
      // 这里返回的就是子应用配置的 dsn和release 用于将错误上传到子应用的sentry
      return event.extra[EXTRA_KEY];
    }
    return [];
  }
);

Sentry.init({
  dsn: "", // 填写 Client Keys DSN
  sampleRate: isDev ? 1 : 0.5,
  tracesSampleRate: 0.1,
  release: packageInfo.version || "last",
  integrations: [new ModuleMetadata()],
  transport, // 自定义上传策略
  beforeSend: (event: any) => {
    if (event?.exception?.values?.[0].stacktrace.frames) {
      const frames: any[] = event.exception.values[0].stacktrace.frames;
      // TODO 官方demo有误请使用如下方案
      // Find the last frame with module metadata containing a DSN
      let routeToList = frames
        .filter((frame) => frame.module_metadata && frame.module_metadata.dsn)
        .map((v) => v.module_metadata);

      if (routeToList.length) {
        event.extra = {
          ...event.extra,
          [EXTRA_KEY]: routeToList,
        };
      }
    }

    return event;
  },
});

注意:

  • 主应用初始化 @sentry/webpack-plugin 的时候一定要带上 _experiments.moduleMetadata, 不然就识别不到子应用的数据, 也就拿不到 routeToList 做多路上传了
  • 官方的 demo 针对 frame.module_metadata 的处理有误, 我已经提了 pr, 我这边已经改过了.

子应用

ts 复制代码
// webpack.config.js
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");

sentryWebpackPlugin({
  url: process.env.REACT_APP_SOURCE_MAPPING_URL, // 上传 sourcemap 的地址
  org: "xmly", // 组织名
  project: projectName, // package.json 的name
  authToken: process.env.SENTRY_AUTH_TOKEN, // 用户的授权token
  _experiments: {
    moduleMetadata: ({ org, project, release }) => {
      return {
        team: projectName,
        // dsn 和release是必填的 其他参数都无所谓
        dsn: 子应用的sentry dsn,
        release,
      };
    },
  },
});

子应用初始化 dsn 和 release 是必填的 其他参数都无所谓

总结

我们看下主应用的报错和子应用的报错 sentry 如何展示的

主应用是 SETTLEMENT-PLATFORM-ADMIN, 子应用是 BALANCE-SERVICE-MANAGE

主应用的sentry报错收集

子应用的sentry报错收集

我们看上面的截图可以看到, 两个不同的 sentry 项目, 报错的地址是一个项目, 至此, 我们实现了微前端项目中配置 sentry 的功能, 助力我们更加精确的排查前端项目报错

相关推荐
Domain-zhuo3 分钟前
JS对于数组去重都有哪些方法?
开发语言·前端·javascript
明月清风徐徐32 分钟前
Vue实训---2-路由搭建
前端·javascript·vue.js
王解40 分钟前
速度革命:esbuild如何改变前端构建游戏 (1)
前端·vite·esbuild
葡萄城技术团队1 小时前
使用 前端技术 创建 QR 码生成器 API1
前端
DN金猿1 小时前
Vue移动端网页(H5)预览pdf文件(pdfh5和vue-pdf)(很详细)
前端·vue.js·pdf
鸽鸽程序猿1 小时前
【前端】javaScript
开发语言·前端·javascript
秦时明月之君临天下1 小时前
React和Next.js的相关内容
前端·javascript·react.js
上官花雨2 小时前
什么是axios?怎么使用axios封装Ajax?
前端·ajax·okhttp
米奇妙妙wuu2 小时前
React中 setState 是同步的还是异步的?调和阶段 setState 干了什么?
前端·javascript·react.js
李刚大人2 小时前
react-amap海量点优化
前端·react.js·前端框架