diff --git a/src/renderer/components/error-boundary/error-boundary.tsx b/src/renderer/components/error-boundary/error-boundary.tsx index d2b0ef90c5..764ff798d8 100644 --- a/src/renderer/components/error-boundary/error-boundary.tsx +++ b/src/renderer/components/error-boundary/error-boundary.tsx @@ -24,6 +24,13 @@ import "./error-boundary.scss"; import React, { ErrorInfo } from "react"; import { reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; +import { + captureEvent, + captureException, + eventFromException, +} from "@sentry/browser"; +import type { Event } from "@sentry/types"; +import { parseSemver } from "@sentry/utils"; import { Button } from "../button"; import { navigation } from "../../navigation"; import { issuesTrackerUrl, slackUrl } from "../../../common/vars"; @@ -36,6 +43,50 @@ interface State { errorInfo?: ErrorInfo; } +const reactVersion = parseSemver(React.version); + +/** + * Logs react error boundary errors to Sentry. + * + * @param error An error captured by React Error Boundary + * @param componentStack The component stacktrace + * + * edited from https://github.com/getsentry/sentry-javascript/blob/master/packages/react/src/errorboundary.tsx + */ +function captureReactErrorBoundaryError(error: Error, componentStack: string) { + const errorBoundaryError = new Error(error.message); + + errorBoundaryError.name = `React ErrorBoundary ${errorBoundaryError.name}`; + errorBoundaryError.stack = componentStack; + + let errorBoundaryEvent: Event = {}; + + void eventFromException({}, errorBoundaryError).then(e => { + errorBoundaryEvent = e; + }); + + if ( + errorBoundaryEvent.exception && + Array.isArray(errorBoundaryEvent.exception.values) && + reactVersion.major && + reactVersion.major >= 17 + ) { + let originalEvent: Event = {}; + + void eventFromException({}, error).then(e => { + originalEvent = e; + }); + + if (originalEvent.exception && Array.isArray(originalEvent.exception.values)) { + originalEvent.exception.values = [...errorBoundaryEvent.exception.values, ...originalEvent.exception.values]; + } + + return captureEvent(originalEvent); + } + + return captureException(error, { contexts: { react: { componentStack } } }); +} + @observer export class ErrorBoundary extends React.Component { public state: State = {}; @@ -47,6 +98,7 @@ export class ErrorBoundary extends React.Component { ); componentDidCatch(error: Error, errorInfo: ErrorInfo) { + captureReactErrorBoundaryError(error, errorInfo.componentStack); this.setState({ error, errorInfo }); } @@ -63,7 +115,7 @@ export class ErrorBoundary extends React.Component { const pageUrl = location.pathname; return ( -
+
App crash at {pageUrl}