Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nextjs): Connect trace between data-fetching methods and pageload #5655

Merged
merged 13 commits into from
Sep 1, 2022

Conversation

lforst
Copy link
Member

@lforst lforst commented Aug 30, 2022

Ref: #5505

Connects traces between data fetching methods and the client by returning trace parent data and baggage from the data fetchers in a way that the client understands.

Additionally:

  • Read trace data off incoming requests and add it to data fetcher transactions
  • Fix a bug where caught uncaught Promises were not rethrown in error instrumentation

We don't return baggage + trace parent info from _document's getInitialProps because those props are not put into the __NEXT_DATA__ tag.

// First of all, we need to capture promise rejections so we have the following check, as well as the try-catch block.
// Additionally, we do the following instead of `await`-ing so we do not change the method signature of the passed function from `() => unknown` to `() => Promise<unknown>.
Promise.resolve(potentialPromiseResult).catch(err => {
// TODO: Extract error logic from `withSentry` in here or create a new wrapper with said logic or something like that.
captureException(err);
throw err;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small bugfix: we need to rethrow the rejected promise here, otherwise it counts as "caught"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay so this turned out to be problematic. Throwing here caused an uncaught promise rejection to bubble up, which terminates the process in Node 16 and above. I think my approach here was overly clever from the beginning so I changed it to simply await the maybe-promise result. This makes the wrapped function async but as far as I can tell that is fine since everywhere we use it, we're already async.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking more about this, if we really want this wrapper function not to be changing the function signature in the future, we can go with that approach, but without the throw.

I think the behaviour stays as expected: awaiting a rejected Promise throws, and you can also catch it via .catch(). The only drawback is, that it will be marked as "caught", not causing an unhandled promise rejection to bubble up. We can maybe circumvent this via queueMicrotask: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#when_promises_and_tasks_collide. But that again sounds a bit overly clever and is complicated to understand after the fact.

Comment on lines -25 to -28
// contains props returned by `getInitialProps` - except for `pageProps`, these are the props that got returned by `getServerSideProps` or `getStaticProps`
props: {
_sentryGetInitialPropsTraceData?: string; // trace parent info, if injected by server-side `getInitialProps`
_sentryGetInitialPropsBaggage?: string; // baggage, if injected by server-side `getInitialProps`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got this wrong in a previous PR

Copy link
Member

@AbhiPrasad AbhiPrasad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test case to our nextjs integration tests for this?

@lforst lforst mentioned this pull request Aug 30, 2022
43 tasks
Copy link
Member

@lobsterkatie lobsterkatie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but I have one question: Does it matter which data-fetching function provides the sentry-trace and baggage data? If not, is there a reason to differentiate between _sentryGetServerSidePropsBaggage and _sentryGetInitialPropsBaggage (same for senty-trace)? Could we even just do something like:

pageProps._sentryTracingHeaders = {"sentry-trace": xxxxx, bagage: yyyyy}

?

} else {
return errorWrappedGetServerSideProps(...getServerSidePropsArguments);
return origGetServerSideProps(...getServerSidePropsArguments);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the error handling here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was just messing around and forgot to put it back. Good catch! Changed it back in faaba12

@github-actions
Copy link
Contributor

github-actions bot commented Aug 31, 2022

size-limit report 📦

Path Size
@sentry/browser - ES5 CDN Bundle (gzipped + minified) 19.42 KB (0%)
@sentry/browser - ES5 CDN Bundle (minified) 60.08 KB (0%)
@sentry/browser - ES6 CDN Bundle (gzipped + minified) 17.99 KB (-0.01% 🔽)
@sentry/browser - ES6 CDN Bundle (minified) 52.94 KB (0%)
@sentry/browser - Webpack (gzipped + minified) 19.79 KB (0%)
@sentry/browser - Webpack (minified) 64.33 KB (0%)
@sentry/react - Webpack (gzipped + minified) 19.81 KB (0%)
@sentry/nextjs Client - Webpack (gzipped + minified) 44.66 KB (-0.13% 🔽)
@sentry/browser + @sentry/tracing - ES5 CDN Bundle (gzipped + minified) 25.91 KB (0%)
@sentry/browser + @sentry/tracing - ES6 CDN Bundle (gzipped + minified) 24.28 KB (0%)

@lforst
Copy link
Member Author

lforst commented Aug 31, 2022

Looks good, but I have one question: Does it matter which data-fetching function provides the sentry-trace and baggage data? If not, is there a reason to differentiate between _sentryGetServerSidePropsBaggage and _sentryGetInitialPropsBaggage (same for senty-trace)?

@lobsterkatie No, it doesn't matter at all, so this is excellent feedback. Thank you! I changed it so we're only setting _sentryTraceData and _sentryBaggage - no matter what data fetcher was called. faaba12

@lforst
Copy link
Member Author

lforst commented Aug 31, 2022

Can we add a test case to our nextjs integration tests for this?

@AbhiPrasad I added some.

@lforst lforst force-pushed the lforst-next-connected-traces branch from b8c734e to cee6dc3 Compare August 31, 2022 14:02
Copy link
Member

@lobsterkatie lobsterkatie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment on lines +15 to 24
/**
* Grabs a transaction off a Next.js datafetcher request object, if it was previously put there via
* `setTransactionOnRequest`.
*
* @param req The Next.js datafetcher request object
* @returns the Transaction on the request object if there is one, or `undefined` if the request object didn't have one.
*/
export function getTransactionFromRequest(req: IncomingMessage): Transaction | undefined {
return req._sentryTransaction;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you choose to wrap this line in a function? (Same goes for the setter.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know to be honest. It just made sense to me to put this behind a little bit of abstraction to provide more context why this field exists on the req object. Feel free to remove it in the future though! I think the ambient type (declare module 'http' { at the top of the file) provides enough context.

@lforst lforst merged commit 53915b0 into master Sep 1, 2022
@lforst lforst deleted the lforst-next-connected-traces branch September 1, 2022 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants