Skip to content

Commit a6746f9

Browse files
authored
fix: Error reporting on Bugsnag (#1655)
* Update Bugsnag * Split error handling code through the processes * Create dev module for development tooling setup * Move main process error handling to a module * Add main/startup module * Add main/events module * Add main/window module * Fix Bugsnag in renderer processes
1 parent 394b19c commit a6746f9

13 files changed

Lines changed: 315 additions & 201 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"eslint": "eslint src gulpfile.js"
3030
},
3131
"dependencies": {
32-
"@bugsnag/js": "^6.3.2",
32+
"@bugsnag/js": "^7.2.0",
3333
"@emotion/core": "^10.0.28",
3434
"@emotion/styled": "^10.0.27",
3535
"@rocket.chat/fuselage": "^0.13.0",
@@ -78,6 +78,7 @@
7878
"convert-svg-to-png": "^0.5.0",
7979
"electron": "~7.3.2",
8080
"electron-builder": "^22.7.0",
81+
"electron-devtools-installer": "^3.1.0",
8182
"electron-notarize": "^1.0.0",
8283
"eslint": "^7.4.0",
8384
"eslint-plugin-import": "^2.22.0",

rollup.config.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default [
4848
...builtinModules,
4949
...Object.keys(appManifest.dependencies),
5050
...Object.keys(appManifest.devDependencies),
51-
],
51+
].filter((moduleName) => moduleName !== '@bugsnag/js'),
5252
input: 'src/app.js',
5353
plugins: [
5454
json(),
@@ -59,7 +59,9 @@ export default [
5959
babel({
6060
babelHelpers: 'bundled',
6161
}),
62-
nodeResolve(),
62+
nodeResolve({
63+
browser: true,
64+
}),
6365
commonjs(),
6466
],
6567
output: [
@@ -75,7 +77,7 @@ export default [
7577
...builtinModules,
7678
...Object.keys(appManifest.dependencies),
7779
...Object.keys(appManifest.devDependencies),
78-
],
80+
].filter((moduleName) => moduleName !== '@bugsnag/js'),
7981
input: 'src/preload.js',
8082
plugins: [
8183
json(),
@@ -86,7 +88,9 @@ export default [
8688
babel({
8789
babelHelpers: 'bundled',
8890
}),
89-
nodeResolve(),
91+
nodeResolve({
92+
browser: true,
93+
}),
9094
commonjs(),
9195
],
9296
output: [

src/app.js

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
1+
import Bugsnag from '@bugsnag/js';
2+
import { remote } from 'electron';
13
import React from 'react';
24
import { render, unmountComponentAtNode } from 'react-dom';
3-
import { remote } from 'electron';
45

5-
import { setupErrorHandling } from './errorHandling';
66
import { setupI18next } from './i18n';
77
import { App } from './components/App';
88

9+
const setupErrorHandling = () => {
10+
if (process.env.BUGSNAG_API_KEY) {
11+
Bugsnag.start({
12+
apiKey: process.env.BUGSNAG_API_KEY,
13+
appVersion: remote.app.getVersion(),
14+
appType: 'rootWindow',
15+
collectUserIp: false,
16+
releaseStage: process.env.NODE_ENV,
17+
});
18+
19+
return;
20+
}
21+
22+
const log = (error) => {
23+
remote.getGlobal('console').error(error && (error.stack || error));
24+
};
25+
26+
window.addEventListener('error', (event) => {
27+
log(event.error);
28+
});
29+
30+
window.addEventListener('unhandledrejection', (event) => {
31+
log(event.reason);
32+
});
33+
};
34+
935
const initialize = async () => {
10-
try {
11-
setupErrorHandling('renderer');
12-
await setupI18next();
36+
await setupI18next();
1337

14-
render(<App />, document.getElementById('root'));
38+
render(<App />, document.getElementById('root'));
1539

16-
window.addEventListener('beforeunload', () => {
17-
unmountComponentAtNode(document.getElementById('root'));
18-
});
19-
} catch (error) {
20-
remote.dialog.showErrorBox(error.message, error.stack);
21-
}
40+
window.addEventListener('beforeunload', () => {
41+
unmountComponentAtNode(document.getElementById('root'));
42+
});
2243
};
2344

45+
setupErrorHandling();
2446
initialize();

src/errorHandling.js

Lines changed: 0 additions & 67 deletions
This file was deleted.

src/main.js

Lines changed: 12 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,13 @@
1-
import path from 'path';
2-
3-
import { app, BrowserWindow } from 'electron';
4-
import setupElectronReload from 'electron-reload';
5-
import rimraf from 'rimraf';
6-
7-
import { setupErrorHandling } from './errorHandling';
8-
9-
if (process.env.NODE_ENV === 'development') {
10-
setupElectronReload(__dirname, {
11-
electron: process.execPath,
12-
});
1+
import { setupDevelopmentTools } from './main/dev';
2+
import { setupErrorHandling } from './main/errors';
3+
import { handleStartup } from './main/startup';
4+
import { setupAppEvents } from './main/events';
5+
import { setupRootWindow } from './main/rootWindow';
6+
7+
if (require.main === module) {
8+
setupDevelopmentTools();
9+
setupErrorHandling();
10+
handleStartup();
11+
setupAppEvents();
12+
setupRootWindow();
1313
}
14-
15-
const preventEvent = (event) => event.preventDefault();
16-
17-
const prepareApp = () => {
18-
setupErrorHandling('main');
19-
20-
app.setAsDefaultProtocolClient('rocketchat');
21-
app.setAppUserModelId('chat.rocket');
22-
23-
const dirName = process.env.NODE_ENV === 'production' ? app.name : `${ app.name } (${ process.env.NODE_ENV })`;
24-
25-
app.setPath('userData', path.join(app.getPath('appData'), dirName));
26-
27-
const [command, args] = [
28-
process.argv.slice(0, app.isPackaged ? 1 : 2),
29-
process.argv.slice(app.isPackaged ? 1 : 2),
30-
];
31-
32-
if (args.includes('--disable-gpu')) {
33-
app.commandLine.appendSwitch('--disable-2d-canvas-image-chromium');
34-
app.commandLine.appendSwitch('--disable-accelerated-2d-canvas');
35-
app.commandLine.appendSwitch('--disable-gpu');
36-
}
37-
38-
if (args.includes('--reset-app-data')) {
39-
const dataDir = app.getPath('userData');
40-
rimraf.sync(dataDir);
41-
app.relaunch({ args: [...command.slice(1)] });
42-
app.exit();
43-
return;
44-
}
45-
46-
const canStart = process.mas || app.requestSingleInstanceLock();
47-
48-
if (!canStart) {
49-
app.exit();
50-
return;
51-
}
52-
53-
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required');
54-
55-
// TODO: make it a setting
56-
if (process.platform === 'linux') {
57-
app.disableHardwareAcceleration();
58-
}
59-
60-
app.addListener('certificate-error', preventEvent);
61-
app.addListener('select-client-certificate', preventEvent);
62-
app.addListener('login', preventEvent);
63-
app.addListener('open-url', preventEvent);
64-
app.addListener('window-all-closed', () => {
65-
app.quit();
66-
});
67-
};
68-
69-
const createMainWindow = () => {
70-
const mainWindow = new BrowserWindow({
71-
width: 1000,
72-
height: 600,
73-
minWidth: 400,
74-
minHeight: 400,
75-
titleBarStyle: 'hidden',
76-
backgroundColor: '#2f343d',
77-
show: false,
78-
webPreferences: {
79-
webviewTag: true,
80-
nodeIntegration: true,
81-
},
82-
});
83-
84-
mainWindow.addListener('close', preventEvent);
85-
86-
mainWindow.webContents.addListener('will-attach-webview', (event, webPreferences) => {
87-
delete webPreferences.enableBlinkFeatures;
88-
});
89-
90-
mainWindow.loadFile(`${ app.getAppPath() }/app/public/app.html`);
91-
};
92-
93-
const initialize = async () => {
94-
prepareApp();
95-
await app.whenReady();
96-
createMainWindow();
97-
};
98-
99-
initialize();

src/main/dev.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import path from 'path';
2+
3+
import { app } from 'electron';
4+
// import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';
5+
import setupElectronReload from 'electron-reload';
6+
7+
export const setupDevelopmentTools = () => {
8+
if (process.env.NODE_ENV !== 'development') {
9+
return;
10+
}
11+
12+
setupElectronReload(path.join(app.getAppPath(), 'app'), {
13+
electron: process.execPath,
14+
});
15+
16+
// Waiting for https://github.com/MarshallOfSound/electron-devtools-installer/pull/140 to be merged
17+
// app.whenReady().then(() => installExtension(REACT_DEVELOPER_TOOLS));
18+
19+
app.setPath('userData', path.join(app.getPath('appData'), `${ app.name } (development)`));
20+
};

src/main/errors.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Bugsnag from '@bugsnag/js';
2+
import { app } from 'electron';
3+
4+
export const setupErrorHandling = () => {
5+
if (process.env.BUGSNAG_API_KEY) {
6+
Bugsnag.start({
7+
apiKey: process.env.BUGSNAG_API_KEY,
8+
appVersion: app.getVersion(),
9+
appType: 'main',
10+
collectUserIp: false,
11+
releaseStage: process.env.NODE_ENV,
12+
});
13+
14+
return;
15+
}
16+
17+
process.addListener('uncaughtException', (error) => {
18+
console.error(error);
19+
app.quit(1);
20+
});
21+
22+
process.addListener('unhandledRejection', (reason) => {
23+
console.error(reason);
24+
app.quit(1);
25+
});
26+
};

src/main/events.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { app } from 'electron';
2+
3+
export const setupAppEvents = () => {
4+
const preventEvent = (event) => {
5+
event.preventDefault();
6+
};
7+
8+
app.addListener('certificate-error', preventEvent);
9+
app.addListener('select-client-certificate', preventEvent);
10+
app.addListener('login', preventEvent);
11+
app.addListener('open-url', preventEvent);
12+
app.addListener('window-all-closed', () => {
13+
app.quit();
14+
});
15+
};

src/main/rootWindow.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import path from 'path';
2+
3+
import { BrowserWindow, app } from 'electron';
4+
5+
const createRootWindow = () => {
6+
const rootWindow = new BrowserWindow({
7+
width: 1000,
8+
height: 600,
9+
minWidth: 400,
10+
minHeight: 400,
11+
titleBarStyle: 'hidden',
12+
backgroundColor: '#2f343d',
13+
show: false,
14+
webPreferences: {
15+
webviewTag: true,
16+
nodeIntegration: true,
17+
},
18+
});
19+
20+
rootWindow.addListener('close', (event) => {
21+
event.preventDefault();
22+
});
23+
24+
rootWindow.webContents.addListener('will-attach-webview', (event, webPreferences) => {
25+
delete webPreferences.enableBlinkFeatures;
26+
});
27+
28+
rootWindow.loadFile(path.join(app.getAppPath(), 'app/public/app.html'));
29+
};
30+
31+
export const setupRootWindow = () => {
32+
app.whenReady().then(() => createRootWindow());
33+
};

0 commit comments

Comments
 (0)