微前端

了解如何在使用微前端或模块联邦时识别错误来源并将事件路由到不同的 Sentry 项目。

如果您的应用程序使用微前端,能够跟踪错误来自哪个微前端将非常有用。通过 Sentry,您可以创建自动或手动设置,将事件发送到代表每个微前端的单独 Sentry 项目。这使得更容易看到问题所在并快速定位和修复问题,特别是在复杂的前端架构中。

以下提供了将错误路由到不同 Sentry 项目的自动和手动设置说明。

在所有情况下,Sentry.init() 绝不能被调用多次,否则会导致未定义行为。

ModuleMetadatamakeMultiplexedTransport 可以一起使用,自动将事件路由到代表您微前端服务的特定 Sentry 项目。一旦确定了发生错误的服务,事件将被路由到正确的项目中,确保错误在正确的项目中被跟踪。

  • 需要 @sentry/webpack-plugin@sentry/rollup-plugin@sentry/vite-plugin@sentry/esbuild-plugin 版本 2.18.0 或更高。
  • 需要 SDK 版本 7.59.0 或更高。

要识别错误来源,您必须首先注入有助于识别哪个包负责该错误的元数据。您可以使用任何 Sentry 打包插件通过启用 moduleMetadata 选项来实现这一点。以下示例是针对 Webpack 的,但在 Vite、Rollup 和 esbuild 中也支持。

在您的微前端中安装以下代码片段:

Copied
// webpack.config.js
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");

module.exports = {
  devtool: "source-map",
  plugins: [
    sentryWebpackPlugin({
      moduleMetadata: ({ release }) => ({
        dsn: "__MODULE_DSN__",
        release,
      }),
    }),
  ],
};

一旦元数据被注入到模块中,moduleMetadataIntegration 可以用于查找该元数据,并将其附加到具有匹配文件名的堆栈帧上。这些元数据随后在 beforeSend 回调中作为每个 StackFramemodule_metadata 属性可用。这可以用于识别哪些包可能对错误负责。一旦确定了目标,您可以将目标存储为 DSN-release 对列表在 event.extra[MULTIPLEXED_TRANSPORT_EXTRA_KEY] 中,以便多路传输引用进行路由。

在您的主机中安装以下代码片段:

Copied
import {
  init,
  makeFetchTransport,
  moduleMetadataIntegration,
  makeMultiplexedTransport,
} from "@sentry/browser";

const EXTRA_KEY = "ROUTE_TO";

const transport = makeMultiplexedTransport(makeFetchTransport, (args) => {
  const event = args.getEvent();
  if (
    event &&
    event.extra &&
    EXTRA_KEY in event.extra &&
    Array.isArray(event.extra[EXTRA_KEY])
  ) {
    return event.extra[EXTRA_KEY];
  }
  return [];
});

init({
  dsn: "__DEFAULT_DSN__",
  integrations: [moduleMetadataIntegration()],
  transport,
  beforeSend: (event) => {
    if (event?.exception?.values?.[0].stacktrace.frames) {
      const frames = event.exception.values[0].stacktrace.frames;
      // Find the last frame with module metadata containing a DSN
      const routeTo = frames
        .filter(
          (frame) => frame.module_metadata && frame.module_metadata.dsn,
        )
        .map((v) => v.module_metadata)
        .slice(-1); // using top frame only - you may want to customize this according to your needs

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

    return event;
  },
});

一旦设置完成,所有错误(无论是已处理的还是未处理的)将自动路由到正确的项目。

默认情况下,args.getEvent 仅返回错误事件。您可以匹配其他类型的事件,例如:args.getEvent(['event', 'transaction', 'replay_event'])。此 getEvent 代码片段将返回错误、事务和重播的匹配项。

如果您希望有更多的控制权,能够明确指定每个 captureException 的目标,可以使用多路传输提供的更高级接口。

需要 SDK 版本 7.59.0 或更高。

以下示例使用 feature 标签来确定将事件发送到哪个 Sentry 项目。如果事件没有 feature 标签,则将其发送到 Sentry.init 中定义的备用 DSN。

Copied
import {
  captureException,
  init,
  makeFetchTransport,
  makeMultiplexedTransport,
} from "@sentry/browser";

init({
  dsn: "__FALLBACK_DSN__",
  transport: makeMultiplexedTransport(makeFetchTransport, ({ getEvent }) => {
    const event = getEvent();

    // Send to different DSNs, based on the event payload
    if (event?.tags?.feature === "cart") {
      return [{ dsn: "__CART_DSN__", release: "cart@1.0.0" }];
    } else if (event?.tags?.feature === "gallery") {
      return [{ dsn: "__GALLERY_DSN__", release: "gallery@1.2.0" }];
    } else {
      return [];
    }
  }),
});

然后,您可以在各个微前端中为事件设置标签/上下文,以决定将事件发送到哪个 Sentry 项目,如下所示:

设置标签时始终使用本地作用域非常重要(如下所示或使用

withScope 文档

)。如果使用全局作用域(例如通过 Sentry.setTag()),将会导致所有后续事件无论来源都被路由到同一个 DSN。

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

从版本 8.7.0 开始,如果同一页面上有多个 Sentry JavaScript SDK,它们只有在使用相同版本时才会相互交互。这防止了不必要的跨 SDK 交互,避免因调用或访问最近添加或已不存在的 SDK API 而导致错误。一个典型的例子是浏览器扩展或第三方脚本在主机应用程序也使用 Sentry 时使用 Sentry。

然而,在微前端等使用场景中,如果您希望多个微前端或子应用程序之间的 SDK 进行交互,则需要确保所有 SDK 使用相同的版本。例如,如果您在主机或骨架应用程序中初始化 SDK,但在微前端子应用程序中进行 Sentry SDK 调用(如 Sentry.captureExceptionSentry.setTag),则需要确保主机和子应用程序中的 SDK 包版本一致。

如果您无法使所有微前端对齐到相同的 SDK 版本,可以参考此解决方法。 但请注意,互操作性无法保证,您可能会遇到一些意外行为。