Identifying The Source Of Errors To Microfrontends When Using Sentry

Damla Demir
Trendyol Tech
Published in
9 min readJul 9, 2024
Image from https://www.21kschool.com

As the Seller Growth team, we work on projects that enable sellers to grow on Trendyol. In the fast-paced world of e-commerce, ensuring that sellers can smoothly manage their transactions is crucial. One of the key tools in achieving this is error monitoring.

We use Sentry in the seller center panel to effectively track errors. Sentry is an error-tracking and performance-monitoring tool for applications. It helps developers quickly detect, diagnose, and fix errors and performance issues by providing detailed insights.

As the Seller Growth team, we are working on projects using a micro front-end architecture. While integrating Sentry for error tracking, we encountered challenges identifying the exact source of errors within this architecture. Sentry was unable to determine which specific project the errors were originating from. In this article, I will explain how we resolved this issue.

Before reading this article, I recommend you read the below article, which contains basic information such as what Sentry is and how to use it.

Micro Front End Architecture

Before diving into the topic, I would like to describe our micro front-end architecture briefly.

Micro front-end is an architectural style where a web application consists of small, independently deliverable front-end modules. Each module is responsible for a specific part of the user interface and can be developed, tested, and deployed separately by different teams.

At Trendyol, the seller panel involves numerous applications and development teams, making the micro front-end architecture an essential approach for us.

We use a custom micro front-end architecture that we have written ourselves with node.js, which allows us to render more than one independent application (root and child applications) on the same page in the Seller Center panels. We use Sentry to track errors in these applications.

Seller Center Panel Micro Front End Architecture

In the image above, you can see the architecture we use in the Seller Center panel. The header, banner, and child applications (main content) are managed by a root application(that is written by node.js), which coordinates various child applications to render the appropriate content.

In such an architecture, we encountered an issue when attempting to use Sentry!

The Sentry may not send the errors to the correct project issue list if your front end includes JavaScript bundles from multiple sources with different release cycles.

Problem Definition

We faced challenges using Sentry effectively in our micro front-end projects. When we set up Sentry in each project loaded on the same page, errors from any project were often sent to the issue list of the first loaded project. For example, even if Sentry was set up in the banner and header projects, errors occurring in these projects were reported to the issue list of the child micro app. This issue arose because Sentry allows only a single SDK initialization per page.

As a result, in our setup, where the first loaded projects were child applications, errors from other projects were mistakenly sent to the issue lists of the child applications, creating false issues and alarms.

Some problems caused by this situation can be listed as follows:

  • A long issue list containing errors in other projects unrelated to the child project in question
  • False alarms occur because it is not possible to detect which project the error came from
  • Projects other than the child application (root project in our case) cannot track errors accurately because they do not have their issue lists where errors are kept.
An Example Issue Sent to the Wrong Issue List

For example, in the image above, the error ‘Error: this is header prod micro-fe test error!’ originates from the header project. However, it is being sent to the issue list of the seller-center-growth project.

How can we resolve this issue? How can we ensure that errors from each project are sent to their respective issue lists?

Solution

Our basic approach here includes these steps:

  • Initializing a single sentry for the relevant page in our micro app architecture,
  • Determining which project Sentry will be set up on,
  • Being able to determine which project the captured errors belong to
  • Establishing the necessary filtering mechanism to send the captured errors to the issue list of the correct application and managing this centrally from a single root project.
Solution Method

The most critical point to consider here is the project in which the sentry initialization is performed. The project where Sentry is set up should be the first project loaded on the relevant page. Otherwise, if other projects are loaded before Sentry is initialized, errors that occur during the loading of those projects will not be caught by Sentry.

In our case, Since sentry configurations may differ for each child project, the best way was to init the Sentry in the child project that creates the page’s content instead of common root projects.

Code Implementation

Now, let’s demonstrate the solution we discussed in theory with some code. In our example, we have three different micro front-end applications: Header, Banner, and Seller-Growth (child), all rendered on the same page.

Since the Growth project is the first to load in our case, we’ll prepare a service in this project to initialize Sentry, as shown in the code below.

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

export default class SentryService {
private readonly sentryInitOptions;

constructor(sentryInitOptions: SentryInitOptions) {
this.sentryInitOptions = sentryInitOptions;
}

public init(options) {
Sentry.init({
...options,
...this.sentryInitOptions,
});
}
}

Next, let’s initialize Sentry.

import { SentryService } from '@trendyol/sc-utils';

const sentryService = new SentryService({
dsn: 'fallback dsn',
release: 'sample release',
environment: 'sample environment',
});

export const initSentry = (app: any) => {
sentryService.init();
};

export default sentryService;

With the implementation above, we’ve completed the first two steps of our solution. Now, errors from the Header, Banner, and Growth projects are collected by Sentry. However, all errors are still being sent to the same DSN, so we can’t yet differentiate which project the errors originate from.

Sentry DSN (Data Source Name) is a unique URL that allows your application to send error and performance data to your Sentry project. It ensures the data is routed to the correct project within Sentry.

Now, we need to identify which project each captured error belongs to. To achieve this, we’ll add a plugin to each of our projects. The specific plugin we use will depend on the module bundler in use. In our case, we use Webpack for the Header and Banner projects, and Vite for the Growth project.

const { sentryWebpackPlugin } = require('@sentry/webpack-plugin');

const opt = {
configureWebpack: {
plugins: [
sentryWebpackPlugin({
_experiments: {
moduleMetadata: ({ release }) => ({
dsn: 'banner dsn',
}),
},
}),
],
},
};

module.exports = opt;

Above, we see how to add this plugin to the Webpack configuration of the Banner project. This plugin allows us to add the DSN information to the module metadata when an error is captured. By including the DSN information on the error, we will perform the necessary filtering and routing based on this data.

We need to apply this configuration to both the Header and Growth projects. Here is an example of a Vite configuration:

import { defineConfig } from 'vite';
import { sentryVitePlugin } from '@sentry/vite-plugin';


export default defineConfig(({ mode }) => ({
plugins: [
sentryVitePlugin({
_experiments: {
moduleMetadata: () => ({
dsn: 'growth dsn',
}),
},
}),
],
}));

By adding the DSN information to each error, we can now identify which project the error belongs to. The final step is to ensure that errors are sent to the correct issue lists based on this DSN information. To achieve this, we will modify the initial Sentry configuration slightly.

import * as Sentry from '@sentry/vue';
import merge from 'ts-deepmerge';

const EXTRA_KEY = 'ROUTE_TO';

export default class SentryService {
private readonly sentryInitOptions;

private 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 [];
});

public readonly defaultSentryOptions = {
transport: this.transport,
integrations: [new Sentry.ModuleMetadata()],
beforeSend: (event) => {
if (event?.exception?.values?.[0].stacktrace?.frames) {
const frames = event.exception.values[0].stacktrace.frames;


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;
},
};

constructor(sentryInitOptions: SentryInitOptions) {
this.sentryInitOptions = sentryInitOptions;
}

private mergeSentryOptions(options){
const sentryOptions = merge(this.defaultSentryOptions, options);

return sentryOptions;
}

public init(app, options): void {
const apps = window && window.Vue ? [app, window.Vue] : [app];
const sentryOptions = this.mergeSentryOptions(options);

Sentry.init({
...sentryOptions,
...this.sentryInitOptions,
app: apps,
});

}
}

First, we integrate Sentry.ModuleMetadata() to use the DSN information added by the plugin. With the beforeSend method, we add the DSN information from the metadata to event.extra before sending the error to Sentry.

A frame in the context of an exception contains information about the steps and locations in the code where the event occurred. It tracks which functions were called and what operations were performed up to the point where the error happened. We capture the last frame because it shows the exact location where the error was triggered.

Finally, we use the makeMultiplexedTransport feature to customize the SDK. This allows us to read the DSN information from event.extra and send the error to the correct DSN. If no DSN is found on the error, it defaults to the fallback DSN provided in the initial configuration.

Sentry.makeFetchTransport is a method in the Sentry JavaScript SDK that creates a Fetch API-based transport provided by Sentry. The makeFetchTransport method generates a transport that uses the Fetch API to send error reports to the Sentry server.

Thus, we have completed our code implementation. Below is an example showing an error before and after micro front-end support.

Before micro frontend support, an error occurring in the root project would appear in the child project’s issue list, as shown below:

An Example Issue Sent to the Wrong Issue List

After micro front-end support, an error occurring in the root project will show up in its own issue list, as shown below:

Benefits

With our micro front-end solution, we have achieved the following benefits:

Effective Real-Time Error Monitoring: There is the possibility of effective error monitoring as errors occurring in each project are collected in their issue list, resulting in errors being accurately captured and reported as soon as they occur.

Preventing False Alarms: By sending the errors to the correct projects, false alarms are completely prevented.

Ability to Take Action Faster: It can quickly identify and respond to errors grouped correctly according to projects, allowing you to take action, reduce downtime, and improve user experience.

Accurate Reporting: As a result of the correct grouping of errors, the reports that Sentry creates based on the data it collects, including environment variables, user context, and more, are displayed correctly.

For practical experience with the concepts we’ve discussed, you can explore the following repository that integrates Sentry with an example project using NX and module federation:

In this article, we covered our approach and solution to the difficulties and problems we encountered when using Sentry in micro front-end architecture. Thank you for reading! I hope you enjoyed learning about our approach and found it exciting and helpful.

If you want to be part of a team that tries new technologies and want to experience a new challenge every day, come to us.

--

--