mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-22 08:16:09 +08:00
290 lines
8.2 KiB
JavaScript
290 lines
8.2 KiB
JavaScript
// This file cannot be Coffeescript because it loads before the Coffeescript
|
|
// interpreter. Note that it runs in both browser and renderer processes.
|
|
|
|
var ErrorReporter, raven, _, fs, path, app, os, remote;
|
|
raven = require('raven');
|
|
os = require('os');
|
|
_ = require('underscore');
|
|
fs = require('fs-plus');
|
|
path = require('path');
|
|
if (process.type === 'renderer') {
|
|
remote = require('remote');
|
|
app = remote.require('app');
|
|
} else {
|
|
app = require('app');
|
|
}
|
|
|
|
var tmpPath = app.getPath('temp');
|
|
|
|
var logpid = process.pid;
|
|
if (process.type === 'renderer') {
|
|
logpid = remote.process.pid + "." + process.pid;
|
|
}
|
|
var logpath = path.join(tmpPath, 'Nylas-N1-' + logpid + '.log');
|
|
|
|
// 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.
|
|
Object.defineProperty(Error.prototype, 'toJSON', {
|
|
value: function () {
|
|
var alt = {};
|
|
|
|
Object.getOwnPropertyNames(this).forEach(function (key) {
|
|
alt[key] = this[key];
|
|
}, this);
|
|
|
|
return alt;
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
module.exports = ErrorReporter = (function() {
|
|
|
|
function ErrorReporter(modes) {
|
|
var self = this;
|
|
|
|
this.inSpecMode = modes.inSpecMode
|
|
this.inDevMode = modes.inDevMode
|
|
|
|
if (!this.inSpecMode) {
|
|
this._setupSentry();
|
|
this._cleanOldLogFiles();
|
|
this._setupNewLogFile();
|
|
this._hookProcessOutputs();
|
|
this._catchUncaughtErrors();
|
|
}
|
|
|
|
console.debug = _.bind(this.consoleDebug, this);
|
|
}
|
|
|
|
ErrorReporter.prototype._setupSentry = function() {
|
|
// Initialize the Sentry connector
|
|
this.client = new raven.Client('https://7a32cb0189ff4595a55c98ffb7939c46:f791c3c402b343068bed056b8b504dd5@sentry.nylas.com/4');
|
|
this.client.on('error', function(e) {
|
|
console.log(e.reason);
|
|
console.log(e.statusCode);
|
|
return console.log(e.response);
|
|
});
|
|
}
|
|
|
|
// If we're the browser process, remove log files that are more than
|
|
// two days old. These log files get pretty big because we're logging
|
|
// so verbosely.
|
|
ErrorReporter.prototype._cleanOldLogFiles = function() {
|
|
if (process.type === 'browser') {
|
|
fs.readdir(tmpPath, function(err, files) {
|
|
if (err) {
|
|
console.error(err);
|
|
return;
|
|
}
|
|
|
|
var logFilter = new RegExp("Nylas-N1-[.0-9]*.log$");
|
|
files.forEach(function(file) {
|
|
if (logFilter.test(file) === true) {
|
|
var filepath = path.join(tmpPath, file);
|
|
fs.stat(filepath, function(err, stats) {
|
|
var lastModified = new Date(stats['mtime']);
|
|
var fileAge = Date.now() - lastModified.getTime();
|
|
if (fileAge > (1000 * 60 * 60 * 24 * 2)) { // two days
|
|
fs.unlink(filepath);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
ErrorReporter.prototype._setupNewLogFile = function() {
|
|
this.shipLogsQueued = false;
|
|
this.shipLogsTime = 0;
|
|
|
|
// Open a file write stream to log output from this process
|
|
console.log("Streaming log data to "+logpath);
|
|
|
|
this.loghost = os.hostname();
|
|
this.logstream = fs.createWriteStream(logpath, {
|
|
flags: 'a',
|
|
encoding: 'utf8',
|
|
fd: null,
|
|
mode: 0666
|
|
});
|
|
}
|
|
|
|
ErrorReporter.prototype._hookProcessOutputs = function() {
|
|
var self = this;
|
|
// Override stdout and stderr to pipe their output to the file
|
|
// in addition to calling through to the existing implementation
|
|
function hook_process_output(channel, callback) {
|
|
var old_write = process[channel].write;
|
|
process[channel].write = (function(write) {
|
|
return function(string, encoding, fd) {
|
|
write.apply(process[channel], arguments)
|
|
callback(string, encoding, fd)
|
|
}
|
|
})(process[channel].write)
|
|
|
|
// Return a function that can be used to undo this change
|
|
return function() {
|
|
process[channel].write = old_write
|
|
};
|
|
}
|
|
|
|
hook_process_output('stdout', function(string, encoding, fd) {
|
|
self.appendLog.apply(self, [string]);
|
|
});
|
|
hook_process_output('stderr', function(string, encoding, fd) {
|
|
self.appendLog.apply(self, [string]);
|
|
});
|
|
}
|
|
|
|
ErrorReporter.prototype._catchUncaughtErrors = function() {
|
|
var self = this;
|
|
// Link to the appropriate error handlers for the browser
|
|
// or renderer process
|
|
if (process.type === 'renderer') {
|
|
atom.onDidThrowError(function(_arg) {
|
|
return self.reportError(_arg.originalError, {
|
|
'message': _arg.message
|
|
});
|
|
});
|
|
|
|
} else if (process.type === 'browser') {
|
|
var nslog = require('nslog');
|
|
console.log = nslog;
|
|
|
|
process.on('uncaughtException', function(error) {
|
|
if (error == null) {
|
|
error = {};
|
|
}
|
|
self.reportError(error);
|
|
if (error.message != null) {
|
|
nslog(error.message);
|
|
}
|
|
if (error.stack != null) {
|
|
return nslog(error.stack);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
ErrorReporter.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);
|
|
}
|
|
this.appendLog.apply(this, [args]);
|
|
}
|
|
|
|
ErrorReporter.prototype.appendLog = function(obj) {
|
|
if (this.inSpecMode) { return };
|
|
|
|
try {
|
|
var message = JSON.stringify({
|
|
host: this.loghost,
|
|
timestamp: (new Date()).toISOString(),
|
|
payload: obj
|
|
})+"\n";
|
|
|
|
this.logstream.write(message, 'utf8', function (err) {
|
|
if (err) {
|
|
console.error("ErrorReporter: Unable to write to the log stream!" + err.toString());
|
|
}
|
|
});
|
|
} catch (err) {
|
|
console.error("ErrorReporter: Unable to write to the log stream." + err.toString());
|
|
}
|
|
};
|
|
|
|
ErrorReporter.prototype.openLogs = function() {
|
|
var shell = require('shell');
|
|
shell.openItem(logpath);
|
|
};
|
|
|
|
ErrorReporter.prototype.shipLogs = function(reason) {
|
|
if (this.inSpecMode) { return };
|
|
|
|
if (!this.shipLogsQueued) {
|
|
var timeSinceLogShip = Date.now() - this.shipLogsTime;
|
|
if (timeSinceLogShip > 20000) {
|
|
this.runShipLogsTask(reason);
|
|
} else {
|
|
this.shipLogsQueued = true;
|
|
var self = this;
|
|
setTimeout(function() {
|
|
self.runShipLogsTask(reason);
|
|
self.shipLogsQueued = false;
|
|
}, 20000 - timeSinceLogShip);
|
|
}
|
|
}
|
|
};
|
|
|
|
ErrorReporter.prototype.runShipLogsTask = function(reason) {
|
|
if (this.inSpecMode) { return };
|
|
|
|
var self = this;
|
|
|
|
this.shipLogsTime = Date.now();
|
|
|
|
if (!reason) {
|
|
reason = "";
|
|
}
|
|
var logPattern = null;
|
|
if (process.type === 'renderer') {
|
|
logPattern = "Nylas-N1-"+remote.process.pid+"[.0-9]*.log$";
|
|
} else {
|
|
logPattern = "Nylas-N1-"+process.pid+"[.0-9]*.log$";
|
|
}
|
|
|
|
console.log("ErrorReporter: Shipping Logs. " + reason);
|
|
|
|
Task = require('./task');
|
|
ship = Task.once(fs.absolute('./tasks/ship-logs-task'), tmpPath, logPattern, function() {
|
|
self.appendLog("ErrorReporter: Shipped Logs.");
|
|
});
|
|
};
|
|
|
|
|
|
ErrorReporter.prototype.getVersion = function() {
|
|
var _ref;
|
|
return (typeof atom !== "undefined" && atom !== null ? atom.getVersion() : void 0) ||
|
|
((_ref = require('app')) != null ? _ref.getVersion() : void 0);
|
|
};
|
|
|
|
ErrorReporter.prototype.reportError = function(err, metadata) {
|
|
if (this.inSpecMode || this.inDevMode) { return };
|
|
|
|
// Never send user auth tokens
|
|
if (err.requestOptions && err.requestOptions.auth) {
|
|
delete err.requestOptions['auth'];
|
|
}
|
|
|
|
// Never send message bodies
|
|
if (err.requestOptions && err.requestOptions.body && err.requestOptions.body.body) {
|
|
delete err.requestOptions.body['body'];
|
|
}
|
|
|
|
this.client.captureError(err, {
|
|
extra: metadata,
|
|
tags: {
|
|
'platform': process.platform,
|
|
'version': this.getVersion()
|
|
}
|
|
});
|
|
|
|
this.appendLog(err, metadata);
|
|
this.shipLogs('Exception occurred');
|
|
};
|
|
|
|
return ErrorReporter;
|
|
|
|
})();
|