未验证 提交 a1b46817 编写于 作者: L Luis Alvarez D 提交者: GitHub

[Examples] Update with-sentry-simple (#13074)

Moved from `getInitialProps` to the new data fetching methods. Updated the readme and simplified the implementation for pages.
上级 b3e52336
......@@ -2,6 +2,12 @@
# Sentry (Simple Example)
This is a simple example showing how to use [Sentry](https://sentry.io) to catch & report errors on both client + server side.
- `_app.js` renders on both the server and client. It initializes Sentry to catch any unhandled exceptions
- `_error.js` is rendered by Next.js while handling certain types of exceptions for you. It is overridden so those exceptions can be passed along to Sentry
- `next.config.js` enables source maps in production for Sentry and swaps out `@sentry/node` for `@sentry/browser` when building the client bundle
## How To Use
### Using `create-next-app`
......@@ -25,58 +31,42 @@ cd with-sentry-simple
Install it and run:
**NPM**
```bash
npm install
npm run dev
```
**Yarn**
```bash
# or
yarn
yarn dev
```
Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
## About Example
This is a simple example showing how to use [Sentry](https://sentry.io) to catch & report errors on both client + server side.
- `_app.js` renders on both the server and client. It initializes Sentry to catch any unhandled exceptions
- `_error.js` is rendered by Next.js while handling certain types of exceptions for you. It is overridden so those exceptions can be passed along to Sentry
- `next.config.js` enables source maps in production for Sentry and swaps out `@sentry/node` for `@sentry/browser` when building the client bundle
**Note**: By default, neither sourcemaps nor error tracking is enabled in development mode (see Configuration).
**Note**: When enabled in development mode, error handling [works differently than in production](https://nextjs.org/docs#custom-error-handling) as `_error.js` is never actually called.
**Note**: The build output will contain warning about unhandled Promise rejections. This caused by the test pages, and is expected.
**Note**: The version of `@zeit/next-source-maps` (`0.0.4-canary.1`) is important and must be specified since it is not yet the default. Otherwise [source maps will not be generated for the server](https://github.com/zeit/next-plugins/issues/377).
## Notes
**Note**: Both `@zeit/next-source-maps` and `@sentry/webpack-plugin` are added to dependencies (rather than `devDependencies`) is because if used with SSR (ex. heroku), these plugins are used during production for generating the source-maps and sending them to sentry.
- By default, neither sourcemaps nor error tracking is enabled in development mode (see Configuration).
- When enabled in development mode, error handling [works differently than in production](https://nextjs.org/docs#custom-error-handling) as `_error.js` is never actually called.
- The build output will contain warning about unhandled Promise rejections. This caused by the test pages, and is expected.
- The version of `@zeit/next-source-maps` (`0.0.4-canary.1`) is important and must be specified since it is not yet the default. Otherwise [source maps will not be generated for the server](https://github.com/zeit/next-plugins/issues/377).
- Both `@zeit/next-source-maps` and `@sentry/webpack-plugin` are added to dependencies (rather than `devDependencies`) is because if used with SSR, these plugins are used during production for generating the source-maps and sending them to sentry.
### Configuration
## Configuration
#### Error tracking
### Error tracking
1. Copy your Sentry DSN. You can get it from the settings of your project in **Client Keys (DSN)**. Then, copy the string labeled **DSN (Public)**.
2. Put the DSN inside the `SENTRY_DSN` environment variable.
2. Put the DSN inside the `SENTRY_DSN` environment variable inside a new environment file called `.env.local`
**Note:** Error tracking is disabled in development mode using the `NODE_ENV` environment variable. To change this behaviour, remove the `enabled` property from the `Sentry.init()` call inside your `_app.js` file. More details about how `NODE_ENV` is set in next deployments can be found [here](https://nextjs.org/docs#production-deployment).
> **Note:** Error tracking is disabled in development mode using the `NODE_ENV` environment variable. To change this behaviour, remove the `enabled` property from the `Sentry.init()` call inside your `_app.js` file.
#### Automatic sourcemap upload (optional)
### Automatic sourcemap upload (optional)
1. Set up the `SENTRY_DSN` environment variable as described above.
2. Save your Sentry Organization slug inside the `SENTRY_ORG` and your project slug inside the `SENTRY_PROJECT` environment variables.
3. Create an auth token in Sentry. The recommended way to do this is by creating a new internal integration for your organization. To do so, go into Settings > Developer Settings > New internal integration. After the integration is created, copy the Token.
4. Save the token inside the `SENTRY_AUTH_TOKEN` environment variable.
**Note:** Sourcemap upload is disabled in development mode using the `NODE_ENV` environment variable. To change this behaviour, remove the `NODE_ENV === 'production'` check from your `next.config.js` file. More details about how `NODE_ENV` is set in next deployments can be found [here](https://nextjs.org/docs#production-deployment).
> **Note:** Sourcemap upload is disabled in development mode using the `NODE_ENV` environment variable. To change this behaviour, remove the `NODE_ENV === 'production'` check from your `next.config.js` file.
#### Other configuration options
### Other configuration options
More configurations is available for [Sentry webpack plugin](https://github.com/getsentry/sentry-webpack-plugin) and using [Sentry Configuration variables](https://docs.sentry.io/cli/configuration/) for defining the releases/verbosity/etc.
More configurations are available for the [Sentry webpack plugin](https://github.com/getsentry/sentry-webpack-plugin) using [Sentry Configuration variables](https://docs.sentry.io/cli/configuration/) for defining the releases/verbosity/etc.
......@@ -5,17 +5,16 @@ const withSourceMaps = require('@zeit/next-source-maps')()
// Use the SentryWebpack plugin to upload the source maps during build step
const SentryWebpackPlugin = require('@sentry/webpack-plugin')
const {
SENTRY_DSN,
NEXT_PUBLIC_SENTRY_DSN: SENTRY_DSN,
SENTRY_ORG,
SENTRY_PROJECT,
SENTRY_AUTH_TOKEN,
NODE_ENV,
} = process.env
process.env.SENTRY_DSN = SENTRY_DSN
module.exports = withSourceMaps({
env: {
SENTRY_DSN: process.env.SENTRY_DSN,
},
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
......
import Error from 'next/error'
export default function NotFound() {
return <Error statusCode={404} />
}
import React from 'react'
import App from 'next/app'
import * as Sentry from '@sentry/node'
Sentry.init({
enabled: process.env.NODE_ENV === 'production',
dsn: process.env.SENTRY_DSN,
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
})
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
// Workaround for https://github.com/zeit/next.js/issues/8592
const { err } = this.props
const modifiedPageProps = { ...pageProps, err }
return <Component {...modifiedPageProps} />
}
export default function App({ Component, pageProps, err }) {
// Workaround for https://github.com/zeit/next.js/issues/8592
return <Component {...pageProps} err={err} />
}
export default MyApp
import React from 'react'
import Error from 'next/error'
import * as Sentry from '@sentry/node'
......@@ -20,37 +19,26 @@ MyError.getInitialProps = async ({ res, err, asPath }) => {
// getInitialProps has run
errorInitialProps.hasGetInitialPropsRun = true
if (res) {
// Running on the server, the response object is available.
//
// Next.js will pass an err on the server if a page's `getInitialProps`
// threw or returned a Promise that rejected
if (res.statusCode === 404) {
// Opinionated: do not record an exception in Sentry for 404
return { statusCode: 404 }
}
if (err) {
Sentry.captureException(err)
return errorInitialProps
}
} else {
// 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 (err) {
Sentry.captureException(err)
return errorInitialProps
}
// 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
......
import React from 'react'
const doAsyncWork = () => Promise.reject(new Error('Client Test 1'))
doAsyncWork()
const Test1 = () => <h1>Client Test 1</h1>
Test1.getInitialProps = () => {
throw new Error('Client Test 1')
}
export default Test1
import React from 'react'
// This code will run just fine on the server in Node.js, but process will be
// undefined in a browser. Note that `isProd = process.env.NODE_ENV` would have
// worked because Webpack's DefinePlugin will replace it with a string at build
// time
const env = process.env
const isProd = env.NODE_ENV === 'production'
const Test2 = () => <h1>Client Test 2</h1>
Test2.getInitialProps = () => Promise.reject(new Error('Client Test 2'))
const Test2 = () => (
<>
<h1>Client Test 2</h1>
<p>isProd: {isProd}</p>
</>
)
export default Test2
import React from 'react'
import { useEffect } from 'react'
const Test3 = () => <h1>Client Test 3</h1>
const Test3 = () => {
useEffect(() => {
throw new Error('Client Test 3')
}, [])
Test3.getInitialProps = () => {
const doAsyncWork = () => Promise.reject(new Error('Client Test 3'))
doAsyncWork()
return {}
return <h1>Client Test 3</h1>
}
export default Test3
import React from 'react'
import { useEffect } from 'react'
const doAsyncWork = () => Promise.reject(new Error('Client Test 4'))
doAsyncWork()
const Test4 = () => {
useEffect(function () {
async function doTest() {
const doAsyncWork = () => Promise.reject(new Error('Client Test 4'))
await doAsyncWork()
}
doTest()
}, [])
const Test4 = () => <h1>Client Test 4</h1>
return <h1>Client Test 4</h1>
}
export default Test4
import React from 'react'
// This code will run just fine on the server in Node.js, but process will be
// undefined in a browser. Note that `isProd = process.env.NODE_ENV` would have
// worked because Webpack's DefinePlugin will replace it with a string at build
// time: https://nextjs.org/docs#build-time-configuration
const env = process.env
const isProd = env.NODE_ENV === 'production'
const Test5 = () => (
<React.Fragment>
<>
<h1>Client Test 5</h1>
<p>isProd: {isProd}</p>
</React.Fragment>
<button
onClick={() => {
throw new Error('Client Test 5')
}}
>
Click me to throw an Error
</button>
</>
)
export default Test5
import React from 'react'
const Test6 = () => {
React.useEffect(() => {
throw new Error('Client Test 6')
}, [])
return <h1>Client Test 6</h1>
}
export default Test6
import React from 'react'
const Test7 = () => {
React.useEffect(function () {
async function doTest() {
const doAsyncWork = () => Promise.reject(new Error('Client Test 7'))
const result = await doAsyncWork()
console.log(result)
}
doTest()
}, [])
return <h1>Client Test 7</h1>
}
export default Test7
import React from 'react'
const Test8 = () => (
<React.Fragment>
<h1>Client Test 8</h1>
<button
onClick={() => {
throw new Error('Client Test 8')
}}
>
Click me to throw an Error
</button>
</React.Fragment>
)
export default Test8
......@@ -13,29 +13,31 @@ const Index = () => (
<strong>Important:</strong> exceptions in development mode take a
different path than in production. These tests should be run on a
production build (i.e. 'next build').{' '}
<a href="https://nextjs.org/docs#custom-error-handling">Read more</a>
<a href="https://nextjs.org/docs/advanced-features/custom-error-page#customizing-the-error-page">
Read more
</a>
</p>
<ul>
<li>Server exceptions</li>
<ul>
<li>
getInitialProps throws an Error. This should cause _error.js to render
and record Error('Client Test 1') in Sentry.{' '}
getServerSideProps throws an Error. This should cause _error.js to
render and record Error('Server Test 1') in Sentry.{' '}
<a href="/server/test1" target="_blank">
Open in a new tab
</a>
</li>
<li>
getInitialProps returns a Promise that rejects. This should cause
getServerSideProps returns a Promise that rejects. This should cause
_error.js to render and record Error('Server Test 2') in Sentry.{' '}
<a href="/server/test2" target="_blank">
Open in a new tab
</a>
</li>
<li>
getInitialProps calls a Promise that rejects, but does not handle the
rejection or await its result (returning synchronously). Sentry should
record Error('Server Test 3').{' '}
getServerSideProps calls a Promise that rejects, but does not handle
the rejection or await its result (returning synchronously). Sentry
should record Error('Server Test 3').{' '}
<a href="/server/test3" target="_blank">
Open in a new tab
</a>
......@@ -52,51 +54,25 @@ const Index = () => (
<li>Client exceptions</li>
<ul>
<li>
getInitialProps throws an Error. This should cause _error.js to render
and record Error('Client Test 1') in Sentry. Note Sentry will double
count this exception. Once from an unhandledrejection and again in
_error.js. Could be a bug in Next.js or Sentry, requires more
debugging.{' '}
<Link href="/client/test1">
<a>Perform client side navigation</a>
</Link>
</li>
<li>
getInitialProps returns a Promise that rejects. This should cause
_error.js to render and record Error('Client Test 2') in Sentry. As
above, Sentry will double count this exception.{' '}
<Link href="/client/test2">
<a>Perform client side navigation</a>
</Link>
</li>
<li>
getInitialProps calls a Promise that rejects, but does not handle the
rejection or await its result (returning synchronously). Sentry should
record Error('Client Test 3').{' '}
<Link href="/client/test3">
<a>Perform client side navigation</a>
</Link>
</li>
<li>
There is a top-of-module Promise that rejects, but its result is not
awaited. Sentry should record Error('Client Test 4').{' '}
<Link href="/client/test4">
awaited. Sentry should record Error('Client Test 1').{' '}
<Link href="/client/test1">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test4" target="_blank">
<a href="/client/test1" target="_blank">
Open in a new tab
</a>
</li>
<li>
There is a top-of-module exception. _error.js should render and record
ReferenceError('process is not defined') in Sentry.{' '}
<Link href="/client/test5">
<Link href="/client/test2">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test5" target="_blank">
<a href="/client/test2" target="_blank">
Open in a new tab
</a>
</li>
......@@ -104,35 +80,35 @@ const Index = () => (
There is an exception during React lifecycle that is caught by
Next.js's React Error Boundary. In this case, when the component
mounts. This should cause _error.js to render and record Error('Client
Test 6') in Sentry.{' '}
<Link href="/client/test6">
Test 3') in Sentry.{' '}
<Link href="/client/test3">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test6" target="_blank">
<a href="/client/test3" target="_blank">
Open in a new tab
</a>
</li>
<li>
There is an unhandled Promise rejection during React lifecycle. In
this case, when the component mounts. Sentry should record
Error('Client Test 7').{' '}
<Link href="/client/test7">
Error('Client Test 4').{' '}
<Link href="/client/test4">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test7" target="_blank">
<a href="/client/test4" target="_blank">
Open in a new tab
</a>
</li>
<li>
An Error is thrown from an event handler. Sentry should record
Error('Client Test 8').{' '}
<Link href="/client/test8">
Error('Client Test 5').{' '}
<Link href="/client/test5">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test8" target="_blank">
<a href="/client/test5" target="_blank">
Open in a new tab
</a>
</li>
......
import React from 'react'
const Test1 = () => <h1>Server Test 1</h1>
Test1.getInitialProps = () => {
export function getServerSideProps() {
throw new Error('Server Test 1')
}
......
import React from 'react'
const Test2 = () => <h1>Server Test 2</h1>
Test2.getInitialProps = () => Promise.reject(new Error('Server Test 2'))
export async function getServerSideProps() {
throw new Error('Server Test 2')
}
export default Test2
import React from 'react'
const Test3 = () => <h1>Server Test 3</h1>
Test3.getInitialProps = () => {
export async function getServerSideProps() {
const doAsyncWork = () => Promise.reject(new Error('Server Test 3'))
doAsyncWork()
return {}
return { props: {} }
}
export default Test3
import React from 'react'
const doAsyncWork = () => Promise.reject(new Error('Server Test 4'))
doAsyncWork()
const Test4 = () => <h1>Server Test 4</h1>
// Define getInitialProps so that the page will be rendered on the server
// instead of statically
Test4.getInitialProps = () => {
return {}
// Define getServerSideProps so that the page will be server rendered
// instead of statically generated
export async function getServerSideProps() {
return { props: {} }
}
export default Test4
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册