Sentry is awesome, but their NodeJS Platform is slightly less great. It's basically entirely synchronous, so if you have a lot of async operations going on things like breadcrumbs and other context information will all get mixed up together.
I put this gist together to share with other people how I worked around this problem in our code base. It's not a perfect solution, but it works pretty well.
How it works
The way this works is that Sentry has a global store (
global.__SENTRY__) that includes a
hub property that stores the current hub. The hub has a stack of scopes that are the things you interact with when using things like
Sentry.configureScope. What I'm doing here is replacing that
hub property with a getter that return an async context local
hub instead of a global one. It does this by using the Node native AsyncLocalStorage module, which creates stores that stay coherent through asynchronous operations.
withSentryHub function creates a brand new
hub instance and then runs the wrapped code with that new hub provided as the "global" hub. Since we patched Sentry's global store to return the appropriate hub instance for each thread, any code that gets run in the "tree" of code spawned by the
withSentryHub call will get that specific hub instance returned, without affecting any other hubs that any other async contexts might be using.
What this means is that if use
withSentryHub to wrap a "transaction" function (such as the express middleware included at the bottom) then any exceptions captured within the scope of that request should only have breadcrumbs and spans related to that actual request. As bonus, this also makes it easy to have different hubs connected to different clients if you need that sort of thing.