2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

Improved error messaging

closes #748

- Removed the alpha software warning
- Better error message output for the whole app - can now specify an error, a context, and a help message
- Improved invalid node version, start and stop messaging
- Listens for Ctrl+C and exits nicely
- Minor improvements to handling and errors with old DBs (temporary)
This commit is contained in:
Hannah Wolfe 2013-09-15 12:11:47 +01:00
parent 9fa659aeee
commit 71a92194ca
5 changed files with 90 additions and 68 deletions

View file

@ -256,27 +256,54 @@ when.all([ghost.init(), helpers.loadCoreHelpers(ghost)]).then(function () {
// Tell users if their node version is not supported, and exit
if (!semver.satisfies(process.versions.node, packageInfo.engines.node)) {
console.log(
"\n !!! INVALID NODE VERSION !!!\n".red,
"Ghost requires node version".red,
"\nERROR: Unsupported version of Node".red,
"\nGhost needs Node version".red,
packageInfo.engines.node.yellow,
"as defined in package.json\n".red
"you are using version".red,
process.versions.node.yellow,
"\nPlease go to http://nodejs.org to get the latest version".green
);
process.exit(-1);
process.exit(0);
}
// Alpha warning, reminds users this is not production-ready software (yet)
// Remove once software becomes suitably 'ready'
// Startup & Shutdown messages
if (process.env.NODE_ENV === 'production') {
console.log(
"\n !!! ALPHA SOFTWARE WARNING !!!\n".red,
"Ghost is in the early stages of development.\n".red,
"Expect to see bugs and other issues (but please report them.)\n".red
"Ghost is running...".green,
"\nYour blog is now available on",
ghost.config().url,
"\nCtrl+C to shut down".grey
);
// Startup message
console.log("Express server listening on address:",
ghost.config().server.host + ':'
+ ghost.config().server.port);
// ensure that Ghost exits correctly on Ctrl+C
process.on('SIGINT', function () {
console.log(
"\nGhost has shut down".red,
"\nYour blog is now offline"
);
process.exit(0);
});
} else {
console.log(
"Ghost is running...".green,
"\nListening on",
ghost.config().server.host + ':' + ghost.config().server.port,
"\nUrl configured as:",
ghost.config().url,
"\nCtrl+C to shut down".grey
);
// ensure that Ghost exits correctly on Ctrl+C
process.on('SIGINT', function () {
console.log(
"\nGhost has shutdown".red,
"\nGhost was running for",
Math.round(process.uptime()),
"seconds"
);
process.exit(0);
});
}
// Let everyone know we have finished loading
loading.resolve();

View file

@ -41,6 +41,9 @@ function getDatabaseVersion() {
.select('value')
.then(function (versions) {
var databaseVersion = _.reduce(versions, function (memo, version) {
if (isNaN(version.value)) {
errors.throwError('Database version is not recognised');
}
return parseInt(version.value, 10) > parseInt(memo, 10) ? version.value : memo;
}, initialVersion);
@ -48,7 +51,8 @@ function getDatabaseVersion() {
// we didn't get a response we understood, assume initialVersion
databaseVersion = initialVersion;
}
return when.resolve(databaseVersion);
return databaseVersion;
});
}
return when.reject('Settings table does not exist');
@ -88,8 +92,10 @@ module.exports = {
if (databaseVersion > defaultVersion) {
// 3. The database exists but the currentVersion setting does not or cannot be understood
// In this case we don't understand the version because it is too high
errors.logError('Database is not compatible with software version.');
process.exit(-3);
errors.logErrorAndExit(
'Your database is not compatible with this version of Ghost',
'You will need to create a new database'
);
}
}, function (err) {
@ -101,8 +107,7 @@ module.exports = {
// 3. The database exists but the currentVersion setting does not or cannot be understood
// In this case the setting was missing or there was some other problem
errors.logError('Database is not recognisable.' + err);
process.exit(-2);
errors.logErrorAndExit('There is a problem with the database', err.message || err);
});
},

View file

@ -1,4 +1,5 @@
var _ = require('underscore'),
colors = require("colors"),
errors;
/**
@ -17,35 +18,44 @@ errors = {
throw err;
},
logError: function (err) {
err = err || "Unknown";
logError: function (err, context, help) {
err = err.message || err || "Unknown";
// TODO: Logging framework hookup
// Eventually we'll have better logging which will know about envs
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'staging'
|| process.env.NODE_ENV === 'production') {
console.log("Error occurred: ", err.message || err, err.stack || "");
console.log("\nERROR:".red, err.red, err.stack || "");
if (context) {
console.log(context);
}
if (help) {
console.log(help.green);
}
// add a new line
console.log("");
}
},
logAndThrowError: function (err) {
this.logError(err);
this.throwError(err);
logErrorAndExit: function (err, context, help) {
this.logError(err, context, help);
// Exit with 0 to prevent npm errors as we have our own
process.exit(0);
},
logErrorWithMessage: function (msg) {
logAndThrowError: function (err, context, help) {
this.logError(err, context, help);
this.throwError(err, context, help);
},
logErrorWithRedirect: function (msg, context, help, redirectTo, req, res) {
var self = this;
return function () {
self.logError(msg);
};
},
logErrorWithRedirect: function (msg, redirectTo, req, res) {
var self = this;
return function () {
self.logError(msg);
self.logError(msg, context, help);
if (_.isFunction(res.redirect)) {
res.redirect(redirectTo);
@ -55,6 +65,6 @@ errors = {
};
// Ensure our 'this' context in the functions
_.bindAll(errors, "throwError", "logError", "logAndThrowError", "logErrorWithMessage", "logErrorWithRedirect");
_.bindAll(errors, "throwError", "logError", "logAndThrowError", "logErrorWithRedirect");
module.exports = errors;

View file

@ -108,6 +108,11 @@ Settings = GhostBookshelf.Model.extend({
_.each(defaultSettings, function (defaultSetting, defaultSettingKey) {
var isMissingFromDB = usedKeys.indexOf(defaultSettingKey) === -1;
// Temporary code to deal with old databases with currentVersion settings
// TODO: remove before release
if (defaultSettingKey === 'databaseVersion' && usedKeys.indexOf('currentVersion') !== -1) {
isMissingFromDB = false;
}
if (isMissingFromDB) {
defaultSetting.value = defaultSetting.defaultValue;
insertOperations.push(Settings.forge(defaultSetting).save());

View file

@ -5,12 +5,11 @@ var testUtils = require('./testUtils'),
sinon = require('sinon'),
// Stuff we are testing
colors = require("colors"),
errors = require('../../server/errorHandling'),
// storing current environment
currentEnv = process.env.NODE_ENV;
describe("Error handling", function () {
// Just getting rid of jslint unused error
@ -51,7 +50,7 @@ describe("Error handling", function () {
errors.logError(err);
// Calls log with message on Error objects
logStub.calledWith("Error occurred: ", err.message).should.equal(true);
logStub.calledWith("\nERROR:".red, err.message.red).should.equal(true);
logStub.reset();
@ -60,37 +59,13 @@ describe("Error handling", function () {
errors.logError(err);
// Calls log with string on strings
logStub.calledWith("Error occurred: ", err).should.equal(true);
logStub.calledWith("\nERROR:".red, err.red).should.equal(true);
logStub.restore();
process.env.NODE_ENV = currentEnv;
});
it("logs promise errors with custom messages", function (done) {
var def = when.defer(),
prom = def.promise,
logStub = sinon.stub(console, "log");
// give environment a value that will console log
process.env.NODE_ENV = "development";
prom.then(function () {
throw new Error("Ran success handler");
}, errors.logErrorWithMessage("test1"));
prom.otherwise(function () {
logStub.calledWith("Error occurred: ", "test1").should.equal(true);
logStub.restore();
done();
});
prom.ensure(function () {
// gives the environment the correct value back
process.env.NODE_ENV = currentEnv;
});
def.reject();
});
it("logs promise errors and redirects", function (done) {
var def = when.defer(),
prom = def.promise,
@ -107,10 +82,10 @@ describe("Error handling", function () {
process.env.NODE_ENV = "development";
prom.then(function () {
throw new Error("Ran success handler");
}, errors.logErrorWithRedirect("test1", "/testurl", req, res));
}, errors.logErrorWithRedirect("test1", null, null, "/testurl", req, res));
prom.otherwise(function () {
logStub.calledWith("Error occurred: ", "test1").should.equal(true);
logStub.calledWith("\nERROR:".red, "test1".red).should.equal(true);
logStub.restore();
redirectStub.calledWith('/testurl').should.equal(true);