Browse Source

feature: added module status screen and module stages and timings (buggy)

Kristian Vos 5 years ago
parent
commit
dcd432438c

+ 19 - 0
backend/core.js

@@ -10,6 +10,10 @@ module.exports = class {
 		this.dependsOn = [];
 		this.eventHandlers = [];
 		this.state = "NOT_INITIALIZED";
+		this.stage = 0;
+		this.lastTime = 0;
+		this.totalTimeInitialize = 0;
+		this.timeDifferences = [];
 	}
 
 	_initialize() {
@@ -18,6 +22,8 @@ module.exports = class {
 
 		this.initialize().then(() => {
 			this.setState("INITIALIZED");
+			this.setStage(0);
+			this.moduleManager.printStatus();
 		}).catch(() => {
 			this.moduleManager.aModuleFailed(this);
 		});
@@ -46,6 +52,17 @@ module.exports = class {
 		this.logger.info(`MODULE_STATE`, `${state}: ${this.name}`);
 	}
 
+	setStage(stage) {
+		if (stage !== 1)
+			this.totalTimeInitialize += (Date.now() - this.lastTime);
+		//this.timeDifferences.push(this.stage + ": " + (Date.now() - this.lastTime) + "ms");
+		this.timeDifferences.push(Date.now() - this.lastTime);
+
+		this.lastTime = Date.now();
+		this.stage = stage;
+		this.moduleManager.printStatus();
+	}
+
 	_validateHook() {
 		return Promise.race([this._onInitialize, this._isInitialized]).then(
 			() => this._isNotLocked()
@@ -54,5 +71,7 @@ module.exports = class {
 
 	_lockdown() {
 		this.lockdown = true;
+		this.setState("LOCKDOWN");
+		this.moduleManager.printStatus();
 	}
 }

+ 48 - 2
backend/index.js

@@ -3,7 +3,7 @@
 process.env.NODE_CONFIG_DIR = `${__dirname}/config`;
 
 process.on('uncaughtException', err => {
-	if (lockdownB || err.code === 'ECONNREFUSED' || err.code === 'UNCERTAIN_STATE') return;
+	if (err.code === 'ECONNREFUSED' || err.code === 'UNCERTAIN_STATE') return;
 	console.log(`UNCAUGHT EXCEPTION: ${err.stack}`);
 });
 
@@ -28,6 +28,7 @@ class ModuleManager {
 	initialize() {
 		if (!this.modules["logger"]) return console.error("There is no logger module");
 		this.logger = this.modules["logger"];
+		this.logger.reservedLines = Object.keys(this.modules).length + 2;
 		
 		for (let moduleName in this.modules) {
 			let module = this.modules[moduleName];
@@ -44,6 +45,8 @@ class ModuleManager {
 				dependenciesInitializedPromises.push(dependency._onInitialize());
 			});
 
+			module.lastTime = Date.now();
+
 			Promise.all(dependenciesInitializedPromises).then((res, res2) => {
 				if (this.lockdown) return;
 				this.logger.info("MODULE_MANAGER", `${moduleName} dependencies have been completed`);
@@ -52,6 +55,38 @@ class ModuleManager {
 		}
 	}
 
+	async printStatus() {
+		try { await Promise.race([this.logger._onInitialize, this.logger._isInitialized]); } catch { return; }
+		
+		let colors = this.logger.colors;
+
+		process.stdout.moveCursor(0, -this.logger.reservedLines);
+		process.stdout.clearScreenDown();
+		process.stdout.write(`\n`);
+		process.stdout.write(`${colors.FgYellow}Modules${colors.FgWhite}:\n`);
+
+		for (let moduleName in this.modules) {
+			let module = this.modules[moduleName];
+			let tabsAmount = 2 - (moduleName.length / 8);
+			
+			let tabs = "";
+			for(let i = 0; i < tabsAmount; i++)
+				tabs += "\t";
+
+			let timing = module.timeDifferences.map((timeDifference) => {
+				return `${colors.FgMagenta}${timeDifference}${colors.FgCyan}ms${colors.FgWhite}`;
+			}).join(", ");
+
+			let stateColor;
+			if (module.state === "NOT_INITIALIZED") stateColor = colors.FgWhite;
+			if (module.state === "INITIALIZING") stateColor = colors.FgYellow;
+			if (module.state === "INITIALIZED") stateColor = colors.FgGreen;
+			if (module.state === "LOCKDOWN") stateColor = colors.FgRed;
+			
+			process.stdout.write(`${moduleName}${tabs}${stateColor}${module.state}\t${colors.FgYellow}Stage: ${colors.FgRed}${module.stage}${colors.FgWhite}. ${colors.FgYellow}Timing${colors.FgWhite}: [${timing}]${colors.FgWhite}${colors.FgWhite}. ${colors.FgYellow}Total time${colors.FgWhite}: ${colors.FgRed}${module.totalTimeInitialize}${colors.FgCyan}ms${colors.Reset}\n`);
+		}
+	}
+
 	moduleInitialized(moduleName) {
 		this.modulesInitialized++;
 		this.modulesLeft.splice(this.modulesLeft.indexOf(moduleName), 1);
@@ -70,8 +105,12 @@ class ModuleManager {
 		this.logger.error("MODULE_MANAGER", `A module has failed, locking down. Module: ${failedModule.name}`);
 		this.modules["discord"].sendAdminAlertMessage(`The backend server failed to start due to a failing module: ${failedModule.name}.`, "#AA0000", "Startup", false, []);
 
-		this.lockdown = true;
+		this._lockdown();
+	}
 
+	_lockdown() {
+		this.lockdown = true;
+		
 		for (let moduleName in this.modules) {
 			let module = this.modules[moduleName];
 			module._lockdown();
@@ -101,3 +140,10 @@ moduleManager.addModule("tasks");
 moduleManager.addModule("utils");
 
 moduleManager.initialize();
+
+process.stdin.on("data", function (data) {
+    if(data.toString() === "lockdown\r\n"){
+        console.log("Locking down.");
+       	moduleManager._lockdown();
+    }
+});

+ 2 - 0
backend/logic/api.js

@@ -9,6 +9,8 @@ module.exports = class extends coreClass {
 
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.app = this.moduleManager.modules["app"];
 
 			this.app.app.get('/', (req, res) => {

+ 2 - 0
backend/logic/app.js

@@ -14,6 +14,8 @@ const OAuth2 = require('oauth').OAuth2;
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			const 	logger 	= this.logger,
 					mail	= this.moduleManager.modules["mail"],
 					cache	= this.moduleManager.modules["cache"],

+ 2 - 0
backend/logic/cache/index.js

@@ -13,6 +13,8 @@ const pubs = {}, subs = {};
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.schemas = {
 				session: require('./schemas/session'),
 				station: require('./schemas/station'),

+ 2 - 0
backend/logic/db/index.js

@@ -24,6 +24,8 @@ mongoose.Promise = bluebird;
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.schemas = {};
 			this.models = {};
 

+ 2 - 0
backend/logic/discord.js

@@ -9,6 +9,8 @@ const bus = new EventEmitter();
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.client = new Discord.Client();
 			
 			this.connected = false;

+ 2 - 0
backend/logic/io.js

@@ -16,6 +16,8 @@ module.exports = class extends coreClass {
 
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			const 	logger		= this.logger,
 					app			= this.moduleManager.modules["app"],
 					cache		= this.moduleManager.modules["cache"],

+ 48 - 6
backend/logic/logger.js

@@ -29,6 +29,8 @@ const getTimeFormatted = () => {
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.configDirectory = `${__dirname}/../../log`;
 
 			if (!config.isDocker && !fs.existsSync(`${this.configDirectory}`))
@@ -36,12 +38,44 @@ module.exports = class extends coreClass {
 
 			let time = getTimeFormatted();
 
+			this.colors = {
+				Reset: "\x1b[0m",
+				Bright: "\x1b[1m",
+				Dim: "\x1b[2m",
+				Underscore: "\x1b[4m",
+				Blink: "\x1b[5m",
+				Reverse: "\x1b[7m",
+				Hidden: "\x1b[8m",
+
+				FgBlack: "\x1b[30m",
+				FgRed: "\x1b[31m",
+				FgGreen: "\x1b[32m",
+				FgYellow: "\x1b[33m",
+				FgBlue: "\x1b[34m",
+				FgMagenta: "\x1b[35m",
+				FgCyan: "\x1b[36m",
+				FgWhite: "\x1b[37m",
+
+				BgBlack: "\x1b[40m",
+				BgRed: "\x1b[41m",
+				BgGreen: "\x1b[42m",
+				BgYellow: "\x1b[43m",
+				BgBlue: "\x1b[44m",
+				BgMagenta: "\x1b[45m",
+				BgCyan: "\x1b[46m",
+				BgWhite: "\x1b[47m"
+			};
+
 			fs.appendFile(this.configDirectory + '/all.log', `${time} BACKEND_RESTARTED\n`, ()=>{});
 			fs.appendFile(this.configDirectory + '/success.log', `${time} BACKEND_RESTARTED\n`, ()=>{});
 			fs.appendFile(this.configDirectory + '/error.log', `${time} BACKEND_RESTARTED\n`, ()=>{});
 			fs.appendFile(this.configDirectory + '/info.log', `${time} BACKEND_RESTARTED\n`, ()=>{});
 			fs.appendFile(this.configDirectory + '/debugStation.log', `${time} BACKEND_RESTARTED\n`, ()=>{});
 
+			for(let i = 0; i < this.reservedLines; i++) {
+				process.stdout.write("\n");
+			}
+
 			resolve();
 		});
 	}
@@ -55,7 +89,7 @@ module.exports = class extends coreClass {
 		this.writeFile('all.log', message);
 		this.writeFile('success.log', message);
 
-		if (display) console.info('\x1b[32m', message, '\x1b[0m');
+		if (display) this.log(this.colors.FgGreen, message);
 	}
 
 	async error(type, text, display = true) {
@@ -67,7 +101,7 @@ module.exports = class extends coreClass {
 		this.writeFile('all.log', message);
 		this.writeFile('error.log', message);
 
-		if (display) console.warn('\x1b[31m', message, '\x1b[0m');
+		if (display) this.log(this.colors.FgRed, message);
 	}
 
 	async info(type, text, display = true) {
@@ -78,8 +112,7 @@ module.exports = class extends coreClass {
 
 		this.writeFile('all.log', message);
 		this.writeFile('info.log', message);
-
-		if (display) console.info('\x1b[36m', message, '\x1b[0m');
+		if (display) this.log(this.colors.FgCyan, message);
 	}
 
 	async stationIssue(text, display = false) {
@@ -90,10 +123,19 @@ module.exports = class extends coreClass {
 
 		this.writeFile('debugStation.log', message);
 
-		if (display) console.info('\x1b[35m', message, '\x1b[0m');
+		if (display) this.log(this.colors.FgMagenta, message);
 	}
 
-	
+	log(color, message) {
+		process.stdout.moveCursor(0, -this.reservedLines);
+		process.stdout.write(`${color}${message}${this.colors.Reset}\n`);
+
+		for(let i = 0; i < this.reservedLines; i++) {
+			process.stdout.write("\n");
+		}
+
+		this.moduleManager.printStatus();
+	}
 
 	writeFile(fileName, message) {
 		fs.appendFile(`${this.configDirectory}/${fileName}`, `${message}\n`, ()=>{});

+ 2 - 0
backend/logic/mail/index.js

@@ -9,6 +9,8 @@ let mailgun = null;
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.schemas = {
 				verifyEmail: require('./schemas/verifyEmail'),
 				resetPasswordRequest: require('./schemas/resetPasswordRequest'),

+ 2 - 0
backend/logic/notifications.js

@@ -11,6 +11,8 @@ const subscriptions = [];
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			const url = this.url = config.get("redis").url;
 			const password = this.password = config.get("redis").password;
 

+ 6 - 0
backend/logic/playlists.js

@@ -13,16 +13,20 @@ module.exports = class extends coreClass {
 
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.cache = this.moduleManager.modules["cache"];
 			this.db	= this.moduleManager.modules["db"];
 			this.utils	= this.moduleManager.modules["utils"];
 
 			async.waterfall([
 				(next) => {
+					this.setStage(2);
 					this.cache.hgetall('playlists', next);
 				},
 	
 				(playlists, next) => {
+					this.setStage(3);
 					if (!playlists) return next();
 					let playlistIds = Object.keys(playlists);
 					async.each(playlistIds, (playlistId, next) => {
@@ -37,10 +41,12 @@ module.exports = class extends coreClass {
 				},
 	
 				(next) => {
+					this.setStage(4);
 					this.db.models.playlist.find({}, next);
 				},
 	
 				(playlists, next) => {
+					this.setStage(5);
 					async.each(playlists, (playlist, next) => {
 						this.cache.hset('playlists', playlist._id, this.cache.schemas.playlist(playlist), next);
 					}, next);

+ 6 - 0
backend/logic/punishments.js

@@ -14,6 +14,8 @@ module.exports = class extends coreClass {
 
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.cache = this.moduleManager.modules['cache'];
 			this.db = this.moduleManager.modules['db'];
 			this.io = this.moduleManager.modules['io'];
@@ -21,10 +23,12 @@ module.exports = class extends coreClass {
 
 			async.waterfall([
 				(next) => {
+					this.setStage(2);
 					this.cache.hgetall('punishments', next);
 				},
 	
 				(punishments, next) => {
+					this.setStage(3);
 					if (!punishments) return next();
 					let punishmentIds = Object.keys(punishments);
 					async.each(punishmentIds, (punishmentId, next) => {
@@ -37,10 +41,12 @@ module.exports = class extends coreClass {
 				},
 	
 				(next) => {
+					this.setStage(4);
 					this.db.models.punishment.find({}, next);
 				},
 	
 				(punishments, next) => {
+					this.setStage(5);
 					async.each(punishments, (punishment, next) => {
 						if (punishment.active === false || punishment.expiresAt < Date.now()) return next();
 						this.cache.hset('punishments', punishment._id, cache.schemas.punishment(punishment, punishment._id), next);

+ 6 - 0
backend/logic/songs.js

@@ -17,6 +17,8 @@ module.exports = class extends coreClass {
 
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.cache = this.moduleManager.modules["cache"];
 			this.db = this.moduleManager.modules["db"];
 			this.io = this.moduleManager.modules["io"];
@@ -24,10 +26,12 @@ module.exports = class extends coreClass {
 
 			async.waterfall([
 				(next) => {
+					this.setStage(2);
 					this.cache.hgetall('songs', next);
 				},
 	
 				(songs, next) => {
+					this.setStage(3);
 					if (!songs) return next();
 					let songIds = Object.keys(songs);
 					async.each(songIds, (songId, next) => {
@@ -40,10 +44,12 @@ module.exports = class extends coreClass {
 				},
 	
 				(next) => {
+					this.setStage(4);
 					this.db.models.song.find({}, next);
 				},
 	
 				(songs, next) => {
+					this.setStage(5);
 					async.each(songs, (song, next) => {
 						this.cache.hset('songs', song.songId, this.cache.schemas.song(song), next);
 					}, next);

+ 4 - 0
backend/logic/spotify.js

@@ -20,6 +20,8 @@ module.exports = class extends coreClass {
 
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+
 			this.cache = this.moduleManager.modules["cache"];
 
 			const client = config.get("apis.spotify.client");
@@ -36,10 +38,12 @@ module.exports = class extends coreClass {
 
 			async.waterfall([
 				(next) => {
+					this.setStage(2);
 					this.cache.hget("api", "spotify", next, true);
 				},
 	
 				(data, next) => {
+					this.setStage(3);
 					if (data) apiResults = data;
 					next();
 				}

+ 6 - 1
backend/logic/stations.js

@@ -15,6 +15,8 @@ module.exports = class extends coreClass {
 
 	initialize() {
 		return new Promise(async (resolve, reject) => {
+			this.setStage(1);
+
 			this.cache = this.moduleManager.modules["cache"];
 			this.db = this.moduleManager.modules["db"];
 			this.utils = this.moduleManager.modules["utils"];
@@ -66,10 +68,12 @@ module.exports = class extends coreClass {
 
 			async.waterfall([
 				(next) => {
+					this.setStage(2);
 					this.cache.hgetall('stations', next);
 				},
 	
 				(stations, next) => {
+					this.setStage(3);
 					if (!stations) return next();
 					let stationIds = Object.keys(stations);
 					async.each(stationIds, (stationId, next) => {
@@ -83,10 +87,12 @@ module.exports = class extends coreClass {
 				},
 	
 				(next) => {
+					this.setStage(4);
 					this.db.models.station.find({}, next);
 				},
 	
 				(stations, next) => {
+					this.setStage(4);
 					async.each(stations, (station, next) => {
 						async.waterfall([
 							(next) => {
@@ -373,7 +379,6 @@ module.exports = class extends coreClass {
 							} else return next(null, null, -14, station);
 						});
 					}
-					console.log(111, station);
 					if (station.type === 'official' && station.playlist.length === 0) {
 						return _this.calculateSongForStation(station, (err, playlist) => {
 							if (err) return next(err);

+ 2 - 0
backend/logic/tasks.js

@@ -9,6 +9,8 @@ let tasks = {};
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+			
 			this.cache = this.moduleManager.modules["cache"];
 			this.stations = this.moduleManager.modules["stations"];
 			this.notifications = this.moduleManager.modules["notifications"];

+ 2 - 0
backend/logic/utils.js

@@ -62,6 +62,8 @@ let youtubeRequestsActive = false;
 module.exports = class extends coreClass {
 	initialize() {
 		return new Promise((resolve, reject) => {
+			this.setStage(1);
+			
 			this.io = this.moduleManager.modules["io"];
 			this.db = this.moduleManager.modules["db"];
 			this.spotify = this.moduleManager.modules["spotify"];