diff --git a/core/boot.js b/core/boot.js index fd6b5136b4..529ab32dd7 100644 --- a/core/boot.js +++ b/core/boot.js @@ -10,6 +10,17 @@ require('./server/overrides'); const debug = require('ghost-ignition').debug('boot'); // END OF GLOBAL REQUIRES +class BootLogger { + constructor(logging, startTime) { + this.logging = logging; + this.startTime = startTime; + } + log(message) { + let {logging, startTime} = this; + logging.info(`Ghost ${message} in ${(Date.now() - startTime) / 1000}s`); + } +} + /** * Get the Database into a ready state * - DatabaseStateManager handles doing all this for us @@ -191,47 +202,50 @@ async function bootGhost() { let ghostServer; try { - // Config is the absolute first thing to do! + // Config must be the first thing we do, because it is required for absolutely everything debug('Begin: Load config'); const config = require('./shared/config'); debug('End: Load config'); // Version is required by sentry & Migratior config & so is fundamental to booting - // However, it involves reading package.json, so put it here for visibility on how slow it is + // However, it involves reading package.json so its slow + // It's here for visibility on slowness debug('Begin: Load version info'); require('./server/lib/ghost-version'); debug('End: Load version info'); + // Logging is used absolutely everywhere + debug('Begin: Load logging'); + const logging = require('./shared/logging'); + const bootLogger = new BootLogger(logging, startTime); + debug('End: Load logging'); + // Sentry must be initialised early, but requires config debug('Begin: Load sentry'); require('./shared/sentry'); debug('End: Load sentry'); + // Start server with minimal app in maintenance mode debug('Begin: load server + minimal app'); - - // Get minimal application in maintenance mode const rootApp = require('./app'); - - // Start server with minimal App const GhostServer = require('./server/ghost-server'); ghostServer = new GhostServer(); await ghostServer.start(rootApp); + bootLogger.log('server started'); + // @TODO: move this ghostServer.rootApp = rootApp; - - const logging = require('./shared/logging'); - logging.info('Ghost server start', (Date.now() - startTime) / 1000 + 's'); debug('End: load server + minimal app'); - debug('Begin: Get DB ready'); // Get the DB ready + debug('Begin: Get DB ready'); await initDatabase({config, logging}); + bootLogger.log('database ready'); debug('End: Get DB ready'); // Load Ghost with all its services debug('Begin: Load Ghost Core Services'); await initCore({ghostServer}); await initFrontend(); - const ghostApp = await initExpressApps({}); await initServices({config}); debug('End: Load Ghost Core Services'); @@ -239,8 +253,8 @@ async function bootGhost() { // Mount the full Ghost app onto the minimal root app & disable maintenance mode mountGhost(rootApp, ghostApp); - // Announce Server Readiness - logging.info('Ghost booted', (Date.now() - startTime) / 1000 + 's'); + // We are technically done here + bootLogger.log('booted'); debug('boot announcing readiness'); GhostServer.announceServerReadiness(); diff --git a/core/server/ghost-server.js b/core/server/ghost-server.js index 8d360492d4..9abe92418e 100644 --- a/core/server/ghost-server.js +++ b/core/server/ghost-server.js @@ -52,7 +52,7 @@ class GhostServer { }; return new Promise(function (resolve, reject) { - if (Object.prototype.hasOwnProperty.call(config.get('server'), 'socket')) { + if (_.has(config.get('server'), 'socket')) { socketConfig = config.get('server').socket; if (_.isString(socketConfig)) { @@ -105,38 +105,7 @@ class GhostServer { // Debug logs output in testmode only if (config.get('server:testmode')) { - // This is horrible and very temporary - const jobService = require('./services/jobs'); - - // Output how many connections are open every 5 seconds - const connectionInterval = setInterval(() => self.httpServer.getConnections( - (err, connections) => logging.warn(`${connections} connections currently open`) - ), 5000); - - // Output a notice when the server closes - self.httpServer.on('close', function () { - clearInterval(connectionInterval); - logging.warn('Server has fully closed'); - }); - - // Output job queue length every 5 seconds - setInterval(() => { - logging.warn(`${jobService.queue.length()} jobs in the queue. Idle: ${jobService.queue.idle()}`); - - const runningScheduledjobs = Object.keys(jobService.bree.workers); - if (Object.keys(jobService.bree.workers).length) { - logging.warn(`${Object.keys(jobService.bree.workers).length} jobs running: ${runningScheduledjobs}`); - } - - const scheduledJobs = Object.keys(jobService.bree.intervals); - if (Object.keys(jobService.bree.intervals).length) { - logging.warn(`${Object.keys(jobService.bree.intervals).length} scheduled jobs: ${scheduledJobs}`); - } - - if (runningScheduledjobs.length === 0 && scheduledJobs.length === 0) { - logging.warn('No scheduled or running jobs'); - } - }, 5000); + self._startTestMode(); } debug('server announcing readiness'); @@ -207,6 +176,9 @@ class GhostServer { logging.info(i18n.t('notices.httpServer.cantTouchThis')); } + /** + * Add a task that should be called on shutdown + */ registerCleanupTask(task) { this.cleanupTasks.push(task); } @@ -240,15 +212,46 @@ class GhostServer { .all(this.cleanupTasks.map(task => task())); } - _onShutdownComplete() { - // Wrap up - events.emit('server.stop'); - this.httpServer = null; - this._logStopMessages(); + /** + * Internal Method for TestMode. + */ + _startTestMode() { + // This is horrible and very temporary + const jobService = require('./services/jobs'); + + // Output how many connections are open every 5 seconds + const connectionInterval = setInterval(() => this.httpServer.getConnections( + (err, connections) => logging.warn(`${connections} connections currently open`) + ), 5000); + + // Output a notice when the server closes + this.httpServer.on('close', function () { + clearInterval(connectionInterval); + logging.warn('Server has fully closed'); + }); + + // Output job queue length every 5 seconds + setInterval(() => { + logging.warn(`${jobService.queue.length()} jobs in the queue. Idle: ${jobService.queue.idle()}`); + + const runningScheduledjobs = Object.keys(jobService.bree.workers); + if (Object.keys(jobService.bree.workers).length) { + logging.warn(`${Object.keys(jobService.bree.workers).length} jobs running: ${runningScheduledjobs}`); + } + + const scheduledJobs = Object.keys(jobService.bree.intervals); + if (Object.keys(jobService.bree.intervals).length) { + logging.warn(`${Object.keys(jobService.bree.intervals).length} scheduled jobs: ${scheduledJobs}`); + } + + if (runningScheduledjobs.length === 0 && scheduledJobs.length === 0) { + logging.warn('No scheduled or running jobs'); + } + }, 5000); } /** - * ### Log Start Messages + * Log Start Messages */ _logStartMessages() { logging.info(i18n.t('notices.httpServer.ghostIsRunningIn', {env: config.get('env')})); @@ -267,8 +270,7 @@ class GhostServer { } /** - * ### Log Stop Messages - * Private / internal API + * Log Stop Messages */ _logStopMessages() { logging.warn(i18n.t('notices.httpServer.ghostHasShutdown'));