Skip to content

WIP: feat(examples): add Next.js example #1

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

Open
wants to merge 1 commit into
base: main
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
2 changes: 1 addition & 1 deletion packages/micro-app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function MicroApp() {
}

return (
<div style={{background: "#555", padding: "12px"}}>
<div style={{background: "#555", padding: "12px", color: "white"}}>
<h2><code>@robincsl/micro-app</code></h2>
<p>The current value is: {value}</p>
<button onClick={increment(value,setValue)}>Increment</button>
Expand Down
30 changes: 30 additions & 0 deletions packages/nextjs-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
7 changes: 7 additions & 0 deletions packages/nextjs-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
For Next.js to correctly capture errors, we need to use Sentry.init in the root of the Next.js app, i.e. in `_app.js`. See for example http://localhost:3000/server/test1 :
- when using `initSentryHub.js`, the error is not reported to Sentry, despite making the hub the main hub.
- when using `initSentry.js`, the error is reported to Sentry

Using error boundaries with hubs for different pages seems to only work in the client side for now it seems. The server side errors are caught by the DSN for `initSentry.js`.

Reason is that we are using BrowserClient when initiliasing Hubs and they only make sense client-side. This concept of Hubs and Clients does not seem to be available in Sentry for Node.js... To be continued.
10 changes: 10 additions & 0 deletions packages/nextjs-app/initSentry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as Sentry from '@sentry/browser'

Sentry.init({
dsn: "http://467e9a03e4774033902d0c9015a722da@0.0.0.0:9000/7",
beforeSend(event) {
console.log("nextjs-app", event)
// Don't send the event to Sentry
return null
}
})
13 changes: 13 additions & 0 deletions packages/nextjs-app/initSentryHub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { makeMain } from '@sentry/hub'
import {init} from "@robincsl/sentry"

export const { hub } = init({
dsn: "http://467e9a03e4774033902d0c9015a722da@0.0.0.0:9000/7",
beforeSend(event) {
console.log("nextjs-app", event)
// Don't send the event to Sentry
return null
}
})

makeMain(hub)
26 changes: 26 additions & 0 deletions packages/nextjs-app/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// next.config.js
const withTM = require('next-transpile-modules')(['@robincsl/micro-app', '@robincsl/sentry']);

module.exports = withTM({
webpack: (config, options) => {
// In `pages/_app.js`, Sentry is imported from @sentry/node. While
// @sentry/browser 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'
}

return config
}
});
20 changes: 20 additions & 0 deletions packages/nextjs-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "nextjs-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@robincsl/micro-app": "*",
"@robincsl/sentry": "*",
"@sentry/browser": "^5.17.0",
"@sentry/node": "^5.17.0",
"next": "9.4.4",
"next-transpile-modules": "^3.3.0",
"react": "16.13.1",
"react-dom": "16.13.1"
}
}
43 changes: 43 additions & 0 deletions packages/nextjs-app/pages/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { hub } from '../initSentryHub'
import { ErrorBoundary }from '@robincsl/sentry'
import * as Sentry from "@sentry/node"
import {makeMain} from "@sentry/hub"

console.log(Sentry.getCurrentHub())
console.log(Sentry.getHubFromCarrier())
const old = makeMain(hub)
console.log({old})
console.log({hub})
// Sentry.init({
// dsn: "http://467e9a03e4774033902d0c9015a722da@0.0.0.0:9000/7",
// beforeSend(event) {
// console.log("nextjs-app", event)
// // Don't send the event to Sentry
// return null
// }
// })
export default function App({ Component, pageProps, err, defaultHub = hub }) {
if (Component.ErrorBoundary) {
{/* Workaround for https://github.com/vercel/next.js/issues/8592 */}
return Component.ErrorBoundary(<Component {...pageProps} err={err} defaultHub={defaultHub} />)
}

return (
<ErrorBoundary hub={hub}>
<Component {...pageProps} err={err} defaultHub={defaultHub} />
</ErrorBoundary>
)
}


// import '../initSentry'
// function App({ Component, pageProps, err }) {
// if (Component.ErrorBoundary) {
// // {/* Workaround for https://github.com/vercel/next.js/issues/8592 */}

// return Component.ErrorBoundary(<Component {...pageProps} err={err} />)
// }
// return <Component {...pageProps} />
// }

// export default App;
62 changes: 62 additions & 0 deletions packages/nextjs-app/pages/_errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import NextErrorComponent from 'next/error'
import * as Sentry from '@sentry/node'

const MyError = ({ statusCode, hasGetInitialPropsRun, err, defaultHub }) => {
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
defaultHub.run(currentHub => {
currentHub.withScope((scope) => {
scope.setExtras({some: 'info'});
currentHub.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)
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}`)
)

return errorInitialProps
}

export default MyError
Loading