sentry-javascript
sentry-javascript copied to clipboard
Issues are not being logged in the correct project, micro-frontends
Is there an existing issue for this?
- [X] I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- [X] I have reviewed the documentation https://docs.sentry.io/
- [X] I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/browser
SDK Version
7.101.0
Framework Version
Preact: 10.19.3
Link to Sentry event
https://ingka.sentry.io/issues/4976674051/?environment=test&environment=development&project=6195091&project=6192997&query=is:unresolved+http.url:https://www.cte.ikeadt.com/*&statsPeriod=1h&stream_index=2&utc=true
SDK Setup
This is how the initSentryClient is implemented, is a library that we use in different micro-frontends
import * as Sentry from '@sentry/browser';
import { ClientOptions, Event } from '@sentry/types';
export function instanceHolder<T>() {
let instance: T;
return {
get: (): T => instance,
set: (value: T) => {
instance = value;
},
};
}
/**
* @description Initialize Sentry Client with the provided options
* @param {Partial<ClientOptions>} options
* @returns void
* @example initSentryClient({dsn: 'https://', release: '1.0.0'}) // Initialize Sentry with DSN ad release tag
*/
export const initSentryClient = (options: Partial<ClientOptions>) => {
const client = new Sentry.BrowserClient({
...options,
transport: Sentry.makeFetchTransport,
stackParser: Sentry.defaultStackParser,
integrations: [
...Sentry.getDefaultIntegrations(options),
Sentry.moduleMetadataIntegration(),
],
beforeSend: (event: Event) => {
const frames =
event?.exception?.values?.[0]?.stacktrace?.frames || [];
const teams = frames
.filter(
(frame) =>
frame.module_metadata && frame.module_metadata.team,
)
.map((frame) => frame.module_metadata.team);
if (teams.length > 0) {
event.extra = {
...event.extra,
teams,
};
}
return event;
},
});
Sentry.withIsolationScope(() => {
Sentry.setCurrentClient(client);
client.init();
});
};
And this is how it's implemented on each mifro-frontend
import { h, render } from 'preact';
import App from './view/app';
import { initSentryClient } from '@team/lib';
window.renderApp = async (containerId, data) => {
try {
// getting config...
} catch (error) {
// handle error...
} finally {
initSentryClient({
dsn: config.sentryDsn,
environment: config.environment,
release: `{micro-frontend-name}@${packageInfo.version}`,
debug: config.environment !== 'production',
});
render(
<App config={config} data={data} />,
document.getElementById(containerId),
);
}
};
window.renderApp('container', {});
And this is how the Webpack plugin is configured
const { sentryWebpackPlugin } = require('@sentry/webpack-plugin');
const PACKAGE = require('./package.json');
module.exports = {
entry: './src/index.jsx',
devtool: 'source-map',
// other options
plugins: [
// other plugins
sentryWebpackPlugin({
org: 'org',
project: '{micro-frontend}',
authToken: process.env.SENTRY_AUTH_TOKEN,
release: {
name: `{micro-frontend}@${PACKAGE.version}`,
},
_experiments: {
moduleMetadata: ({ org, project, release }) => ({
team: 'team-name',
release,
org,
project,
}),
},
}),
],
};
Steps to Reproduce
As this is not in production, I can't share the development URL here; even if I can, it's protected behind SSO.
On the page where the micro-frontends were rendering, I blocked two API URLs on the Chrome dev tools, one for each micro-frontend; this will trigger Sentry.captureException().
Both micro-frontends are distinct projects on sentry, which means both have their own Sentry ID and Sentry Key.
There are two Sentry calls on the Chrome Dev Tools screenshot; they should be one for each and called separately on the respective micro-frontend.
For some reason, it always calls with the same ID, sometimes from the Application A ID and sometimes with the Application B ID, but never with both, separately.
Expected Result
The expected result should be one call for each DSN, resulting in the issue being sent to the respective project (green lines)
Actual Result
Because of this duplicated call, issues from Application A end on Project B, and vice-versa. (red lines)
┆Issue is synchronized with this Jira Improvement by Unito
Hi Vinicius, I took a quick look and I see two potential issues immediately:
- You need to chain
.slice(-1);after.filter()and.map()in yourbeforeSend. That was a bug in example code in the docs which we fixed in one spot but not another. - Not sure if we support this way of initializing. I could be wrong but haven't seen it done this way before. What is your intention with having multiple clients? Is there a reason not to just call
Sentry.init()directly? The solution described in the final section of our docs relies on multiplexed transport and should not require multiple clients:
const client = new Sentry.BrowserClient({
.
.
.
Sentry.withIsolationScope(() => {
Sentry.setCurrentClient(client);
client.init();
});
Please let me know if this doesn't help and I can look deeper.
Hey @realkosty
For the first one, it makes sense; I was sent ~8 times to the same team; thanks for that.
Giving more context on the second: my team have standalone micro-frontend applications, and we don't control the page that they are being used, so we can't rely on the page host to have a single Init, that's why we add an init for each application.
@realkosty I remember telling @vicainelli to use multiple clients. Moving forward (also thinking about future SDK versions), having multiple clients in MFRE situations is definitely beneficial. My mental model is that each team likely wants to have their own client. The reason also being manual instrumentation, where you want to manually want to send events through your team's client instead of potentially leaking it to other teams via ambiently defined clients.
@vicainelli has confirmed that this issue has been solved