-
Notifications
You must be signed in to change notification settings - Fork 27.1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
How to catch and handle errors to report logs on server side #1852
Comments
For logging errors in the client side we have been doing the following: It basically sends errors to the server, that are in the end printed to |
I'm also having this problem. I wonder: would simply returning a rejected promise from next's handleRequest (req, res, parsedUrl) {
// .....snip....
return this.run(req, res, parsedUrl)
.catch((err) => {
if (!this.quiet) console.error(err)
res.statusCode = 500
res.end(STATUS_CODES[500])
// rethrow error to create new, rejected promise
throw err;
})
} Then, in user code: const app = nextJs({ dev: process.env.NODE_ENV !== 'production' });
const nextJsHandler = app.getRequestHandler();
const expressApp = express();
app.prepare().then(() => {
// invoke express middlewares
// ...
// time to run next
expressApp.use(function(req, res, next) {
nextJsHandler(req, res).catch(e => {
// use rejected promise to forward error to next express middleware
next(e)
})
});
// Use standard express error middlewares to handle the error from next
// this makes it easy to log to sentry etc.
}) |
Agree to re-throw an error so we can play around with it. |
I am also in the situation where we want to sent errors, both on server and client side, to a service similar to Sentry. I believe that the most valuable feature of such services/tools is to report issues that are most unexpected, which in my experience are uncaught errors in the wild (ie. client-side). Unfortunately, as previously stressed out in related issue #2334, Next.js’ client-side handlers keep these errors to themselves, with no possible way to pass them to Sentry of such other tool. What has bitten us particularly hard is this: a properly server-side rendered page is re-rendered as an error page if an uncaught exception occurs before React rendering on the client-side. This can be seen as either a great feature, but also a frustrating developer experience, as it essentially ruins the benefits of serving an already rendered document, on a surprisingly portion of clients. Here is a sample page that illustrates the problem: import React from 'react';
// Works well in Node 8, but crashes in Chrome<56, Firefox<48, Edge<15, Safari<10, any IE…
const whoops = 'Phew, I made it to the client-side…'.padEnd(80);
export default () => <pre>{whoops}</pre>; The above code can be perfectly server-side-rendered and delivered to the client, only to become a Flash Of Unwanted Content before the whole page is replaced on the client-side by the dreaded ”An unexpected error has occurred” message on most browsers, without any possible way to report the error to Sentry (or any other service/tool). This ”error swallowing” also prevents any leverage of the standard onerror handler, which most client-side error reporting tools hook onto (or the more modern, but not widespread, onunhandledrejection, which may be more suitable given the async nature of client-side code). Possible client-side solutionAs far as I can tell, this type of pre-React client-side error is swallowed in the Dear Next.js authors and maintainers, for the sake of control of what is rendered, what would you think would be acceptable/possible among the following ideas:
|
This is something we've talked about internally. And we'll be addressing soon. |
I'm using Sentry.io to report errors and the solution we apply is: 1- Configure raven-node on server side With this two steps we catch any unhandled exceptions both on client and server. 3- Create an The way to decide how to load if raven-node or ravenjs is solved importing dynamically depending if we are in client const webpack = require('webpack');
module.exports = {
// Do not show the X-Powered-By header in the responses
poweredByHeader: false,
webpack: (config) => {
config.plugins.push(new webpack.IgnorePlugin(/^raven$/));
return config;
},
}; |
Can you show me how did you configured raven-js on your Do I need to send the all the errors manually at // _document constructor
constructor(props) {
super(props);
Raven
.config('...')
.install();
} |
Next.js still outputs errors to With Sentry, you can enable Example implementation: const express = require('express');
const nextjs = require('next');
const Raven = require('raven');
const dev = process.env.NODE_ENV !== 'production';
// Must configure Raven before doing anything else with it
if (!dev) {
Raven.config('__DSN__', {
autoBreadcrumbs: true,
captureUnhandledRejections: true,
}).install();
}
const app = nextjs({ dev });
const handle = app.getRequestHandler();
const captureMessage = (req, res) => () => {
if (res.statusCode > 200) {
Raven.captureMessage(`Next.js Server Side Error: ${res.statusCode}`, {
req,
res,
});
}
};
app
.prepare()
.then(() => {
const server = express();
if (!dev) {
server.use((req, res, next) => {
res.on('close', captureMessage(req, res));
res.on('finish', captureMessage(req, res));
next();
});
}
[...]
server.get('/', (req, res) => {
return app.render(req, res, '/home', req.query)
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen('3000', (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
}); This is a very contrived example adapted from our actual code. I haven't tested it in this form. Let me know if it breaks. Of course, it would still be best if Next.js could pass those errors around to Express, so that we can use the Sentry/Express integration out of the box. |
@tusgavomelo Sorry, for the late reply. We have update. In our app we have a helper file with a method responsible to get a Raven instance taking into account if we are on client or server side. let clientInstance;
let serverInstance;
const getRavenInstance = (key, config) => {
const clientSide = typeof window !== 'undefined';
if (clientSide) {
if (!clientInstance) {
const Raven = require('raven-js'); // eslint-disable-line global-require
Raven.config(key, config).install();
clientInstance = Raven;
}
return clientInstance;
}
if (!serverInstance) {
// NOTE: raven (for node) is not bundled by webpack (see rules in next.config.js).
const RavenNode = require('raven'); // eslint-disable-line global-require
RavenNode.config(key, config).install();
serverInstance = RavenNode;
}
return serverInstance;
}; This code runs both server side (when a request arrives) and client side. What we have done is configure webpack ( |
@acanimal can you maybe provide a working example ? It seems I am not getting the full stack trace ? or maybe can you post your I am doing something like
|
This seems like the place to ask for custom logger support since |
The errors details are also available on the stderr stream. |
Gotta love unortodox solutions
|
Compact version of how to get correct Raven for node and browser w/o custom webpack config. Inspired by @acanimal comment // package.json
"browser": {
"raven": "raven-js"
}
// getRaven.js
const Raven = require('raven')
if (process.env.NODE_ENV === 'production') {
Raven.config('YOUR_SENTRY_DSN').install()
}
module.exports = Raven |
a quick recap for anyone who investigating on this problem. // pages/_error.js
import Raven from 'raven';
...
static async getInitialProps({ store, err, isServer }) {
if (isServer && err) {
// https://github.com/zeit/next.js/issues/1852
// eslint-disable-next-line global-require
const Raven = require('raven');
Raven.captureException(err);
}
... // next.config.js
config.plugins.push(new webpack.IgnorePlugin(/^raven$/)); thanks to @acanimal 's comment. |
Install both import Raven from 'raven-js';
// https://gist.github.com/impressiver/5092952
const clientIgnores = {
ignoreErrors: [
'top.GLOBALS',
'originalCreateNotification',
'canvas.contentDocument',
'MyApp_RemoveAllHighlights',
'http://tt.epicplay.com',
"Can't find variable: ZiteReader",
'jigsaw is not defined',
'ComboSearch is not defined',
'http://loading.retry.widdit.com/',
'atomicFindClose',
'fb_xd_fragment',
'bmi_SafeAddOnload',
'EBCallBackMessageReceived',
'conduitPage',
'Script error.',
],
ignoreUrls: [
// Facebook flakiness
/graph\.facebook\.com/i,
// Facebook blocked
/connect\.facebook\.net\/en_US\/all\.js/i,
// Woopra flakiness
/eatdifferent\.com\.woopra-ns\.com/i,
/static\.woopra\.com\/js\/woopra\.js/i,
// Chrome extensions
/extensions\//i,
/^chrome:\/\//i,
// Other plugins
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
/webappstoolbarba\.texthelp\.com\//i,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
],
};
const options = {
autoBreadcrumbs: true,
captureUnhandledRejections: true,
};
let IsomorphicRaven = null;
if (process.browser === true) {
IsomorphicRaven = Raven;
IsomorphicRaven.config(SENTRY_PUBLIC_DSN, {
...clientIgnores,
...options,
}).install();
} else {
// https://arunoda.me/blog/ssr-and-server-only-modules
IsomorphicRaven = eval("require('raven')");
IsomorphicRaven.config(
SENTRY_DSN,
options,
).install();
}
export default IsomorphicRaven; Then you can use it in your import NextError from 'next/error';
import IsomorphicRaven from 'lib/raven';
class MyError extends NextError {
static getInitialProps = async (context) => {
if (context.err) {
IsomorphicRaven.captureException(context.err);
}
const errorInitialProps = await NextError.getInitialProps(context);
return errorInitialProps;
};
}
export default MyError; |
Here is my PR for Rollbar sourcemap wepback plugin thredup/rollbar-sourcemap-webpack-plugin#56 with Next.js support :) |
@tusgavomelo , Can you elaborate on how to make use of the error present on the stream ? Where should we write this line of code, for it to log the error to Node console ? |
That is only Client side. |
@teekey99 Do you have similar solution for |
I use So I made two small utility functions.
/**
* HOC handle `failure` prop by wrapping page components.
*/
export const withExceptionCatcher = (WrappedComponent) => {
const wrapper = (props) => {
// if there is no failure, render normal page component :)
if (!props.failure) {
return <WrappedComponent {...props} />
}
const { ... } = props.failure
return (
<div>
<FailureSection
...
/>
</div>
)
}
return wrapper
}
/**
* Handle exceptions in original page components from getServerSideProps
* `failure` prop will be passed if error occurs.
*/
export function getServerSidePropsWithCatcher(getServerSidePropsFunc: Function) {
return async (ctx) => {
let props = {}
try {
props = await getServerSidePropsFunc(ctx)
} catch (error) {
return { props: { failure: error } }
}
return { ...props }
}
} Here is usage // MyPage.tsx
function MyPage() {
...
}
export const getServerSideProps = getServerSidePropsWithCatcher(async (ctx) => {
const result = await fetchData()
return {
props: { ... },
}
})
export default withExceptionCatcher(MyPage) Hope this might help. |
I've been struggling with monkey-patching all the different parts of Next.js that log errors on the path to using Next.js for Notion's new marketing site. It's been a frustrating experience; I'm up to patching |
It's super frustrating that a basic need like this still open after 4 years and 10 major releases. Custom logging and error handling are basic features being neglected from the Next.js team sadly 🥲 |
Despite having spent quite a lot of time configuring Sentry, I also feel like it can still be improved and I'm afraid I'm not catching every case. (I'm talking about error catching only) Regarding logging, I'm also "frustrated" with it and not satisfied of the solution I came up with using Winston (too heavy, not flexible enough, etc.) |
Same to you folks. with-sentry example doesn't allow lazy loading, as well as it doesn't catch errors before app initialization. Moreover there are can be errors in your custom ssr server which are also want to be tracked with sentry. So these days I see such issues with setup of sentry error tracking with nextjs which are not covered by any guide:
|
I feel like this and lack of guides/prebuilt integrations for apm and error reporting services are a big gap for next.js. I hope this is high on the roadmap. how are large companies even running next.js without these 2 basic things? I guess custom server and prayers 😢 |
@kelly-tock Large companies using Next.js do have error handling, logging, and monitoring set up! I agree with your statement though, and we're working on better integrations (e.g. Sentry, Datadog) with Vercel, as well as overall better guidance for "going to production" with Next.js (logging, monitoring). |
Glad to hear that! I'm currently working on a proposal for switching to next, and performance monitoring and error handling will be key. Any ideas on timelines? And for both APM and error logging services, we're left to do custom servers for now right? |
It's definitely a priority! And no, you wouldn't need a custom server:
Hope this helps in the meantime. |
@kelly-tock if you have control of Next’s invocation, you can do this without a “custom server” using a “preload”. If you can invoke next like |
@justjake Yes, absolutely 😄 |
we need a my project would fall into
so would like to integrate a datadog/appsignal/etc into it. as well as rollbar. looking forward to more guides and resources. |
@kelly-tock Next Right Now uses Sentry for monitoring (front + back + api) Running about 20 projects using Next.js (most of them using NRN) I totally feel you regarding monitoring, it's a must-have for confidence. Regarding logging though, I haven't found a solution that satisfies me. I hope it comes built-in. |
@kelly-tock okay, I wrote up a quick note about how we use preloading with NextJS: https://jake.tl/notes/2021-04-04-nextjs-preload-hack |
Hi, 2 things that I still don't understand regarding this issue:
|
@justjake Thanks for the excellent writeup! While having to monkey-patch logging is a shame, I think using Sentry just released a new official Next.js SDK that has an interesting approach: it actually injects Sentry setup into the Webpack bundles for your app, both the client and server (https://github.com/getsentry/sentry-javascript/blob/master/packages/nextjs/src/utils/config.ts#L154-L155). Unfortunately, we're using Rollbar at my company, and Rollbar doesn't have any Next.js integration, so we'll have to do a manual setup. The In general, I think Next.js docs should incorporate discussion of error reporting. I had deferred adding error handling to the "last 10% work" of a new project launch and spent yesterday in a bit of a panic trying to figure out how on earth I was going to add it. I had just assumed it'd be as easy as adding Sentry/Rollbar/Bugsnag/etc usually is - just add it to the entry point - until I realized Next has no "entry point" without a custom server. If the use of |
@thomasboyt Thanks for pointing out Sentry released something for Next.js! I'm gonna take a look at it very soon! For anyone interested: https://sentry.io/for/nextjs/ Also, we mentionned logging above, and we've released https://github.com/UnlyEd/simple-logger a few days ago, which is 1kB an universal. After trying out a lot of open source alternatives, we couldn't find something that felt just right. Maybe you'll find it as useful as we do! |
thanks for writing that out but indeed we need a generic "error handler" for the server like pretty much any express.js production app will end up having so unexpected errors can be caught, a user-friendly 500 can be return and the error can be sent to any error reporting library. fingers crossed this will be sorted out soon as it's rather important. |
This comment has been minimized.
This comment has been minimized.
I tried with this preloading script. It printed error messages but missed request URL and stack trace. It is super hard or impossible to know where the error was happening. Edit: I have ended up using https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs |
Worked for me! |
We need a server side ErrorBoundary 🤗 |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Hi,
I'm in the situation where we want to sent errors, both on server and client side, to Sentry tool.
Our app uses Express as a custom server. Basically we create an express app, apply some middlewares but delegate all the real job to the next.js handle:
With this approach next.js takes control over the rendering process and any error is catch by next.js and the only way I have to process it is overriding the
_error.js
page file.Within that
_error.js
file I need a universal way to report errors to Sentry. Currently there are two libraries (raven
for node andraven-js
por javascript). The proble is I can't import both of them becauseraven
works for SSR but fails when webpack builds the bundle, and alsoraven-js
fails due XMLHTTPRequest dependency too.Is there a way I can be notified for next.js error on server side?
The text was updated successfully, but these errors were encountered: