From 3defb863b35b707cfe719b334030a6c66a252261 Mon Sep 17 00:00:00 2001 From: James Salter Date: Fri, 29 Oct 2021 09:34:25 +0100 Subject: [PATCH] Automatic error reporting (#7046) * Enable sentry global handlers if automaticErrorReporting is on * Pass the exception through on session restore error Passing the exception object itself through to the BugReportDialog means a stack trace can be correctly recorded in Sentry --- src/Lifecycle.ts | 5 +++- .../dialogs/SessionRestoreErrorDialog.tsx | 6 ++-- .../tabs/user/LabsUserSettingsTab.tsx | 1 + src/i18n/strings/en_EN.json | 1 + src/sentry.ts | 30 +++++++++++++------ src/settings/Settings.tsx | 6 ++++ 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index d351c46180..d53bd39c95 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -59,6 +59,7 @@ import SessionRestoreErrorDialog from "./components/views/dialogs/SessionRestore import StorageEvictedDialog from "./components/views/dialogs/StorageEvictedDialog"; import { logger } from "matrix-js-sdk/src/logger"; +import { setSentryUser } from "./sentry"; const HOMESERVER_URL_KEY = "mx_hs_url"; const ID_SERVER_URL_KEY = "mx_is_url"; @@ -455,7 +456,7 @@ async function handleLoadSessionFailure(e: Error): Promise { logger.error("Unable to load session", e); const modal = Modal.createTrackedDialog('Session Restore Error', '', SessionRestoreErrorDialog, { - error: e.message, + error: e, }); const [success] = await modal.finished; @@ -582,6 +583,8 @@ async function doSetLoggedIn( PosthogAnalytics.instance.updateAnonymityFromSettings(credentials.userId); + setSentryUser(credentials.userId); + const client = MatrixClientPeg.get(); if (credentials.freshLogin && SettingsStore.getValue("feature_dehydration")) { diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx index b36dbf548e..d8f2b93af0 100644 --- a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx +++ b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx @@ -28,13 +28,15 @@ import DialogButtons from "../elements/DialogButtons"; import { IDialogProps } from "./IDialogProps"; interface IProps extends IDialogProps { - error: string; + error: Error; } @replaceableComponent("views.dialogs.SessionRestoreErrorDialog") export default class SessionRestoreErrorDialog extends React.Component { private sendBugReport = (): void => { - Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {}); + Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, { + error: this.props.error, + }); }; private onClearStorageClick = (): void => { diff --git a/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx b/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx index d05a11483c..4b8c8f8c40 100644 --- a/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx @@ -92,6 +92,7 @@ export default class LabsUserSettingsTab extends React.Component<{}, IState> { + { hiddenReadReceipts } ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1fffc04696..178d3214fb 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -890,6 +890,7 @@ "Display Communities instead of Spaces": "Display Communities instead of Spaces", "Temporarily show communities instead of Spaces for this session. Support for this will be removed in the near future. This will reload Element.": "Temporarily show communities instead of Spaces for this session. Support for this will be removed in the near future. This will reload Element.", "Developer mode": "Developer mode", + "Automatically send debug logs on any error": "Automatically send debug logs on any error", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading logs": "Uploading logs", diff --git a/src/sentry.ts b/src/sentry.ts index 88abada17a..db90d9c3fa 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -192,6 +192,11 @@ export async function sendSentryReport(userText: string, issueUrl: string, error } } +export function setSentryUser(mxid: string): void { + if (!SdkConfig.get().sentry || !SettingsStore.getValue("automaticErrorReporting")) return; + Sentry.setUser({ username: mxid }); +} + interface ISentryConfig { dsn: string; environment?: string; @@ -199,21 +204,28 @@ interface ISentryConfig { export async function initSentry(sentryConfig: ISentryConfig): Promise { if (!sentryConfig) return; + // Only enable Integrations.GlobalHandlers, which hooks uncaught exceptions, if automaticErrorReporting is true + const integrations = [ + new Sentry.Integrations.InboundFilters(), + new Sentry.Integrations.FunctionToString(), + new Sentry.Integrations.Breadcrumbs(), + new Sentry.Integrations.UserAgent(), + new Sentry.Integrations.Dedupe(), + ]; + + if (SettingsStore.getValue("automaticErrorReporting")) { + integrations.push(new Sentry.Integrations.GlobalHandlers( + { onerror: false, onunhandledrejection: true })); + integrations.push(new Sentry.Integrations.TryCatch()); + } + Sentry.init({ dsn: sentryConfig.dsn, release: process.env.VERSION, environment: sentryConfig.environment, defaultIntegrations: false, autoSessionTracking: false, - integrations: [ - // specifically disable Integrations.GlobalHandlers, which hooks uncaught exceptions - we don't - // want to capture those at this stage, just explicit rageshakes - new Sentry.Integrations.InboundFilters(), - new Sentry.Integrations.FunctionToString(), - new Sentry.Integrations.Breadcrumbs(), - new Sentry.Integrations.UserAgent(), - new Sentry.Integrations.Dedupe(), - ], + integrations, // Set to 1.0 which is reasonable if we're only submitting Rageshakes; will need to be set < 1.0 // if we collect more frequently. tracesSampleRate: 1.0, diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index ab3b164517..5adfeb4545 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -762,6 +762,12 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: false, }, + "automaticErrorReporting": { + displayName: _td("Automatically send debug logs on any error"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: false, + controller: new ReloadOnChangeController(), + }, [UIFeature.RoomHistorySettings]: { supportedLevels: LEVELS_UI_FEATURE, default: true,