From b5895ec81ff741aa4d5d539d316838b171d4db04 Mon Sep 17 00:00:00 2001 From: Todor Totev <51530311+TodorTotev@users.noreply.github.com> Date: Tue, 9 Jun 2020 04:32:23 +0300 Subject: [PATCH] [Example] Deprecate with-sentry in favor of with-sentry-simple (#13904) Co-authored-by: Luis Alvarez D Co-authored-by: Joe Haddad --- examples/with-sentry-simple/README.md | 73 +-------- examples/with-sentry-simple/next.config.js | 61 -------- examples/with-sentry-simple/package.json | 19 --- examples/with-sentry-simple/pages/_app.js | 11 -- examples/with-sentry-simple/pages/index.js | 119 -------------- examples/with-sentry/README.md | 69 ++++---- examples/with-sentry/next.config.js | 66 ++++++-- examples/with-sentry/package.json | 29 ++-- .../pages/404.js | 0 examples/with-sentry/pages/_app.js | 93 +---------- .../pages/_error.js | 0 .../pages/client/test1.js | 0 .../pages/client/test2.js | 0 .../pages/client/test3.js | 0 .../pages/client/test4.js | 0 .../pages/client/test5.js | 0 examples/with-sentry/pages/index.js | 147 +++++++++++++----- .../pages/server/test1.js | 0 .../pages/server/test2.js | 0 .../pages/server/test3.js | 0 .../pages/server/test4.js | 0 examples/with-sentry/server.js | 61 -------- examples/with-sentry/utils/sentry.js | 87 ----------- 23 files changed, 224 insertions(+), 611 deletions(-) delete mode 100644 examples/with-sentry-simple/next.config.js delete mode 100644 examples/with-sentry-simple/package.json delete mode 100644 examples/with-sentry-simple/pages/_app.js delete mode 100644 examples/with-sentry-simple/pages/index.js rename examples/{with-sentry-simple => with-sentry}/pages/404.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/_error.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/client/test1.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/client/test2.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/client/test3.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/client/test4.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/client/test5.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/server/test1.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/server/test2.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/server/test3.js (100%) rename examples/{with-sentry-simple => with-sentry}/pages/server/test4.js (100%) delete mode 100644 examples/with-sentry/server.js delete mode 100644 examples/with-sentry/utils/sentry.js diff --git a/examples/with-sentry-simple/README.md b/examples/with-sentry-simple/README.md index 3c3eab34289dd6..b4e5fa4786a881 100644 --- a/examples/with-sentry-simple/README.md +++ b/examples/with-sentry-simple/README.md @@ -1,72 +1 @@ -[![Deploy To Now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/vercel/next.js/tree/master/examples/with-sentry-simple) - -# 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` - -Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: - -```bash -npx create-next-app --example with-sentry-simple with-sentry-simple -# or -yarn create next-app --example with-sentry-simple with-sentry-simple -``` - -### Download Manually - -Download the example: - -```bash -curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-sentry-simple -cd with-sentry-simple -``` - -Install it and run: - -```bash -npm install -npm run dev -# 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)). - -## Notes - -- 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 is 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`) because if used with SSR, these plugins are used during production for generating the source-maps and sending them to sentry. - -## Configuration - -### 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 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. - -### 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. - -### Other configuration options - -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. +This example has been moved to [examples/with-sentry](../with-sentry). diff --git a/examples/with-sentry-simple/next.config.js b/examples/with-sentry-simple/next.config.js deleted file mode 100644 index e93b8cb9e444b1..00000000000000 --- a/examples/with-sentry-simple/next.config.js +++ /dev/null @@ -1,61 +0,0 @@ -// Use the hidden-source-map option when you don't want the source maps to be -// publicly available on the servers, only to the error reporting -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 { - 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({ - 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#customizing-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'], - urlPrefix: '~/_next', - release: options.buildId, - }) - ) - } - - return config - }, -}) diff --git a/examples/with-sentry-simple/package.json b/examples/with-sentry-simple/package.json deleted file mode 100644 index 2247484221ae09..00000000000000 --- a/examples/with-sentry-simple/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "with-sentry-simple", - "version": "1.0.0", - "license": "ISC", - "scripts": { - "dev": "next", - "build": "next build", - "start": "next start" - }, - "dependencies": { - "@sentry/browser": "^5.15.5", - "@sentry/node": "^5.15.5", - "@sentry/webpack-plugin": "^1.11.1", - "@zeit/next-source-maps": "0.0.4-canary.1", - "next": "latest", - "react": "^16.8.6", - "react-dom": "^16.8.6" - } -} diff --git a/examples/with-sentry-simple/pages/_app.js b/examples/with-sentry-simple/pages/_app.js deleted file mode 100644 index 93e2a2a412c5cc..00000000000000 --- a/examples/with-sentry-simple/pages/_app.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from '@sentry/node' - -Sentry.init({ - enabled: process.env.NODE_ENV === 'production', - dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, -}) - -export default function App({ Component, pageProps, err }) { - // Workaround for https://github.com/vercel/next.js/issues/8592 - return -} diff --git a/examples/with-sentry-simple/pages/index.js b/examples/with-sentry-simple/pages/index.js deleted file mode 100644 index 9bf62707dee96b..00000000000000 --- a/examples/with-sentry-simple/pages/index.js +++ /dev/null @@ -1,119 +0,0 @@ -import Link from 'next/link' - -const Index = () => ( -
-

Sentry Simple Example 🚨

-

- This example demonstrates how to record unhandled exceptions in your code - with Sentry. There are several test pages below that result in various - kinds of unhandled exceptions. -

-

- Important: exceptions in development mode take a - different path than in production. These tests should be run on a - production build (i.e. 'next build').{' '} - - Read more - -

-
    -
  • Server exceptions
  • -
      -
    • - getServerSideProps throws an Error. This should cause _error.js to - render and record Error('Server Test 1') in Sentry.{' '} - - Open in a new tab - -
    • -
    • - getServerSideProps returns a Promise that rejects. This should cause - _error.js to render and record Error('Server Test 2') in Sentry.{' '} - - Open in a new tab - -
    • -
    • - 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').{' '} - - Open in a new tab - -
    • -
    • - There is a top-of-module Promise that rejects, but its result is not - awaited. Sentry should record Error('Server Test 4'). Note this will - also be recorded on the client side, once the page is hydrated.{' '} - - Open in a new tab - -
    • -
    - -
  • Client exceptions
  • - -
-
-) - -export default Index diff --git a/examples/with-sentry/README.md b/examples/with-sentry/README.md index dacecbd1da84e0..5ab3fbf3b5744c 100644 --- a/examples/with-sentry/README.md +++ b/examples/with-sentry/README.md @@ -1,55 +1,72 @@ -# Sentry example +[![Deploy To Now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/vercel/next.js/tree/master/examples/with-sentry) -An example showing use of [Sentry](https://sentry.io) to catch & report errors on both client + server side. +# Sentry -## How to use +This is a 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` Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: ```bash -npx create-next-app --example with-sentry with-sentry-app +npx create-next-app --example with-sentry with-sentry # or -yarn create next-app --example with-sentry with-sentry-app +yarn create next-app --example with-sentry with-sentry ``` -### Download manually +### Download Manually Download the example: -Install it and run: - -**npm** - ```bash -npm install -npm run dev +curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-sentry +cd with-sentry ``` -**yarn** +Install it and run: ```bash +npm install +npm run dev +# 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)). -### Configuration +## Notes -You will need a _Sentry DSN_ for your project. You can get it from the Settings of your Project, in **Client Keys (DSN)**, and copy the string labeled **DSN (Public)**. +- 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 is 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`) because if used with SSR, these plugins are used during production for generating the source-maps and sending them to sentry. -The Sentry DSN should then be added as an environment variable when running the `dev`, `build`, and `start` scripts in `package.json`: +## Configuration -```bash -{ - "scripts": { - "dev": "SENTRY_DSN= node server.js", - "build": "SENTRY_DSN= next build", - "start": "SENTRY_DSN= NODE_ENV=production node server.js" - } -} -``` +### 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 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. + +### 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. + +### Other configuration options -_Note: Setting environment variables in a `package.json` is not secure, it is done here only for demo purposes. See the [`with-dotenv`](../with-dotenv) example for an example of how to set environment variables safely._ +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. diff --git a/examples/with-sentry/next.config.js b/examples/with-sentry/next.config.js index 2e6f677554d52d..e93b8cb9e444b1 100644 --- a/examples/with-sentry/next.config.js +++ b/examples/with-sentry/next.config.js @@ -1,21 +1,61 @@ -const webpack = require('webpack') -const nextSourceMaps = require('@zeit/next-source-maps')() +// Use the hidden-source-map option when you don't want the source maps to be +// publicly available on the servers, only to the error reporting +const withSourceMaps = require('@zeit/next-source-maps')() -module.exports = nextSourceMaps({ - env: { - SENTRY_DSN: process.env.SENTRY_DSN, - }, - webpack: (config, { isServer, buildId }) => { - config.plugins.push( - new webpack.DefinePlugin({ - 'process.env.SENTRY_RELEASE': JSON.stringify(buildId), - }) - ) +// 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, +} = process.env + +process.env.SENTRY_DSN = SENTRY_DSN - if (!isServer) { +module.exports = withSourceMaps({ + 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#customizing-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'], + urlPrefix: '~/_next', + release: options.buildId, + }) + ) + } + return config }, }) diff --git a/examples/with-sentry/package.json b/examples/with-sentry/package.json index 6ffd21552af4e4..2247484221ae09 100644 --- a/examples/with-sentry/package.json +++ b/examples/with-sentry/package.json @@ -1,24 +1,19 @@ { - "name": "with-sentry", - "version": "1.1.0", + "name": "with-sentry-simple", + "version": "1.0.0", + "license": "ISC", "scripts": { - "dev": "SENTRY_DSN=abc123 node server.js", - "build": "SENTRY_DSN=abc123 next build", - "start": "SENTRY_DSN=abc123 NODE_ENV=production node server.js" + "dev": "next", + "build": "next build", + "start": "next start" }, "dependencies": { - "@sentry/browser": "^5.0.3", - "@sentry/integrations": "^5.0.3", - "@sentry/node": "^5.0.3", + "@sentry/browser": "^5.15.5", + "@sentry/node": "^5.15.5", + "@sentry/webpack-plugin": "^1.11.1", "@zeit/next-source-maps": "0.0.4-canary.1", - "cookie-parser": "1.4.4", - "express": "^4.16.4", - "js-cookie": "2.2.0", "next": "latest", - "react": "16.13.1", - "react-dom": "16.13.1", - "sentry-testkit": "^2.1.0", - "uuid": "^3.3.2" - }, - "license": "ISC" + "react": "^16.8.6", + "react-dom": "^16.8.6" + } } diff --git a/examples/with-sentry-simple/pages/404.js b/examples/with-sentry/pages/404.js similarity index 100% rename from examples/with-sentry-simple/pages/404.js rename to examples/with-sentry/pages/404.js diff --git a/examples/with-sentry/pages/_app.js b/examples/with-sentry/pages/_app.js index 44f60b2efe61b9..93e2a2a412c5cc 100644 --- a/examples/with-sentry/pages/_app.js +++ b/examples/with-sentry/pages/_app.js @@ -1,88 +1,11 @@ -import App from 'next/app' -import sentry from '../utils/sentry' +import * as Sentry from '@sentry/node' -const { Sentry, captureException } = sentry() +Sentry.init({ + enabled: process.env.NODE_ENV === 'production', + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, +}) -export default class MyApp extends App { - constructor() { - super(...arguments) - this.state = { - hasError: false, - errorEventId: undefined, - } - } - - static async getInitialProps({ Component, ctx }) { - try { - let pageProps = {} - - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx) - } - - return { pageProps } - } catch (error) { - // Capture errors that happen during a page's getInitialProps. - // This will work on both client and server sides. - const errorEventId = captureException(error, ctx) - return { - hasError: true, - errorEventId, - } - } - } - - static getDerivedStateFromProps(props, state) { - // If there was an error generated within getInitialProps, and we haven't - // yet seen an error, we add it to this.state here - return { - hasError: props.hasError || state.hasError || false, - errorEventId: props.errorEventId || state.errorEventId || undefined, - } - } - - static getDerivedStateFromError() { - // React Error Boundary here allows us to set state flagging the error (and - // later render a fallback UI). - return { hasError: true } - } - - componentDidCatch(error, errorInfo) { - const errorEventId = captureException(error, { errorInfo }) - - // Store the event id at this point as we don't have access to it within - // `getDerivedStateFromError`. - this.setState({ errorEventId }) - } - - render() { - return this.state.hasError ? ( -
-

There was an error!

-

- - Sentry.showReportDialog({ eventId: this.state.errorEventId }) - } - > - 📣 Report this error - -

-

- { - window.location.reload(true) - }} - > - Or, try reloading the page - -

-
- ) : ( - // Render the normal Next.js page - super.render() - ) - } +export default function App({ Component, pageProps, err }) { + // Workaround for https://github.com/vercel/next.js/issues/8592 + return } diff --git a/examples/with-sentry-simple/pages/_error.js b/examples/with-sentry/pages/_error.js similarity index 100% rename from examples/with-sentry-simple/pages/_error.js rename to examples/with-sentry/pages/_error.js diff --git a/examples/with-sentry-simple/pages/client/test1.js b/examples/with-sentry/pages/client/test1.js similarity index 100% rename from examples/with-sentry-simple/pages/client/test1.js rename to examples/with-sentry/pages/client/test1.js diff --git a/examples/with-sentry-simple/pages/client/test2.js b/examples/with-sentry/pages/client/test2.js similarity index 100% rename from examples/with-sentry-simple/pages/client/test2.js rename to examples/with-sentry/pages/client/test2.js diff --git a/examples/with-sentry-simple/pages/client/test3.js b/examples/with-sentry/pages/client/test3.js similarity index 100% rename from examples/with-sentry-simple/pages/client/test3.js rename to examples/with-sentry/pages/client/test3.js diff --git a/examples/with-sentry-simple/pages/client/test4.js b/examples/with-sentry/pages/client/test4.js similarity index 100% rename from examples/with-sentry-simple/pages/client/test4.js rename to examples/with-sentry/pages/client/test4.js diff --git a/examples/with-sentry-simple/pages/client/test5.js b/examples/with-sentry/pages/client/test5.js similarity index 100% rename from examples/with-sentry-simple/pages/client/test5.js rename to examples/with-sentry/pages/client/test5.js diff --git a/examples/with-sentry/pages/index.js b/examples/with-sentry/pages/index.js index 1025ad286186a8..9bf62707dee96b 100644 --- a/examples/with-sentry/pages/index.js +++ b/examples/with-sentry/pages/index.js @@ -1,52 +1,119 @@ -import { useEffect, useState } from 'react' import Link from 'next/link' -const Index = () => { - const [state, setState] = useState({ - raiseErrorInUseEffectHook: null, - }) - - useEffect(() => { - if (state.raiseErrorInUseEffectHook) { - throw new Error('Error in useEffect Hook') - } - }, [state.raiseErrorInUseEffectHook]) - - return ( -
-

Index page

+const Index = () => ( +
+

Sentry Simple Example 🚨

+

+ This example demonstrates how to record unhandled exceptions in your code + with Sentry. There are several test pages below that result in various + kinds of unhandled exceptions. +

+

+ Important: exceptions in development mode take a + different path than in production. These tests should be run on a + production build (i.e. 'next build').{' '} + + Read more + +

+
- ) -} -export async function getServerSideProps({ query }) { - if (query.raiseError) { - throw new Error('Error in getServerSideProps') - } - - return { - props: {}, - } -} +
  • Client exceptions
  • + + +
    +) export default Index diff --git a/examples/with-sentry-simple/pages/server/test1.js b/examples/with-sentry/pages/server/test1.js similarity index 100% rename from examples/with-sentry-simple/pages/server/test1.js rename to examples/with-sentry/pages/server/test1.js diff --git a/examples/with-sentry-simple/pages/server/test2.js b/examples/with-sentry/pages/server/test2.js similarity index 100% rename from examples/with-sentry-simple/pages/server/test2.js rename to examples/with-sentry/pages/server/test2.js diff --git a/examples/with-sentry-simple/pages/server/test3.js b/examples/with-sentry/pages/server/test3.js similarity index 100% rename from examples/with-sentry-simple/pages/server/test3.js rename to examples/with-sentry/pages/server/test3.js diff --git a/examples/with-sentry-simple/pages/server/test4.js b/examples/with-sentry/pages/server/test4.js similarity index 100% rename from examples/with-sentry-simple/pages/server/test4.js rename to examples/with-sentry/pages/server/test4.js diff --git a/examples/with-sentry/server.js b/examples/with-sentry/server.js deleted file mode 100644 index a4911ca3f62451..00000000000000 --- a/examples/with-sentry/server.js +++ /dev/null @@ -1,61 +0,0 @@ -const next = require('next') -const express = require('express') -const cookieParser = require('cookie-parser') -const uuidv4 = require('uuid/v4') -const port = parseInt(process.env.PORT, 10) || 3000 -const dev = process.env.NODE_ENV !== 'production' -const app = next({ dev }) -const handler = app.getRequestHandler() - -function sessionCookie(req, res, next) { - const htmlPage = - !req.path.match(/^\/(_next|static)/) && - !req.path.match(/\.(js|map)$/) && - req.accepts('text/html', 'text/css', 'image/png') === 'text/html' - - if (!htmlPage) { - next() - return - } - - if (!req.cookies.sid || req.cookies.sid.length === 0) { - req.cookies.sid = uuidv4() - res.cookie('sid', req.cookies.sid) - } - - next() -} - -const sourcemapsForSentryOnly = (token) => (req, res, next) => { - // In production we only want to serve source maps for Sentry - if (!dev && !!token && req.headers['x-sentry-token'] !== token) { - res - .status(401) - .send('Authentication access token is required to access the source map.') - return - } - next() -} - -app.prepare().then(() => { - // app.buildId is only available after app.prepare(), hence why we setup here - const { Sentry } = require('./utils/sentry')(app.buildId) - - express() - // This attaches request information to Sentry errors - .use(Sentry.Handlers.requestHandler()) - .use(cookieParser()) - .use(sessionCookie) - .get(/\.map$/, sourcemapsForSentryOnly(process.env.SENTRY_TOKEN)) - // Regular next.js request handler - .use(handler) - // This handles errors if they are thrown before reaching the app - .use(Sentry.Handlers.errorHandler()) - .listen(port, (err) => { - if (err) { - throw err - } - // eslint-disable-next-line no-console - console.log(`> Ready on http://localhost:${port}`) - }) -}) diff --git a/examples/with-sentry/utils/sentry.js b/examples/with-sentry/utils/sentry.js deleted file mode 100644 index 345311fd036de8..00000000000000 --- a/examples/with-sentry/utils/sentry.js +++ /dev/null @@ -1,87 +0,0 @@ -// NOTE: This require will be replaced with `@sentry/browser` -// client side thanks to the webpack config in next.config.js -const Sentry = require('@sentry/node') -const SentryIntegrations = require('@sentry/integrations') -const Cookie = require('js-cookie') - -module.exports = (release = process.env.SENTRY_RELEASE) => { - const sentryOptions = { - dsn: process.env.SENTRY_DSN, - release, - maxBreadcrumbs: 50, - attachStacktrace: true, - } - - // When we're developing locally - if (process.env.NODE_ENV !== 'production') { - // Don't actually send the errors to Sentry - sentryOptions.beforeSend = () => null - - // Instead, dump the errors to the console - sentryOptions.integrations = [ - new SentryIntegrations.Debug({ - // Trigger DevTools debugger instead of using console.log - debugger: false, - }), - ] - } - - Sentry.init(sentryOptions) - - return { - Sentry, - captureException: (err, ctx) => { - Sentry.configureScope((scope) => { - if (err.message) { - // De-duplication currently doesn't work correctly for SSR / browser errors - // so we force deduplication by error message if it is present - scope.setFingerprint([err.message]) - } - - if (err.statusCode) { - scope.setExtra('statusCode', err.statusCode) - } - - if (ctx) { - const { req, res, errorInfo, query, pathname } = ctx - - if (res && res.statusCode) { - scope.setExtra('statusCode', res.statusCode) - } - - if (typeof window !== 'undefined') { - scope.setTag('ssr', false) - scope.setExtra('query', query) - scope.setExtra('pathname', pathname) - - // On client-side we use js-cookie package to fetch it - const sessionId = Cookie.get('sid') - if (sessionId) { - scope.setUser({ id: sessionId }) - } - } else { - scope.setTag('ssr', true) - scope.setExtra('url', req.url) - scope.setExtra('method', req.method) - scope.setExtra('headers', req.headers) - scope.setExtra('params', req.params) - scope.setExtra('query', req.query) - - // On server-side we take session cookie directly from request - if (req.cookies.sid) { - scope.setUser({ id: req.cookies.sid }) - } - } - - if (errorInfo) { - Object.keys(errorInfo).forEach((key) => - scope.setExtra(key, errorInfo[key]) - ) - } - } - }) - - return Sentry.captureException(err) - }, - } -}