Skip to content

Commit

Permalink
fix: log the error instance modified extra location info (#71930)
Browse files Browse the repository at this point in the history
  • Loading branch information
huozhi authored Oct 29, 2024
1 parent b2bc1ed commit 14b92e6
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 35 deletions.
43 changes: 20 additions & 23 deletions packages/next/src/client/react-client-callbacks/app-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { handleClientError } from '../components/react-dev-overlay/internal/help
import { isNextRouterError } from '../components/is-next-router-error'
import { isBailoutToCSRError } from '../../shared/lib/lazy-dynamic/bailout-to-csr'
import { reportGlobalError } from './report-global-error'
import isError from '../../lib/is-error'
import { originConsoleError } from '../components/globals/intercept-console-error'

export const onCaughtError: HydrationOptions['onCaughtError'] = (
Expand All @@ -16,9 +15,7 @@ export const onCaughtError: HydrationOptions['onCaughtError'] = (
// Skip certain custom errors which are not expected to be reported on client
if (isBailoutToCSRError(err) || isNextRouterError(err)) return

const stitchedError = getReactStitchedError(err)

if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV !== 'production') {
const errorBoundaryComponent = errorInfo?.errorBoundary?.constructor
const errorBoundaryName =
// read react component displayName
Expand All @@ -36,11 +33,6 @@ export const onCaughtError: HydrationOptions['onCaughtError'] = (
componentThatErroredFrame?.match(/\s+at (\w+)\s+|(\w+)@/) ?? []
const componentThatErroredName = matches[1] || matches[2] || 'Unknown'

// In development mode, pass along the component stack to the error
if (process.env.NODE_ENV === 'development' && errorInfo.componentStack) {
;(stitchedError as any)._componentStack = errorInfo.componentStack
}

// Create error location with errored component and error boundary, to match the behavior of default React onCaughtError handler.
const errorBoundaryMessage = `It was handled by the <${errorBoundaryName}> error boundary.`
const componentErrorMessage = componentThatErroredName
Expand All @@ -49,10 +41,16 @@ export const onCaughtError: HydrationOptions['onCaughtError'] = (

const errorLocation = `${componentErrorMessage} ${errorBoundaryMessage}`

const originErrorStack = isError(err) ? err.stack || '' : ''
const stitchedError = getReactStitchedError(err)
// TODO: change to passing down errorInfo later
// In development mode, pass along the component stack to the error
if (errorInfo.componentStack) {
;(stitchedError as any)._componentStack = errorInfo.componentStack
}

// Log and report the error with location but without modifying the error stack
originConsoleError('%o\n\n%s', err, errorLocation)

// Log the modified error message with stack so without being intercepted again.
originConsoleError(originErrorStack + '\n\n' + errorLocation)
handleClientError(stitchedError, [])
} else {
originConsoleError(err)
Expand All @@ -66,29 +64,28 @@ export const onUncaughtError: HydrationOptions['onUncaughtError'] = (
// Skip certain custom errors which are not expected to be reported on client
if (isBailoutToCSRError(err) || isNextRouterError(err)) return

const stitchedError = getReactStitchedError(err)

if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV !== 'production') {
const componentThatErroredFrame = errorInfo?.componentStack?.split('\n')[1]

// Match chrome or safari stack trace
const matches =
componentThatErroredFrame?.match(/\s+at (\w+)\s+|(\w+)@/) ?? []
const componentThatErroredName = matches[1] || matches[2] || 'Unknown'

// In development mode, pass along the component stack to the error
if (process.env.NODE_ENV === 'development' && errorInfo.componentStack) {
;(stitchedError as any)._componentStack = errorInfo.componentStack
}

// Create error location with errored component and error boundary, to match the behavior of default React onCaughtError handler.
const errorLocation = componentThatErroredName
? `The above error occurred in the <${componentThatErroredName}> component.`
: `The above error occurred in one of your components.`

const errStack = (stitchedError as any).stack || ''
// Log the modified error message with stack so without being intercepted again.
originConsoleError(errStack + '\n\n' + errorLocation)
const stitchedError = getReactStitchedError(err)
// TODO: change to passing down errorInfo later
// In development mode, pass along the component stack to the error
if (errorInfo.componentStack) {
;(stitchedError as any)._componentStack = errorInfo.componentStack
}

// Log and report the error with location but without modifying the error stack
originConsoleError('%o\n\n%s', err, errorLocation)
reportGlobalError(stitchedError)
} else {
reportGlobalError(err)
Expand Down
24 changes: 12 additions & 12 deletions test/development/app-dir/owner-stack/owner-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ describe('app-dir - owner-stack', () => {

if (process.env.TURBOPACK) {
expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: browser error
"%o
%s Error: browser error
at useThrowError
at useErrorHook
at Page
Expand All @@ -88,12 +89,12 @@ describe('app-dir - owner-stack', () => {
at renderRootSync
at performWorkOnRoot
at performWorkOnRootViaSchedulerTask
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
at MessagePort.performWorkUntilDeadline The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
`)
} else {
expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: browser error
"%o
%s Error: browser error
at useThrowError
at useErrorHook
at Page
Expand All @@ -107,8 +108,7 @@ describe('app-dir - owner-stack', () => {
at renderRootSync
at performWorkOnRoot
at performWorkOnRootViaSchedulerTask
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
at MessagePort.performWorkUntilDeadline The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
`)
}
})
Expand Down Expand Up @@ -144,7 +144,8 @@ describe('app-dir - owner-stack', () => {
}

expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: browser error
"%o
%s Error: browser error
at useThrowError
at useErrorHook
at Thrower
Expand All @@ -158,8 +159,7 @@ describe('app-dir - owner-stack', () => {
at renderRootSync
at performWorkOnRoot
at performWorkOnRootViaSchedulerTask
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Thrower> component. It was handled by the <MyErrorBoundary> error boundary."
at MessagePort.performWorkUntilDeadline The above error occurred in the <Thrower> component. It was handled by the <MyErrorBoundary> error boundary."
`)
})

Expand Down Expand Up @@ -187,7 +187,8 @@ describe('app-dir - owner-stack', () => {
}).message

expect(normalizeStackTrace(errorLog)).toMatchInlineSnapshot(`
"Error: ssr error
"%o
%s Error: ssr error
at useThrowError
at useErrorHook
at Page
Expand All @@ -201,8 +202,7 @@ describe('app-dir - owner-stack', () => {
at renderRootSync
at performWorkOnRoot
at performWorkOnRootViaSchedulerTask
at MessagePort.performWorkUntilDeadline
The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
at MessagePort.performWorkUntilDeadline The above error occurred in the <Page> component. It was handled by the <ReactDevOverlay> error boundary."
`)
})

Expand Down

0 comments on commit 14b92e6

Please sign in to comment.