forked from Foundry376/Mailspring
-
Notifications
You must be signed in to change notification settings - Fork 14
/
error-logger.js
170 lines (149 loc) · 5.22 KB
/
error-logger.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
var ErrorLogger, _, app, remote;
let ipcRenderer = null;
if (process.type === 'renderer') {
ipcRenderer = require('electron').ipcRenderer;
remote = require('electron').remote;
app = remote.app;
} else {
app = require('electron').app;
}
var appVersion = app.getVersion();
var crashReporter = require('electron').crashReporter;
var RavenErrorReporter = require('./error-logger-extensions/raven-error-reporter');
// A globally available ErrorLogger that can report errors to various
// sources and enhance error functionality.
//
// This runs in both the backend browser process and each and every
// renderer process.
//
// This is available as `global.errorLogger` in the backend browser
// process.
//
// It is available at `AppEnv.errorLogger` in each renderer process.
// You should almost always use `AppEnv.reportError` in the renderer
// processes instead of manually accessing the `errorLogger`
//
// The errorLogger will report errors to a log file as well as to 3rd
// party reporting services if enabled.
module.exports = ErrorLogger = (function() {
function ErrorLogger(args) {
this.reportError = this.reportError.bind(this);
this.inSpecMode = args.inSpecMode;
this.inDevMode = args.inDevMode;
this.resourcePath = args.resourcePath;
this._startCrashReporter();
this._extendErrorObject();
this._extendNativeConsole();
this.extensions = [
new RavenErrorReporter({
inSpecMode: args.inSpecMode,
inDevMode: args.inDevMode,
resourcePath: args.resourcePath,
}),
];
if (this.inSpecMode) {
return;
}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////// PUBLIC METHODS //////////////////////////
/////////////////////////////////////////////////////////////////////
ErrorLogger.prototype.reportError = function(error, extra = {}) {
if (this.inSpecMode) {
return;
}
if (!error) {
error = { stack: '' };
}
if (process.type === 'renderer') {
var errorJSON = '{}';
try {
errorJSON = JSON.stringify(error);
} catch (err) {
var recoveredError = new Error();
recoveredError.stack = error.stack;
recoveredError.message = `Recovered Error: ${error.message}`;
errorJSON = JSON.stringify(recoveredError);
}
var extraJSON;
try {
extraJSON = JSON.stringify(extra);
} catch (err) {
extraJSON = '{}';
}
/**
* We synchronously send all errors to the backend main process.
*
* This is important because errors can frequently happen right
* before a renderer window is closing. Since error reporting hits
* APIs and is asynchronous it's possible for the window to be
* destroyed before the report makes it.
*
* This is a rare use of `sendSync` to ensure the command has made
* it before the window closes.
*/
ipcRenderer.sendSync('report-error', { errorJSON: errorJSON, extra: extraJSON });
} else {
this._notifyExtensions('reportError', error, extra);
}
console.error(error, extra);
};
/////////////////////////////////////////////////////////////////////
////////////////////////// PRIVATE METHODS //////////////////////////
/////////////////////////////////////////////////////////////////////
ErrorLogger.prototype._startCrashReporter = function(args) {
crashReporter.start({
productName: 'Mailspring',
companyName: 'Mailspring',
submitURL: `https://invalid.localhost/report-crash?ver=${appVersion}&platform=${process.platform}`,
uploadToServer: true,
autoSubmit: true,
extra: {
ver: appVersion,
platform: process.platform,
},
});
};
ErrorLogger.prototype._extendNativeConsole = function(args) {
console.debug = this._consoleDebug.bind(this);
};
// globally define Error.toJSON. This allows us to pass errors via IPC
// and through the Action Bridge. Note:they are not re-inflated into
// Error objects automatically.
ErrorLogger.prototype._extendErrorObject = function(args) {
Object.defineProperty(Error.prototype, 'toJSON', {
value: function() {
var alt = {};
Object.getOwnPropertyNames(this).forEach(function(key) {
alt[key] = this[key];
}, this);
return alt;
},
configurable: true,
});
};
ErrorLogger.prototype._notifyExtensions = function() {
var command, args;
command = arguments[0];
args = 2 <= arguments.length ? Array.prototype.slice.call(arguments, 1) : [];
for (var i = 0; i < this.extensions.length; i++) {
const extension = this.extensions[i];
extension[command].apply(extension, args);
}
};
// Create a new console.debug option, which takes `true` (print)
// or `false`, don't print in console as the first parameter.
// This makes it easy for developers to turn on and off
// "verbose console" mode.
ErrorLogger.prototype._consoleDebug = function() {
var args = [];
var showIt = arguments[0];
for (var ii = 1; ii < arguments.length; ii++) {
args.push(arguments[ii]);
}
if (this.inDevMode === true && showIt === true) {
console.log.apply(console, args);
}
};
return ErrorLogger;
})();