-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
ref(nextjs): Remove next.js plugin #3462
Conversation
size-limit report
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😢
🕊️ |
…ack (#3463) Co-authored-by: Daniel Griesser <daniel.griesser.86@gmail.com>
scope.addEventProcessor(event => parseRequest(event, req)); | ||
captureException(e); | ||
}); | ||
await flush(2000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is flushing on every error required? #3301 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iker-barriocanal @lobsterkatie i've found that flushing here breaks Next.js API Routes in dev mode when using cors
middleware.
It results in an ERR_STREAM_WRITE_AFTER_END
error.
Removing the call to flush resolves it, but i'm not sure what the side affects of doing this might be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We flush to ensure that when API routes are deployed as serverless functions, the event gets sent before the function ends. @ashconnell - can you share a sample API route which is crashing for you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lobsterkatie here's a reproduction.
I'm using the latest Next.js and Sentry versions.
To reproduce the error you need to fetch the api route from another origin so that it triggers the cors preflight etc.
Note that the request works if you remove the withSentry wrapper.
import { withSentry } from '@sentry/nextjs'
import Cors from 'cors'
// This is straight from Next.js docs on how to use cors
// See: https://nextjs.org/docs/api-routes/api-middlewares#connectexpress-middleware-support
function runMiddleware(req, res, fn) {
return new Promise((resolve, reject) => {
fn(req, res, result => {
if (result instanceof Error) {
return reject(result)
}
return resolve(result)
})
})
}
const cors = async (req, res) => {
return await runMiddleware(req, res, Cors({}))
}
async function handler(req, res) {
await cors(req, res)
res.status(200).send({ message: 'Hello!' })
}
export default withSentry(handler)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lobsterkatie from what I can tell, flush is somehow calling res.end()
after the regular request has already ended. Since flush is part of the core, maybe regular express apps protect against calling .end() multtiple times, but Next.js just uses a thin wrapper around node's http.ServerResponse which does not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may have found something. So when @sentry/nextjs calls flush, @sentry/node attempts to get the client which does not exist, and rejects the promise with false
// node/sdk.ts
export async function flush(timeout?: number): Promise<boolean> {
const client = getCurrentHub().getClient<NodeClient>();
if (client) {
return client.flush(timeout);
}
return Promise.reject(false);
}
Since the @sentry/nextjs flush awaits
flush in the finally clause and it fails, the whole withSentry
call is rejected, which Next.js catches and calls res.end()
.
This is where Next.js handles this: https://github.com/vercel/next.js/blob/canary/packages/next/next-server/server/api-utils.ts#L84
When making the exact same request from the same origin, the client
value is defined so this error never happens. I have no idea why this is.
// type WebpackExport = (config: WebpackConfig, options: WebpackOptions) => Promise<WebpackConfig>; | ||
|
||
// The two arguments passed to the exported `webpack` function, as well as the thing it returns | ||
type WebpackConfig = { devtool: string; plugins: PlainObject[]; entry: EntryProperty }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use type Configuration
which is exported from webpack
? By the way, it probably should be added to peer dependencies (and also to dev dependencies if the type Configuration
is used)... It's also good from the perspective of letting users know what version of webpack
this package expects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next is in the process of moving from webpack 4 to webpack 5 right now. Worth taking that into account with the dep versioning.
NB: This PR was accidentally merged without the complete set of changes described below being included. The remaining changes are in #3476.
This PR removes the nextjs plugin, in light of the infrastructure for it having been removed by Vercel, and replaces it with a combination of webpack loaders and server monkeypatches. Specifically:
In order to ensure that the SDK is initialized (in other words, that
sentry.server.config.js
andsentry.client.config.js
are run) when the server starts and a page loads in the browser, respectively, theentry
property in the webpack config is modified to include those files in three places: 1) inpages/_document
, which covers non-API pages, 2) inpages/api/*
, which covers API request handlers, and 3)main
, which is included in every page loaded in the browser.To catch errors in API routes, a new
withSentry
helper has been introduced, with which API routes can be wrapped. Docs for that are here. We will try to make this automatic in the future.To catch errors in functions like
getServerSideProps
(functions which run as part of non-API request handling), the server is monkeypatched to wrap the error logger which is called whenever such an error occurs.There are still a number of TODOs, as you will see in the code, but none of them are blockers, and can therefore be handled in future PRs.