diff --git a/src/apps/main/background-processes/sync-engine.ts b/src/apps/main/background-processes/sync-engine.ts index 3f97d7ecc..cce03edc1 100644 --- a/src/apps/main/background-processes/sync-engine.ts +++ b/src/apps/main/background-processes/sync-engine.ts @@ -43,7 +43,9 @@ function scheduleHeathCheck() { // Logger.debug('Health check succeeded'); }) .catch(() => { - Logger.warn('Health check failed, relaunching the worker'); + const warning = 'Health check failed, relaunching the worker'; + Logger.warn(warning); + Sentry.captureMessage(warning); workerIsRunning = false; worker?.destroy(); spawnSyncEngineWorker(); diff --git a/src/apps/main/main.ts b/src/apps/main/main.ts index 26e16b2f9..2cafa8338 100644 --- a/src/apps/main/main.ts +++ b/src/apps/main/main.ts @@ -64,11 +64,13 @@ Logger.log(`Running ${packageJson.version}`); Logger.log('Initializing Sentry for main process'); if (process.env.SENTRY_DSN) { + Logger.log(`App is packaged: ${app.isPackaged}`); Sentry.init({ // Enable Sentry only when app is packaged enabled: app.isPackaged, dsn: process.env.SENTRY_DSN, }); + Sentry.captureMessage('Main process started'); Logger.log('Sentry is ready for main process'); } else { Logger.error('Sentry DSN not found, cannot initialize Sentry'); diff --git a/src/apps/main/realtime.ts b/src/apps/main/realtime.ts index d390c4058..756b2a231 100644 --- a/src/apps/main/realtime.ts +++ b/src/apps/main/realtime.ts @@ -3,7 +3,7 @@ import { io, Socket } from 'socket.io-client'; import { obtainToken } from './auth/service'; import eventBus from './event-bus'; import { broadcastToWindows } from './windows'; -import { reportError } from '../renderer/utils/errors'; +import * as Sentry from '@sentry/electron/main'; type XHRRequest = { getResponseHeader: (headerName: string) => string[] | null; @@ -54,11 +54,12 @@ function cleanAndStartRemoteNotifications() { socket.on('disconnect', (reason) => { logger.log('❌ Remote notifications disconnected, reason: ', reason); + Sentry.captureException(reason); }); socket.on('connect_error', (error) => { logger.error('❌ Remote notifications connect error: ', error); - reportError(error); + Sentry.captureException(error); }); socket.on('event', (data) => { diff --git a/src/apps/renderer/hooks/VirtualDriveStatus.tsx b/src/apps/renderer/hooks/VirtualDriveStatus.tsx index 6acd47a5d..2c7813740 100644 --- a/src/apps/renderer/hooks/VirtualDriveStatus.tsx +++ b/src/apps/renderer/hooks/VirtualDriveStatus.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { reportError } from '../utils/errors'; +import { reportError } from '../utils/sentry'; import { VirtualDriveStatus } from '../../shared/types/VirtualDriveStatus'; export default function useVirtualDriveStatus() { diff --git a/src/apps/renderer/index.tsx b/src/apps/renderer/index.tsx index 7029174ef..260afa255 100644 --- a/src/apps/renderer/index.tsx +++ b/src/apps/renderer/index.tsx @@ -1,4 +1,6 @@ import { render } from 'react-dom'; import App from './App'; +import { initSentry } from './utils/sentry'; +initSentry(); render(, document.getElementById('root')); diff --git a/src/apps/renderer/pages/Feedback/index.tsx b/src/apps/renderer/pages/Feedback/index.tsx index 45070821f..5963d4eea 100644 --- a/src/apps/renderer/pages/Feedback/index.tsx +++ b/src/apps/renderer/pages/Feedback/index.tsx @@ -4,7 +4,7 @@ import { useTranslationContext } from '../../context/LocalContext'; import { ChatsCircle } from 'phosphor-react'; import WindowTopBar from '../../components/WindowTopBar'; import TextArea from '../../components/TextArea'; -import { reportError } from '../../utils/errors'; +import { reportError } from '../../utils/sentry'; const CHARACTERS_LIMIT = 1000; export default function Feedback() { diff --git a/src/apps/renderer/pages/Login/index.tsx b/src/apps/renderer/pages/Login/index.tsx index f2eef050e..88cc5d2e4 100644 --- a/src/apps/renderer/pages/Login/index.tsx +++ b/src/apps/renderer/pages/Login/index.tsx @@ -10,6 +10,7 @@ import Button from '../../components/Button'; import PasswordInput from '../../components/PasswordInput'; import TextInput from '../../components/TextInput'; import WindowTopBar from '../../components/WindowTopBar'; +import { reportError, setUserContextForReports } from '../../utils/sentry'; const TOWFA_ERROR_MESSAGE = 'Wrong 2-factor auth code'; @@ -38,18 +39,20 @@ export default function Login() { try { const res = await accessRequest(email, password, encryptedHash, twoFA); + setUserContextForReports(res.user); window.electron.userLoggedIn(res); } catch (err) { const { message } = err as Error; const phaseToSet = - message === TOWFA_ERROR_MESSAGE ? '2fa' : 'credentials'; + message === TOWFA_ERROR_MESSAGE ? '2fa' : 'credentials'; setState('error'); setPhase(phaseToSet); // TODO: adjust styles to acomodate longer error messages setErrorDetails(translate('login.2fa.wrong-code')); window.electron.userLogginFailed(email); + reportError(err); } } diff --git a/src/apps/renderer/pages/Migration/index.tsx b/src/apps/renderer/pages/Migration/index.tsx index 5afb9e4e7..7e23027d3 100644 --- a/src/apps/renderer/pages/Migration/index.tsx +++ b/src/apps/renderer/pages/Migration/index.tsx @@ -3,7 +3,7 @@ import { SLIDES } from './config'; import { MigrationSlideProps } from './helpers'; import { useTranslationContext } from '../../context/LocalContext'; import useClientPlatform from '../../hooks/ClientPlatform'; -import { reportError } from '../../utils/errors'; +import { reportError } from '../../utils/sentry'; const totalSlides = SLIDES.length - 3; diff --git a/src/apps/renderer/pages/Widget/Header.tsx b/src/apps/renderer/pages/Widget/Header.tsx index 33dca6632..f5e398792 100644 --- a/src/apps/renderer/pages/Widget/Header.tsx +++ b/src/apps/renderer/pages/Widget/Header.tsx @@ -10,7 +10,7 @@ import useGeneralIssues from '../../hooks/GeneralIssues'; import useProcessIssues from '../../hooks/ProcessIssues'; import useUsage from '../../hooks/useUsage'; import useVirtualDriveStatus from '../../hooks/VirtualDriveStatus'; -import { reportError } from '../../utils/errors'; +import { reportError } from '../../utils/sentry'; export default function Header() { const { translate } = useTranslationContext(); diff --git a/src/apps/renderer/utils/errors.ts b/src/apps/renderer/utils/errors.ts deleted file mode 100644 index 852bd31bb..000000000 --- a/src/apps/renderer/utils/errors.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as Sentry from '@sentry/electron/renderer'; - -/** - * Reports an error to Sentry from the renderer process - * - * @param error The error to be reported - * @param context The context to attach to the error such the userId, tags, boolean values... - */ -export const reportError = ( - error: unknown, - context: Record = {} -) => { - Sentry.captureException(error, context); -}; diff --git a/src/apps/renderer/utils/sentry.ts b/src/apps/renderer/utils/sentry.ts new file mode 100644 index 000000000..348fbe3e9 --- /dev/null +++ b/src/apps/renderer/utils/sentry.ts @@ -0,0 +1,44 @@ +import * as Sentry from '@sentry/electron/renderer'; +import { User } from '../../main/types'; + +/** + * Init Sentry in the renderer process + * @param dsn Sentry DSN + * @param enabled Whether Sentry should be enabled or not + */ +export const initSentry = () => { + Sentry.init({ + dsn: process.env.SENTRY_DSN, + enabled: true, // it is true but is using app.isPackaged from the main process + }); + Sentry.captureMessage('Render process started'); +}; + +/** + * Reports an error to Sentry from the renderer process + * + * @param error The error to be reported + * @param context The context to attach to the error such the userId, tags, boolean values... + */ +export const reportError = ( + error: unknown, + context: Record = {} +) => { + Sentry.captureException(error, context); +}; + + +/** + * Set user context in Sentry + * @param user User object + */ +export const setUserContextForReports = (user: User) => { + if (!user) { + Sentry.setUser(null); + return; + } + Sentry.setUser({ + email: user.email, + id: user.uuid, + }); +}; diff --git a/src/apps/sync-engine/index.ts b/src/apps/sync-engine/index.ts index b72836c69..903a261f5 100644 --- a/src/apps/sync-engine/index.ts +++ b/src/apps/sync-engine/index.ts @@ -6,6 +6,17 @@ import { BindingsManager } from './BindingManager'; import fs from 'fs/promises'; import { iconPath } from '../utils/icon'; import * as Sentry from '@sentry/electron/renderer'; + +function initSentry() { + Sentry.init({ + dsn: process.env.SENTRY_DSN, + enabled: true // it is true but is using app.isPackaged from the main process + }); + Sentry.captureMessage('Sync engine process started'); +} + +initSentry(); + async function ensureTheFolderExist(path: string) { try { await fs.access(path); @@ -16,6 +27,7 @@ async function ensureTheFolderExist(path: string) { } async function setUp() { + Logger.info('[SYNC ENGINE] Starting sync engine process'); const virtualDrivePath = await ipcRenderer.invoke('get-virtual-drive-root'); @@ -97,8 +109,8 @@ setUp() Logger.error('[SYNC ENGINE] Error setting up', error); Sentry.captureException(error); if (error.toString().includes('Error: ConnectSyncRoot failed')) { - Sentry.captureMessage('ConnectSyncRoot failed'); Logger.info('[SYNC ENGINE] We neeed to restart the app virtual drive'); + Sentry.captureMessage('Restarting sync engine virtual drive is required'); } ipcRenderer.send('SYNC_ENGINE_PROCESS_SETUP_FAILED'); });