diff --git a/src/QEMUVM.ts b/src/QEMUVM.ts index 82a9324..7783f39 100644 --- a/src/QEMUVM.ts +++ b/src/QEMUVM.ts @@ -7,6 +7,7 @@ import QMPClient from "./QMPClient.js"; import BatchRects from "./RectBatcher.js"; import { createCanvas, Canvas, CanvasRenderingContext2D, createImageData } from "canvas"; import { Mutex } from "async-mutex"; +import log from "./log.js"; export default class QEMUVM extends EventEmitter { vnc? : rfb.RfbClient; @@ -34,7 +35,7 @@ export default class QEMUVM extends EventEmitter { constructor(Config : IConfig) { super(); if (Config.vm.vncPort < 5900) { - console.error("[FATAL] VNC Port must be 5900 or higher"); + log("FATAL", "VNC port must be 5900 or higher") process.exit(1); } Config.vm.qmpSockDir == null ? this.qmpType = "tcp:" : this.qmpType = "unix:"; @@ -65,13 +66,13 @@ export default class QEMUVM extends EventEmitter { try { fs.unlinkSync(this.qmpSock); } catch (e) { - console.error("[FATAL] Could not remove existing QMP socket: " + e); + log("ERORR", `Failed to delete existing socket: ${e}`); process.exit(-1); } var qemuArr = this.qemuCmd.split(" "); this.qemuProcess = execa(qemuArr[0], qemuArr.slice(1)); this.qemuProcess.catch(() => false); - this.qemuProcess.stderr?.on('data', (d) => console.log(d.toString())); + this.qemuProcess.stderr?.on('data', (d) => log("ERROR", `QEMU sent to stderr: ${d.toString()}`)); this.qemuProcess.once('spawn', () => { setTimeout(async () => { await this.qmpClient.connect(); @@ -83,10 +84,10 @@ export default class QEMUVM extends EventEmitter { clearTimeout(this.vncReconnectTimeout); this.processRestartErrorLevel++; if (this.processRestartErrorLevel > 4) { - console.error("[FATAL] QEMU failed to launch 5 times."); + log("FATAL", "QEMU failed to launch 5 times."); process.exit(-1); } - console.warn("QEMU exited unexpectly. Restarting in 3 seconds..."); + log("WARN", "QEMU exited unexpectedly, retrying in 3 seconds"); this.qmpClient.disconnect(); this.vnc?.end(); this.qemuRestartTimeout = setTimeout(() => this.Start(), 3000); @@ -99,7 +100,7 @@ export default class QEMUVM extends EventEmitter { private qmpConnected() { this.qmpErrorLevel = 0; this.processRestartErrorLevel = 0; - console.log("QMP Connected"); + log("INFO", "QMP Connected"); setTimeout(() => this.startVNC(), 1000); } @@ -123,10 +124,10 @@ export default class QEMUVM extends EventEmitter { if (this.expectedExit) return; this.qmpErrorLevel++; if (this.qmpErrorLevel > 4) { - console.error("[FATAL] Failed to connect to QMP after 5 attempts"); + log("FATAL", "Failed to connect to QMP after 5 attempts"); process.exit(1); } - console.warn("Failed to connect to QMP. Retrying in 3 seconds..."); + log("ERROR", "Failed to connect to QMP, retrying in 3 seconds."); this.qmpReconnectTimeout = setTimeout(() => this.qmpClient.connect(), 3000); } @@ -135,19 +136,20 @@ export default class QEMUVM extends EventEmitter { if (this.expectedExit) return; this.vncErrorLevel++; if (this.vncErrorLevel > 4) { - console.error("[FATAL] Failed to connect to VNC after 5 attempts"); + log("FATAL", "Failed to connect to VNC after 5 attempts.") process.exit(1); } try { this.vnc?.end(); } catch {}; - console.warn("Failed to connect to VNC. Retrying in 3 seconds..."); + log("ERROR", "Failed to connect to VNC, retrying in 3 seconds"); this.vncReconnectTimeout = setTimeout(() => this.startVNC(), 3000); } private vncConnected() { this.vncOpen = true; this.emit('vncconnect'); + log("INFO", "VNC Connected"); this.vncErrorLevel = 0; //@ts-ignore this.onVNCSize({height: this.vnc.height, width: this.vnc.width}); @@ -222,7 +224,7 @@ export default class QEMUVM extends EventEmitter { this.vnc?.end(); clearInterval(this.vncUpdateInterval); var killTimeout = setTimeout(() => { - console.log("Force killing QEMU after 10 seconds of waiting for shutdown"); + log("WARN", "Force killing QEMU after 10 seconds of waiting for shutdown"); this.qemuProcess?.kill(9); }, 10000); var closep = new Promise(async (reso, reje) => { diff --git a/src/QMPClient.ts b/src/QMPClient.ts index 6c7625d..7232f77 100644 --- a/src/QMPClient.ts +++ b/src/QMPClient.ts @@ -1,6 +1,7 @@ import EventEmitter from "events"; import { Socket } from "net"; import { Mutex } from "async-mutex"; +import log from "./log.js"; export default class QMPClient extends EventEmitter { socketfile : string; @@ -32,7 +33,7 @@ export default class QMPClient extends EventEmitter { this.onClose(); } this.connected = true; - this.socket.on('error', (err) => console.log(err)); // Disable throwing if QMP errors + this.socket.on('error', () => false); // Disable throwing if QMP errors this.socket.on('data', (data) => this.onData(data)); this.socket.on('close', () => this.onClose()); this.once('connected', () => {res();}); @@ -69,13 +70,13 @@ export default class QMPClient extends EventEmitter { switch(msg.event) { case "STOP": { - console.log("[INFO] Resetting QEMU..."); + log("INFO", "The VM was shut down, restarting..."); this.reboot(); break; } case "RESET": { - console.log("[INFO] QEMU reset event occurred"); + log("INFO", "QEMU reset event occured"); this.resume(); break; }; diff --git a/src/WSServer.ts b/src/WSServer.ts index 0be9311..bd16be7 100644 --- a/src/WSServer.ts +++ b/src/WSServer.ts @@ -13,6 +13,7 @@ import { isIP } from 'net'; import QEMUVM from './QEMUVM.js'; import { Canvas, createCanvas, CanvasRenderingContext2D } from 'canvas'; import { IPData } from './IPData.js'; +import log from './log.js'; export default class WSServer { private Config : IConfig; @@ -157,14 +158,14 @@ export default class WSServer { this.onMessage(user, msg); }); user.sendMsg(this.getAdduserMsg()); - console.log(`[Connect] From ${user.IP.address}`); + log("INFO", `Connect from ${user.IP.address}`); }; private connectionClosed(user : User) { if(user.IP.vote != null) user.IP.vote = null; if (this.indefiniteTurn === user) this.indefiniteTurn = null; this.clients.splice(this.clients.indexOf(user), 1); - console.log(`[DISCONNECT] From ${user.IP.address}${user.username ? ` with username ${user.username}` : ""}`); + log("INFO", `Disconnect From ${user.IP.address}${user.username ? ` with username ${user.username}` : ""}`); if (!user.username) return; if (this.TurnQueue.toArray().indexOf(user) !== -1) { var hadturn = (this.TurnQueue.peek() === user); @@ -506,12 +507,12 @@ export default class WSServer { //@ts-ignore client.sendMsg(guacutils.encode("rename", "0", status, client.username)); if (hadName) { - console.log(`[RENAME] ${client.IP.address} from ${oldname} to ${client.username}`); + log("INFO", `Rename ${client.IP.address} from ${oldname} to ${client.username}`); this.clients.filter(c => c.username !== client.username).forEach((c) => //@ts-ignore c.sendMsg(guacutils.encode("rename", "1", oldname, client.username))); } else { - console.log(`[RENAME] ${client.IP.address} to ${client.username}`); + log("INFO", `Rename ${client.IP.address} to ${client.username}`); this.clients.forEach((c) => //@ts-ignore c.sendMsg(guacutils.encode("adduser", "1", client.username, client.rank))); diff --git a/src/index.ts b/src/index.ts index 31d31f6..ef7ce67 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,20 +3,23 @@ import IConfig from './IConfig.js'; import * as fs from "fs"; import WSServer from './WSServer.js'; import QEMUVM from './QEMUVM.js'; +import log from './log.js'; + +log("INFO", "CollabVM Server starting up"); // Parse the config file var Config : IConfig; if (!fs.existsSync("config.toml")) { - console.error("config.toml not found. Please copy config.example.toml to config.toml and fill out fields."); + log("FATAL", "Config.toml not found. Please copy config.example.toml and fill out fields") process.exit(1); } try { var configRaw = fs.readFileSync("config.toml").toString(); Config = toml.parse(configRaw); } catch (e) { - console.error(`Failed to read or parse the config file: ${e}`); + log("FATAL", `Failed to read or parse the config file: ${e}`); process.exit(1); } diff --git a/src/log.ts b/src/log.ts new file mode 100644 index 0000000..5cbb325 --- /dev/null +++ b/src/log.ts @@ -0,0 +1,3 @@ +export default function log(loglevel : string, message : string) { + console[(loglevel === "ERROR" || loglevel === "FATAL") ? "error" : "log"](`[${new Date().toLocaleString()}] [${loglevel}] ${message}`); +} \ No newline at end of file