getsentry / sentry-javascript-bundler-plugins

JavaScript Bundler Plugins for Sentry
https://sentry.io
BSD 3-Clause "New" or "Revised" License
136 stars 35 forks source link

Sentry webpack plugin does not set properly module metadata in module federation #448

Open DamlaDemir opened 11 months ago

DamlaDemir commented 11 months ago

Environment

"@sentry/webpack-plugin": "^2.10.1", "@sentry/react": "^7.77.0", "@nx/webpack": "17.0.3",

Problem

I have a react micro front-end project that I set up with Nx and module federation.

I have four different micro front-end projects: header, category, product, and footer. I want to track errors via Sentry. I am initiating the sentry in my header micro front-end project. I use sentryWebpackPlugin to understand which project the errors occur in. As shown below, I installed the sentryWebpack plugin separately for each project.

import { composePlugins, withNx } from '@nx/webpack';
import { withReact } from '@nx/react';
import { withModuleFederation } from '@nx/react/module-federation';
import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
import { merge } from 'webpack-merge';
import baseConfig from './module-federation.config';

const mfConfig = {
  ...baseConfig,
};

module.exports = composePlugins(
  withNx(),
  withReact(),
  async (config, { options, context }) => {
    const federatedModules = await withModuleFederation(mfConfig);

    const config = merge(federatedModules(config, { options, context }), {
      devtool: 'source-map',
      plugins: [
        sentryWebpackPlugin({
          _experiments: {
            moduleMetadata: ({ release }) => ({
              dsn: 'MY_DSN',
              release,
            }),
          },
        }),
      ],
    });

    return config;
  }
);

I include my header and footer micro projects in the host (shell) project. I imported my product project into my header project and category project into my product project as shown below.

host/app.tsx

const Header = React.lazy(() => import('header/Module'));
const Footer = React.lazy(() => import('footer/Module'));

export function App() {
  return (
    <React.Suspense fallback={null}>
      <Header />
      <Footer />
    </React.Suspense>
  );
}

header/app.tsx

const Product = React.lazy(() => import('product/Module'));

export function App() {
  return (
    <React.Suspense fallback={null}>
      <ChakraProvider>
         ...
        <Routes>
          <Route path="/product" element={<Product />} /> 
        </Routes>
      </ChakraProvider>
    </React.Suspense>
  );
}

product/app.tsx

const Category = React.lazy(() => import('category/Module'));

export function App() {
  return (
    <div>
      <Category />
      ...
    </div>
  );
}

For errors occurring in the product and category project, the sentry webpack plugin sets the DSN information of header DSN information. This causes errors to be sent to the wrong project.(Errors occurring in product and category projects are sent to the issue list of the header project.) How can I overcome this situation?

AbhiPrasad commented 11 months ago

Hey @DamlaDemir, what does your Sentry.init config look like? What does your beforeSend and transport look like?

DamlaDemir commented 11 months ago

Hi @AbhiPrasad, my sentry config like this:

import * as Sentry from '@sentry/react';

const EXTRA_KEY = 'ROUTE_TO';

const transport = Sentry.makeMultiplexedTransport(
  Sentry.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 [];
  }
);

export default {
  init: () => {
    Sentry.init({
      transport: transport,
      dsn: 'MY_DSN',
      integrations: [
        new Sentry.ModuleMetadata(),
      ],
      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);

          if (routeTo.length > 0) {
            const lastIndex = routeTo.length - 1;

            event.extra = {
              ...event.extra,
              [EXTRA_KEY]: [routeTo[lastIndex]],
            };
          }
        }

        return event;
      },
    });
  },
};
AbhiPrasad commented 11 months ago

You may need to use the top frame instead of last index.

We recently updated the docs to reflect this: https://docs.sentry.io/platforms/javascript/configuration/micro-frontend-support/#combining-modulemetadata-and-makemultiplexedtransport

See https://github.com/getsentry/sentry-docs/pull/8515

otherwise it's hard for me to tell what is going wrong here, everything else seems fine. Can you test if beforeSend is attaching the correct dsn?

DamlaDemir commented 11 months ago

I tried what you said, and it didn't solve the problem. The problem is not related to the top frame because when I throw an error from the product project and debug the before-send method, I see all frames with metadata have DSN information of the header. (expected product DSN)

So, the problem is related to the correct DSN information not being set in the module metadata when the error is detected.

As I showed above, I import and use the product micro project as a component in the header project. Is Sentry webpack plugin unable to find the correct DSN information because my product project is added to the bundle of my header project?

If I import all my micro projects into the host project as shown below, this problem does not occur and meta data is added to the frames with the correct DSN information for each project.

host/app.tsx

const Header = React.lazy(() => import('header/Module'));
const Footer = React.lazy(() => import('footer/Module'));
const Product = React.lazy(() => import('product/Module'));
const Category = React.lazy(() => import('category/Module'));

export function App() {
  return (
    <React.Suspense fallback={null}>
      <Header />
      <Category />
      <Product />
      <Footer />
    </React.Suspense>
  );
}
AbhiPrasad commented 11 months ago

As I showed above, I import and use the product micro project as a component in the header project. Is Sentry webpack plugin unable to find the correct DSN information because my product project is added to the bundle of my header project?

Ah yes this might be the issue. I think we rely on the host being aware of everything, so nesting like that might be causing a problem.

If you have any suggestions of how to fix this, please share! Otherwise I think we don't have the bandwidth to immediately fix this right now. Thanks!