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

Add Sentry #24

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 70 additions & 5 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
const path = require('path');
const withSourceMaps = require('@zeit/next-source-maps')();

module.exports = {
sassOptions: {
includePaths: [path.join(__dirname, 'src', 'styles')],
// Use the SentryWebpack plugin to upload the source maps during build step
const SentryWebpackPlugin = require('@sentry/webpack-plugin');

const {
NEXT_PUBLIC_SENTRY_DSN: SENTRY_DSN,
SENTRY_ORG,
SENTRY_PROJECT,
SENTRY_AUTH_TOKEN,
NODE_ENV,
VERCEL_GITHUB_COMMIT_SHA,
VERCEL_GITLAB_COMMIT_SHA,
VERCEL_BITBUCKET_COMMIT_SHA,
} = process.env;

const COMMIT_SHA =
VERCEL_GITHUB_COMMIT_SHA ||
VERCEL_GITLAB_COMMIT_SHA ||
VERCEL_BITBUCKET_COMMIT_SHA;

process.env.SENTRY_DSN = SENTRY_DSN;
const basePath = '';

module.exports = withSourceMaps({
serverRuntimeConfig: {
rootDir: __dirname,
},
webpack(config) {
webpack(config, options) {
config.resolve = {
...config.resolve,
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
Expand All @@ -13,8 +36,50 @@ module.exports = {
test: /\.svg$/,
use: ['@svgr/webpack'],
});

// In `pages/_app.js`, Sentry is imported from @sentry/browser. While
// @sentry/node will run in a Node.js environment. @sentry/node will use
// Node.js-only APIs to catch even more unhandled exceptions.
//
// This works well when Next.js is SSRing your page on a server with
// Node.js, but it is not what we want when your client-side bundle is being
// executed by a browser.
//
// Luckily, Next.js will call this webpack function twice, once for the
// server and once for the client. Read more:
// https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config
//
// So ask Webpack to replace @sentry/node imports with @sentry/browser when
// building the browser's bundle
if (!options.isServer) {
config.resolve.alias['@sentry/node'] = '@sentry/browser';
}

// When all the Sentry configuration env variables are available/configured
// The Sentry webpack plugin gets pushed to the webpack plugins to build
// and upload the source maps to sentry.
// This is an alternative to manually uploading the source maps
// Note: This is disabled in development mode.
if (
SENTRY_DSN &&
SENTRY_ORG &&
SENTRY_PROJECT &&
SENTRY_AUTH_TOKEN &&
NODE_ENV === 'production'
) {
config.plugins.push(
new SentryWebpackPlugin({
include: '.next',
ignore: ['node_modules'],
stripPrefix: ['webpack://_N_E/'],
urlPrefix: `~${basePath}/_next`,
})
);
}

return config;
},
basePath,
async redirects() {
return [
{
Expand All @@ -34,4 +99,4 @@ module.exports = {
},
];
},
};
});
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
},
"dependencies": {
"@popperjs/core": "^2.5.2",
"@sentry/node": "^5.27.2",
"@sentry/react": "^5.27.2",
"@sentry/tracing": "^5.27.2",
"@sentry/webpack-plugin": "^1.13.0",
"@svgr/webpack": "^5.4.0",
"@tailwindcss/custom-forms": "^0.2.1",
"@zeit/next-source-maps": "0.0.4-canary.1",
"classnames": "^2.2.6",
"downshift": "^6.0.6",
"i18next": "^19.8.2",
Expand Down
5 changes: 5 additions & 0 deletions src/pages/404.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Error from 'next/error';

export default function NotFound() {
return <Error statusCode={404} />;
}
12 changes: 10 additions & 2 deletions src/pages/_app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ import 'styles/index.css';
import {ReactQueryDevtools} from 'react-query-devtools';
import 'locales/i18n';
import Layout from 'components/Layout';
import * as Sentry from '@sentry/node';

if (process.env.NEXT_PUBLIC_SENTRY_DSN) {
Sentry.init({
// enabled: process.env.NODE_ENV === 'production',
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
});
}

const queryCache = new QueryCache();

export default function App({Component, pageProps}) {
export default function App({Component, pageProps, err}) {
return (
<ReactQueryCacheProvider queryCache={queryCache}>
<Hydrate state={pageProps.dehydratedState}>
<Layout>
<Component {...pageProps} />
<Component {...pageProps} err={err} />
</Layout>
<ReactQueryDevtools initialIsOpen />
</Hydrate>
Expand Down
59 changes: 59 additions & 0 deletions src/pages/_error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import NextErrorComponent from 'next/error';
import * as Sentry from '@sentry/node';

const MyError = ({statusCode, hasGetInitialPropsRun, err}) => {
if (!hasGetInitialPropsRun && err) {
// getInitialProps is not called in case of
// https://github.com/vercel/next.js/issues/8592. As a workaround, we pass
// err via _app.js so it can be captured
Sentry.captureException(err);
}

return <NextErrorComponent statusCode={statusCode} />;
};

MyError.getInitialProps = async ({res, err, asPath}) => {
const errorInitialProps = await NextErrorComponent.getInitialProps({
res,
err,
});

// Workaround for https://github.com/vercel/next.js/issues/8592, mark when
// getInitialProps has run
errorInitialProps.hasGetInitialPropsRun = true;

// Running on the server, the response object (`res`) is available.
//
// Next.js will pass an err on the server if a page's data fetching methods
// threw or returned a Promise that rejected
//
// Running on the client (browser), Next.js will provide an err if:
//
// - a page's `getInitialProps` threw or returned a Promise that rejected
// - an exception was thrown somewhere in the React lifecycle (render,
// componentDidMount, etc) that was caught by Next.js's React Error
// Boundary. Read more about what types of exceptions are caught by Error
// Boundaries: https://reactjs.org/docs/error-boundaries.html

if (res?.statusCode === 404) {
// Opinionated: do not record an exception in Sentry for 404
return {statusCode: 404};
}
if (err) {
Sentry.captureException(err);
await Sentry.flush(2000);
return errorInitialProps;
}

// If this point is reached, getInitialProps was called without any
// information about what the error might be. This is unexpected and may
// indicate a bug introduced in Next.js, so record it in Sentry
Sentry.captureException(
new Error(`_error.js getInitialProps missing data at path: ${asPath}`)
);
await Sentry.flush(2000);

return errorInitialProps;
};

export default MyError;
Loading