question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[@sentry/tracing] Nested express custom middlewares detection

See original GitHub issue

Package + Version

@sentry/tracing 5.29.2

Description

At first I had the problem described in #2968 but I saw #2972 and added the methods I wanted to log:

new Tracing.Integrations.Express({ app, methods: ['get', 'post'] }),

This works great, except that it doesn’t seem to handle nested middlewares. I checked the code source and indeed it seems the wrapping only applies to the app or router parameter. https://github.com/getsentry/sentry-javascript/blob/0ddd74f03035c9b4cbbaf8cdd5e7e77fbd7fda46/packages/tracing/src/integrations/express.ts#L201

That means considering a code like this:

// index.ts
const app = express();

Sentry.init({
  dsn: "...",
  integrations: [
    new Tracing.Integrations.Express({ app, methods: ["get"] }),
  ],
  tracesSampleRate: 1.0,
});

app.get("/test", someHandler); // first endpoint

app.use("/api", require("./api")); // nested router


// api.ts
const router = express.Router();

router.get("/users", someHandler); // second endpoint

export default router;
  • If we call /test we’ll indeed get middleware.get corresponding to our first endpoint in the list of spans.
  • If we call /api/users we’ll only get middleware.use router corresponding to the nested router, but the second endpoint will not appear at all in the list of spans.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:7
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
lobsterkatiecommented, Mar 22, 2021

Hi, all. I took some time to look into this, and unfortunately, Kamil is right - without a near-total rewrite of the Express integration, there’s no good way to solve this problem. I think I might have a hack which could solve part of it (the labeling of transactions, accounting for the substitution problem Kamil mentioned), and it’s conceivable a similar method could work to solve the original issue mentioned here (that of missing spans), but to really make it work, we’ll have to adopt a different approach. That’s not off the table, but moves this out of the category of “small bug fix” and into the category of “work that has to be planned for,” which is why it hasn’t happened yet, unfortunately.

A little more detail, in case it’s helpful:

The first things to know are:

  1. Each router only keeps track of or handles its part of the path.
  2. The SDK has access to the routers (both for span creation and the access to the parameterized route) by dint of wrapping .use(), .get(), and the like, which are at their heart registration methods - “when there’s a GET request to /xyz, please run this handler” - and which therefore are only run once, at app startup.

The root cause of the problem is that by the time the SDK comes along to do its wrapping, all but the top-level routers have already registered their handlers: as soon as you import a router, its code runs and all of the sub-routers get registered. But of course, you have to import the router in order to be able to give it to the SDK to wrap, so by definition that will happen before the SDK can do its thing. Only calls to .use(), .get(), etc which occur after Sentry.init() get wrapped, so the upshot is that only routers the top-most level of the router tree can either create spans or provide a parameterized version of their part of the path. (Further, at the moment, the integration only accepts a single router, the root of the tree, and so nothing below that top level could be wrapped in any case.) The only way to get around this would be to modify the integration to accept multiple routers, and then to create and configure all routers in the same file as the Sentry.init() call, making sure that all .use(), .get(), etc calls happen once the SDK is already running. Gross, and also unrealistic.

I spent more time than I care to admit stepping through Express routing code, and I think there may be a spot we could hook into, as the parameters are being processed, which would let us reconstruct each part of the parameterized path as it’s matched. (It’s similar to the substitution Kamil mentioned, but with the ability to disambiguate identical parameter names, as well as parameter values which match hard-coded parts of the path.) I haven’t had a chance to fully test it out, though, nor to see if similar hackery could be employed for span creation. Ultimately, I think our better bet will be to continue to accept only the top-level router, but to then actually walk the router tree after it’s fully formed.

I know this doesn’t solve the problem, but hopefully it at least gives you context for why it hasn’t been solved yet. I’ll update here as anything changes.

2reactions
kamilogorekcommented, Jan 22, 2021

@adamup928 agreed, I’ll make sure that this issue gets addressed as soon as possible.

Read more comments on GitHub >

github_iconTop Results From Across the Web

node.js - How does one implement Sentry Performance ...
I have poked around @sentry/tracing code and there seems to be an Express integration available. Based on the integration source code, it looks ......
Read more >
Express.js Middleware Can Be Arbitrarily Nested Within A ...
Ben Nadel demonstrates that middleware can be arbitrarily nested within an Express.js route definition in Node.js. This is because Express ...
Read more >
Using middleware - Express.js
Bind application-level middleware to an instance of the app object by using the app.use() and app.METHOD() functions, where METHOD is the HTTP method...
Read more >
Express - Sentry Documentation
Learn about using Sentry with Express. ... The request handler must be the first middleware on the app app.use(Sentry.Handlers.requestHandler()) ...
Read more >
@sentry/tracing | Yarn - Package Manager
[react] feat: Expose eventId on ErrorBoundary component (#2704); [node] fix: Extract transaction from nested express paths correctly (#2714) ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found