未验证 提交 47e2beba 编写于 作者: T Todor Totev 提交者: GitHub

[Example] Deprecate with-sentry in favor of with-sentry-simple (#13904)

Co-authored-by: NLuis Alvarez D <luis@vercel.com>
Co-authored-by: NJoe Haddad <joe.haddad@zeit.co>
上级 2920af7c
[![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).
// 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
},
})
{
"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"
}
}
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 <Component {...pageProps} err={err} />
}
import Link from 'next/link'
const Index = () => (
<div style={{ maxWidth: 700, margin: '0 auto' }}>
<h2>Sentry Simple Example 🚨</h2>
<p>
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.
</p>
<p>
<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/advanced-features/custom-error-page#customizing-the-error-page">
Read more
</a>
</p>
<ul>
<li>Server exceptions</li>
<ul>
<li>
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>
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>
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>
</li>
<li>
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.{' '}
<a href="/server/test4" target="_blank">
Open in a new tab
</a>
</li>
</ul>
<li>Client exceptions</li>
<ul>
<li>
There is a top-of-module Promise that rejects, but its result is not
awaited. Sentry should record Error('Client Test 1').{' '}
<Link href="/client/test1">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<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/test2">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test2" target="_blank">
Open in a new tab
</a>
</li>
<li>
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 3') in Sentry.{' '}
<Link href="/client/test3">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<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 4').{' '}
<Link href="/client/test4">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<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 5').{' '}
<Link href="/client/test5">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test5" target="_blank">
Open in a new tab
</a>
</li>
</ul>
</ul>
</div>
)
export default Index
# 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=<dsn> node server.js",
"build": "SENTRY_DSN=<dsn> next build",
"start": "SENTRY_DSN=<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.
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
},
})
{
"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"
}
}
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 ? (
<section>
<h1>There was an error!</h1>
<p>
<a
href="#"
onClick={() =>
Sentry.showReportDialog({ eventId: this.state.errorEventId })
}
>
📣 Report this error
</a>
</p>
<p>
<a
href="#"
onClick={() => {
window.location.reload(true)
}}
>
Or, try reloading the page
</a>
</p>
</section>
) : (
// 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 <Component {...pageProps} err={err} />
}
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 (
<div>
<h2>Index page</h2>
const Index = () => (
<div style={{ maxWidth: 700, margin: '0 auto' }}>
<h2>Sentry Simple Example 🚨</h2>
<p>
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.
</p>
<p>
<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/advanced-features/custom-error-page#customizing-the-error-page">
Read more
</a>
</p>
<ul>
<li>Server exceptions</li>
<ul>
<li>
<a
href="#"
onClick={() =>
setState((current) => ({
...current,
raiseErrorInUseEffectHook: '1',
}))
}
>
Raise the error in render or update
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>
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>
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>
</li>
<li>
<Link href={{ pathname: '/', query: { raiseError: '1' } }}>
<a>Raise error in getServerSideProps</a>
</Link>
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.{' '}
<a href="/server/test4" target="_blank">
Open in a new tab
</a>
</li>
</ul>
</div>
)
}
export async function getServerSideProps({ query }) {
if (query.raiseError) {
throw new Error('Error in getServerSideProps')
}
return {
props: {},
}
}
<li>Client exceptions</li>
<ul>
<li>
There is a top-of-module Promise that rejects, but its result is not
awaited. Sentry should record Error('Client Test 1').{' '}
<Link href="/client/test1">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<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/test2">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test2" target="_blank">
Open in a new tab
</a>
</li>
<li>
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 3') in Sentry.{' '}
<Link href="/client/test3">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<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 4').{' '}
<Link href="/client/test4">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<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 5').{' '}
<Link href="/client/test5">
<a>Perform client side navigation</a>
</Link>{' '}
or{' '}
<a href="/client/test5" target="_blank">
Open in a new tab
</a>
</li>
</ul>
</ul>
</div>
)
export default Index
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}`)
})
})
// 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)
},
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册