-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
feat(nextjs): Instrument SSR page components #9346
Changes from all commits
91652f4
d81c001
6999c18
609b630
d01a554
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React from 'react'; | ||
|
||
export default class Page extends React.Component { | ||
render() { | ||
throw new Error('Pages SSR Error Class'); | ||
return <div>Hello world!</div>; | ||
} | ||
} | ||
|
||
export function getServerSideProps() { | ||
return { | ||
props: { | ||
foo: 'bar', | ||
}, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export default function Page() { | ||
throw new Error('Pages SSR Error FC'); | ||
return <div>Hello world!</div>; | ||
} | ||
|
||
export function getServerSideProps() { | ||
return { | ||
props: { | ||
foo: 'bar', | ||
}, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { test, expect } from '@playwright/test'; | ||
import { waitForError } from '../event-proxy-server'; | ||
|
||
test('Will capture error for SSR rendering error (Class Component)', async ({ page }) => { | ||
const errorEventPromise = waitForError('nextjs-13-app-dir', errorEvent => { | ||
return errorEvent?.exception?.values?.[0]?.value === 'Pages SSR Error Class'; | ||
}); | ||
|
||
await page.goto('/pages-router/ssr-error-class'); | ||
|
||
const errorEvent = await errorEventPromise; | ||
expect(errorEvent).toBeDefined(); | ||
}); | ||
|
||
test('Will capture error for SSR rendering error (Functional Component)', async ({ page }) => { | ||
const errorEventPromise = waitForError('nextjs-13-app-dir', errorEvent => { | ||
return errorEvent?.exception?.values?.[0]?.value === 'Pages SSR Error FC'; | ||
}); | ||
|
||
await page.goto('/pages-router/ssr-error-fc'); | ||
|
||
const errorEvent = await errorEventPromise; | ||
expect(errorEvent).toBeDefined(); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { captureException } from '@sentry/core'; | ||
import { addExceptionMechanism } from '@sentry/utils'; | ||
|
||
interface FunctionComponent { | ||
(...args: unknown[]): unknown; | ||
} | ||
|
||
interface ClassComponent { | ||
new (...args: unknown[]): { | ||
render(...args: unknown[]): unknown; | ||
}; | ||
} | ||
|
||
function isReactClassComponent(target: unknown): target is ClassComponent { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
return typeof target === 'function' && target?.prototype?.isReactComponent; | ||
} | ||
|
||
/** | ||
* Wraps a page component with Sentry error instrumentation. | ||
*/ | ||
export function wrapPageComponentWithSentry(pageComponent: FunctionComponent | ClassComponent): unknown { | ||
if (isReactClassComponent(pageComponent)) { | ||
return class SentryWrappedPageComponent extends pageComponent { | ||
public render(...args: unknown[]): unknown { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. l: I wonder if we can use suspense + error boundary to catch this - it's what is recommended with reactjs/rfcs#215 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SSR error boundaries are effectively only needed when you want to display a fallback in case of an error. I don't think we need this and I don't think this is possible in non React Server Component worlds but there's an excellent article on this: https://gal.hagever.com/posts/react-error-boundaries-and-ssr |
||
try { | ||
return super.render(...args); | ||
} catch (e) { | ||
captureException(e, scope => { | ||
scope.addEventProcessor(event => { | ||
addExceptionMechanism(event, { | ||
handled: false, | ||
}); | ||
return event; | ||
}); | ||
|
||
return scope; | ||
}); | ||
throw e; | ||
} | ||
} | ||
}; | ||
} else if (typeof pageComponent === 'function') { | ||
return new Proxy(pageComponent, { | ||
apply(target, thisArg, argArray) { | ||
try { | ||
return target.apply(thisArg, argArray); | ||
} catch (e) { | ||
captureException(e, scope => { | ||
scope.addEventProcessor(event => { | ||
addExceptionMechanism(event, { | ||
handled: false, | ||
}); | ||
return event; | ||
}); | ||
|
||
return scope; | ||
}); | ||
throw e; | ||
} | ||
}, | ||
}); | ||
} else { | ||
return pageComponent; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LETS GOO