Open johnmbauan opened 5 months ago
Hi, unfortunately it is indeed not that simple because the browser lacks AsyncLocalStorage
(or basically any functionality to store data for an async execution context) which is what we would use to avoid leakage between scopes.
You mentioned that you are not using module federation. Module federation is not a requirement to use our Micro Frontend guide: https://docs.sentry.io/platforms/javascript/best-practices/micro-frontends/ As long as you have separate builds for each of your micro frontends you can use the solution we propose there.
Thanks @lforst . Based on my understanding, the solution proposed in https://docs.sentry.io/platforms/javascript/best-practices/micro-frontends/ would require some centralization of the initialization of Sentry, which we'd like to avoid. Is my understanding correct?
In all cases Sentry.init() must never be called more than once, doing so will result in undefined behavior.
So in order to catch global errors (ie. errors that bubble up to window.onerror
) you need some kind of centralization, since you would need to determine which client/DSN should capture the error.
As it stands, I don't think there is a way around having at least one global (fallback) client that does routing based on some heuristic. Lmk if you have any suggestions.
As it stands, I don't think there is a way around having at least one global (fallback) client that does routing based on some heuristic.
Gotcha, that makes sense.
How do things work with in the context of multiple clients initialized through these steps ? Which is the sentry instance that would catch global errors? š¤
Anyways, I think in the context of multiple remote MFEs in a host MFE (i'm using module federation terms here), each remote MFE would really be interested in catching errors originating form "itself" (i.e. its root Vue/React/Angular "application" instance); any other error should be caught by the host MFE. In this context, all the remote MFEs would initialize the sentry instance using an isolated scope, while the host MFE would initialize sentry in the standard way (and would act as the default error handler). Would that be possible? This would allow for fully independent sentry initialization, avoiding for centralization that would need to be repeated in each host MFE.
Ideally, you have one client that uses the default integrations via getDefaultIntegrations()
. This client then catches all global errors (ie errors from handlers, window.onerror, setTimeout etc) and routes them to the right DSN with the process described here.
Then, additionally, you can have clients that can be used to manually capture errors via client.captureException(error)
. You need to lose the idea of "scope". Scope is not possible in the browser due to the lack of an AsyncLocalStorage
API. Without such an API, errors will leak all over the place and between your projects when you have async stuff happening (which is basically all the time in a real-world scenario).
The only thing you should absolutely not do is call Sentry.init()
more than once, or set up multiple clients with the default integrations.
In my descriptions, I will intentionally not mention anything about module federation or any other type of frameworks, since in my experience everybody does MFEs differently and finding specific solutions is pointless.
This issue has gone three weeks without activity. In another week, I will close it.
But! If you comment or otherwise update it, I will reset the clock, and if you remove the label Waiting for: Community
, I will leave it alone ... forever!
"A weed is but an unloved flower." ā Ella Wheeler Wilcox š„
Problem Statement
I'm working in a context where we have several micro-frontends, and we want these micro-frontends to be completely independent in terms of how sentry is initialized and keep the different instances running in any given page isolated. We're not setup for Module Federation so we cannot follow what is suggested here.
I've followed what's described in this page: created a scope, created a
BrowserClient
and usescope.captureException
.Manually capturing exceptions with
isolatedScope.captureException
works well, as the events are sent to the correct Sentry project. However, if an uncaught exception is thrown somewhere in the Vue app, nothing happens. I've investigated the codebase and found that theerrorHandler
defined in the Vue integration doesn't account for any isolated scope created outside of the vue integrationinit
function. This means that any exceptions that are not explicitly handled withisolatedScope.captureException
will be handled using whatever is the current scope.Solution Brainstorm
I propose making the option of the
init
function accept an additional"useIsolatedScope": boolean;
field, based on which the error handler that is injected into the Vue app will use a new isolatedscope
to capture exceptions. I'm sure it's not that simple, but the high-level idea is to simplify the setup of an isolated sentry instance with Vue.(I have a working solution that somewhat resembles what I'm proposing, though I went lower level by using a modified version of
attachErrorHandler
directly on the Vue app and passing the scope to it)