Server side you may want to create a custom server, using the express connection will make it easier to integrate.
Client side you can load the JS lib in a custom pages/_document.js and run it, you can follow the example in Sentry docs on how to use it with React because you will need to handle error in render of each component. Maybe you can create a HOC for that.
Thanks @sergiodxa for answering.
This still does not work because of #1852, which really needs to be addressed.
@sergiodxa I've tried creating a HOC wrapping the Main component and it didn't worked. It returns Error: unmountComponentAtNode(...): Target container is not a DOM element.
import React from 'react'
import Raven from 'raven-js'
import config from '../config/app'
let logException = (ex, context) => { window.console && console.error && console.error(ex) } // eslint-disable-line no-console
if (config.sentry) {
Raven.config(config.sentry).install()
logException = (ex, context) => {
Raven.captureException(ex, {
extra: context
})
window.console && console.error && console.error(ex) // eslint-disable-line no-console
}
}
export default function withSentry(wrappedComponent) {
return class SentryComponent extends React.Component {
render() {
try {
return <wrappedComponent {...this.props} />
} catch (ex) {
logException(ex)
}
}
}
}
And then in the _document.js I wrap it this way:
// ...
render() {
const Content = withSentry(Main)
return ( // ...
<Content />
// ...
)
}
// ...
Finally resolved it by adding the withSentry HOC to each page instead of the _document.js. Not sure if it's the best way but works.
I confirm the issue, next.js is swallowing the errors thrown during the render of React.
For instance, the following hook is never called:
window.onerror = function() {
console.log('onerror')
};
The errors are catch on the server and the client, also using this hook ReactReconciler.mountComponent. It's challenging catch the errors.
I ended up performing the following changes:
raven to raven-js on the client for code sharing as the code needed to initialize raven-js on the client needs to be bundled for the server too. resolve: Object.assign({}, config.resolve, {
alias: Object.assign({}, config.resolve.alias, {
// raven for the server
// raven-js for the client
raven: 'raven-js',
}),
}),
server.js initialization script to bootstrap raven on the server. This is performed before bootstrapping the next.js request handler.client.js initialization script to bootstrap raven-js on the client. This is performed at the page level. Also, we need to make sure it's bootstrapped only once.common.js script to throw errors independently from the platform (client or server). I'm using a global internally to share the raven/raven-js object.getInitialProps with a try {} catch (err) {} to throw errors. In my case, react-apollo is traversing the react tree (depth breadth first) in a higher-order component.ReactReconciler.mountComponent to catch errors.ReactReconciler.mountComponent.+1 for being able to track the errors in the way we need!
@oliviertassinari should we re-open the issue? I think the errors should be propagated to the client/server. Many tools may be affected by this (Sentry, Rollbar, New Relic, etc)
@lardissone I would rather see next.js forwarding errors after handling them internally if possible. I'm doing the opposite right now, catching errors then forwarding them to next.js, that's more work on user space.
One last thing, I had to remove the production aliasing to the minified version of React in order to get the ReactReconciler.mountComponent hooks working.
alias: Object.assign({}, config.resolve.alias, {
// raven for the server
// raven-js for the client
raven: 'raven-js',
// Fix for hijacking react-dom/lib/ReactReconciler
react: 'react',
'react-dom': 'react-dom',
}),
@lardissone Back to your question. Yes, I think that we should be reopening this issue in order to expose a clean API. I believe that the amount of introspection needed into next.js to make it work is too high.
More specifically, having to hook into ReactReconciler.mountComponent on userland isn't great. I'm not sure what's the React Fiber story around error boundaries is, but I would rather not have to do it on userland (I only had to do it because next.js do it)
This seems to be working well:
// pages/_error.js
import NextError from 'next/error'
import { logException } from '../src/utils/analytics'
export default class MyError extends NextError {
static getInitialProps({ res, err }) {
if (!(err instanceof Error)) {
err = new Error(err && err.message)
}
res ? Raven.captureException(err) : logException(err)
return NextError.getInitialProps({ res, err })
}
}
Some notes:
res is truthy in SSR onlybrowser.raven to false in package.json to prevent it from being bundled for the clienterr parameter seems inconsistentlogException is a wrapper around Raven.captureException using the CDN-hosted package. I've got a Custom Document that loads Raven before <NextScript />Thoughts?
@mattfysh How can I reach inside of MyError when a error is thrown on client side?
I add res ? console.log('server side error') : console.log('client side error'); in getInitialProps, but I can get 'server side error' only.
When client side error thrown, Sentry get the error. But I think this is not related with MyError.
@pueue how are you navigating to the page? If you're doing full page refresh/load of the page, you'll get the server side error. To trigger the client-side error, you need to navigate from another page, to the page with the error, using a <Link> or similar.
@mattfysh This is a example.
// pages/page1.js
export default Page1 extends React.Component {
...
throwError() {
throw new Error('client side eventHandling error');
}
render() {
return (
<div>
<Link href='/page2'>Link</Link><br />
<TextField onChange={this.throwError}/>
</div>
)
}
}
// pages/page2.js
export default Page2 extends React.Component {
...
render() {
eval("throw new Error('client side rendering error')");
return (
<div>
</div>
);
}
}
When I change something in TextField, a error is thrown and sentry catch it.
But when I click Link, a error is thrown and sentry cannot catch it.
Can I see your sample code using what you suggest? I need a help.
Most helpful comment
This seems to be working well:
Some notes:
resis truthy in SSR onlybrowser.raventofalsein package.json to prevent it from being bundled for the clienterrparameter seems inconsistentlogExceptionis a wrapper aroundRaven.captureExceptionusing the CDN-hosted package. I've got a Custom Document that loads Raven before<NextScript />Thoughts?