Skip to content
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: Enhance Sentry integration for error tracking and reporting Electron side #30

Merged
merged 2 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
651 changes: 468 additions & 183 deletions electron/package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
},
"dependencies": {
"@capacitor-community/electron": "^4.1.0",
"@sentry/electron": "^4.4.0",
"@sentry/cli": "^2.28.5",
"@sentry/node": "^5.5.0",
"auto-launch": "^5.0.5",
"chokidar": "~3.5.2",
"electron-is-dev": "~2.0.0",
Expand Down
17 changes: 12 additions & 5 deletions electron/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,25 @@ import electronIsDev from 'electron-is-dev';
import unhandled from 'electron-unhandled';
import { autoUpdater } from 'electron-updater';
import fs from 'fs-extra';
import * as Sentry from "@sentry/electron";

import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup';
import { captureException } from '@sentry/node';
const gotTheLock = app.requestSingleInstanceLock();
// Graceful handling of unhandled errors.
unhandled({
logger: () => {
console.error();
logger: (e) => {
console.error(e);
captureException(e);
console.log("there is an error occurs")
},
showDialog: false,
reportButton: (error) => {
console.log('Report Button Initialized');
captureException(error);
}
});

Sentry.init({ dsn: "https://e52e97fc558344bc80a218fc22a9a6a9@excubo.unicef.io/47" });


let isQuiting = false;
let mainWindow = null;
Expand Down Expand Up @@ -148,6 +150,7 @@ if (!gotTheLock) {
//throw new Error("opps there is unexpected error")
} catch (error) {
console.error('Error during update installation:', error);
captureException(error);
const dialogOpts = {
type: 'info',
buttons: ['Restart / Reinicie. / Перезапуск', 'Later / Después / Позже'],
Expand All @@ -164,6 +167,10 @@ if (!gotTheLock) {



});
autoUpdater.on('error', (error) => {
console.error('Update Error:', error);
captureException(error);
});
/*
autoUpdater.on('error', (error) => {
Expand All @@ -183,7 +190,7 @@ if (!gotTheLock) {
});

*/



// Security - Set Content-Security-Policy based on whether or not we are in dev mode.
Expand Down
183 changes: 150 additions & 33 deletions electron/src/setup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable prefer-arrow/prefer-arrow-functions */
/* eslint-disable object-shorthand */
/* eslint-disable @typescript-eslint/member-ordering */
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
Expand All @@ -8,21 +9,80 @@ import {
} from '@capacitor-community/electron';
import chokidar from 'chokidar';
import type { MenuItemConstructorOptions } from 'electron';
import { app, BrowserWindow, Menu, MenuItem, nativeImage, Tray, session, shell, globalShortcut } from 'electron';
import {
app,
BrowserWindow,
Menu,
MenuItem,
nativeImage,
Tray,
session,
shell,
globalShortcut,
} from 'electron';
import electronIsDev from 'electron-is-dev';
import electronServe from 'electron-serve';
import windowStateKeeper from 'electron-window-state';
import { join } from 'path';
import * as Sentry from '@sentry/node';
import { Console } from 'console';
import { Severity } from '@sentry/node';
var AutoLaunch = require('auto-launch');
var isQuiting = false;

// Enhanced Sentry configuration
Sentry.init({
dsn: 'https://e52e97fc558344bc80a218fc22a9a6a9@excubo.unicef.io/47',
environment: 'production',
beforeSend: (event)=> {
// Add app version to help with debugging
event.extra = {
...event.extra,
appVersion: app.getVersion(),
platform: process.platform,
electronVersion: process.versions.electron,
};
return event;
},
debug: electronIsDev,
maxBreadcrumbs: 50,
release: `giga-meter-electron@${app.getVersion()}`,

});

// Set up global error handlers for Sentry
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
Sentry.captureException(error);
});

process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
Sentry.captureException(reason);
});

app.on('render-process-gone', (event, webContents, details) => {
console.error('Renderer process crashed:', details);
Sentry.captureMessage(`Renderer process crashed: ${details.reason}`);
});

app.on('child-process-gone', (event, details) => {
console.error('Child process crashed:', details);
Sentry.captureMessage(
`Child process crashed: ${details.type} - ${details.reason}`
);
});

const gotTheLock = app.requestSingleInstanceLock();
// Define components for a watcher to detect when the webapp is changed so we can reload in Dev mode.
const reloadWatcher = {
debouncer: null,
ready: false,
watcher: null,
};
export function setupReloadWatcher(electronCapacitorApp: ElectronCapacitorApp): void {
export function setupReloadWatcher(
electronCapacitorApp: ElectronCapacitorApp
): void {
reloadWatcher.watcher = chokidar
.watch(join(app.getAppPath(), 'app'), {
ignored: /[/\\]\./,
Expand Down Expand Up @@ -54,16 +114,18 @@ export class ElectronCapacitorApp {
private CapacitorFileConfig: CapacitorElectronConfig;
private TrayMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [
new MenuItem({
label: 'Open', click: function () {
label: 'Open',
click: function () {
this.MainWindow.show();
}
},
}),
new MenuItem({
label: 'Quit App', click: function () {
label: 'Quit App',
click: function () {
isQuiting = true;
app.quit();
this.MainWindow.close();
}
},
}),
];
private AppMenuBarMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [
Expand All @@ -81,7 +143,9 @@ export class ElectronCapacitorApp {
) {
this.CapacitorFileConfig = capacitorFileConfig;

this.customScheme = this.CapacitorFileConfig.electron?.customUrlScheme ?? 'capacitor-electron';
this.customScheme =
this.CapacitorFileConfig.electron?.customUrlScheme ??
'capacitor-electron';

if (trayMenuTemplate) {
this.TrayMenuTemplate = trayMenuTemplate;
Expand All @@ -98,7 +162,6 @@ export class ElectronCapacitorApp {
});
}


// Helper function to load in the app.
private async loadMainWindow(thisRef: any) {
await thisRef.loadWebApp(thisRef.MainWindow);
Expand All @@ -115,7 +178,11 @@ export class ElectronCapacitorApp {

async init(): Promise<void> {
const icon = nativeImage.createFromPath(
join(app.getAppPath(), 'assets', process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png')
join(
app.getAppPath(),
'assets',
process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png'
)
);
this.mainWindowState = windowStateKeeper({
defaultWidth: 376,
Expand Down Expand Up @@ -144,16 +211,58 @@ export class ElectronCapacitorApp {
preload: preloadPath,
},
});

// Add error tracking for renderer process
this.MainWindow.webContents.on('render-process-gone', (event, details) => {
const crashData = {
reason: details.reason,
exitCode: details.exitCode,
processType: 'renderer',
};
Sentry.captureException(new Error('Renderer Process Gone'), {
extra: crashData,
});
});

this.MainWindow.on('unresponsive', () => {
Sentry.captureMessage('Window became unresponsive', {
level: Severity.Error,
extra: {
windowId: this.MainWindow.id,
},
});
});

this.MainWindow.webContents.on(
'console-message',
(event, level, message, line, sourceId) => {
if (level === 2) {
// error level
Sentry.captureMessage(`Console Error: ${message}`, {
extra: {
line,
sourceId,
},
});
}
}
);

this.MainWindow.setSize(376, 550);
this.mainWindowState.manage(this.MainWindow);

if (this.CapacitorFileConfig.backgroundColor) {
this.MainWindow.setBackgroundColor(this.CapacitorFileConfig.electron.backgroundColor);
this.MainWindow.setBackgroundColor(
this.CapacitorFileConfig.electron.backgroundColor
);
}

// If we close the main window with the splashscreen enabled we need to destory the ref.
this.MainWindow.on('closed', () => {
if (this.SplashScreen?.getSplashWindow() && !this.SplashScreen.getSplashWindow().isDestroyed()) {
if (
this.SplashScreen?.getSplashWindow() &&
!this.SplashScreen.getSplashWindow().isDestroyed()
) {
this.SplashScreen.getSplashWindow().close();
}
});
Expand Down Expand Up @@ -181,12 +290,16 @@ export class ElectronCapacitorApp {
}
});
this.TrayIcon.setToolTip(app.getName());
this.TrayIcon.setContextMenu(Menu.buildFromTemplate(this.TrayMenuTemplate));
this.TrayIcon.setContextMenu(
Menu.buildFromTemplate(this.TrayMenuTemplate)
);
}

// Setup the main manu bar at the top of our window.
if (this.CapacitorFileConfig.electron?.appMenuBarMenuTemplateEnabled) {
Menu.setApplicationMenu(Menu.buildFromTemplate(this.AppMenuBarMenuTemplate));
Menu.setApplicationMenu(
Menu.buildFromTemplate(this.AppMenuBarMenuTemplate)
);
} else {
Menu.setApplicationMenu(new Menu());
}
Expand All @@ -196,7 +309,8 @@ export class ElectronCapacitorApp {
imageFilePath: join(
app.getAppPath(),
'assets',
this.CapacitorFileConfig.electron?.splashScreenImageName ?? 'splash.png'
this.CapacitorFileConfig.electron?.splashScreenImageName ??
'splash.png'
),
windowWidth: 400,
windowHeight: 400,
Expand All @@ -222,11 +336,11 @@ export class ElectronCapacitorApp {
// this.MainWindow.on('close',(event)=>{
// if(!isQuiting){
// event.preventDefault();
// this.MainWindow.hide();
// return false;
// this.MainWindow.hide();
// return false;
// } else {
// app.quit();
// }
// }
// });
// Link electron plugins into the system.
setupCapacitorElectronPlugins();
Expand All @@ -239,20 +353,23 @@ export class ElectronCapacitorApp {
if (!this.CapacitorFileConfig.electron?.hideMainWindowOnLaunch) {
this.MainWindow.show();
}
globalShortcut.register('Control+Shift+I', () => {
if(this.MainWindow.webContents.isDevToolsOpened()){
globalShortcut.register('Super+Shift+N', () => {
console.log('Super+Shift+N pressed');
if (this.MainWindow.webContents.isDevToolsOpened()) {
this.MainWindow.webContents.closeDevTools();
}
else{
} else {
this.MainWindow.webContents.openDevTools();
}
});
});
setTimeout(() => {
if (this.CapacitorFileConfig.electron?.electronIsDev) {
this.MainWindow.webContents.openDevTools();
this.MainWindow.setSize(800, 600);
}
CapElectronEventEmitter.emit('CAPELECTRON_DeeplinkListenerInitialized', '');
CapElectronEventEmitter.emit(
'CAPELECTRON_DeeplinkListenerInitialized',
''
);
}, 400);
});

Expand All @@ -274,24 +391,24 @@ export class ElectronCapacitorApp {

// Auto lunching code added by Kajal
var measureAppAutoLuncher = new AutoLaunch({
name: 'Unicef PDCA'
name: app.getName(),
});

measureAppAutoLuncher.enable();
measureAppAutoLuncher.isEnabled().then(function (isEnabled) {
if (isEnabled) {
return;
}
measureAppAutoLuncher.enable();
})
measureAppAutoLuncher
.isEnabled()
.then(function (isEnabled) {
if (isEnabled) {
return;
}
measureAppAutoLuncher.enable();
})
.catch(function (err) {
// handle error
console.log(err)
console.log(err);
});
// End of Auto lunching code

}

}

// Set a CSP up for our application based on the custom scheme
Expand Down
Loading