Add proper logging
This commit is contained in:
@@ -7,6 +7,7 @@ import QMPClient from "./QMPClient.js";
|
|||||||
import BatchRects from "./RectBatcher.js";
|
import BatchRects from "./RectBatcher.js";
|
||||||
import { createCanvas, Canvas, CanvasRenderingContext2D, createImageData } from "canvas";
|
import { createCanvas, Canvas, CanvasRenderingContext2D, createImageData } from "canvas";
|
||||||
import { Mutex } from "async-mutex";
|
import { Mutex } from "async-mutex";
|
||||||
|
import log from "./log.js";
|
||||||
|
|
||||||
export default class QEMUVM extends EventEmitter {
|
export default class QEMUVM extends EventEmitter {
|
||||||
vnc? : rfb.RfbClient;
|
vnc? : rfb.RfbClient;
|
||||||
@@ -34,7 +35,7 @@ export default class QEMUVM extends EventEmitter {
|
|||||||
constructor(Config : IConfig) {
|
constructor(Config : IConfig) {
|
||||||
super();
|
super();
|
||||||
if (Config.vm.vncPort < 5900) {
|
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);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
Config.vm.qmpSockDir == null ? this.qmpType = "tcp:" : this.qmpType = "unix:";
|
Config.vm.qmpSockDir == null ? this.qmpType = "tcp:" : this.qmpType = "unix:";
|
||||||
@@ -65,13 +66,13 @@ export default class QEMUVM extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
fs.unlinkSync(this.qmpSock);
|
fs.unlinkSync(this.qmpSock);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[FATAL] Could not remove existing QMP socket: " + e);
|
log("ERORR", `Failed to delete existing socket: ${e}`);
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
var qemuArr = this.qemuCmd.split(" ");
|
var qemuArr = this.qemuCmd.split(" ");
|
||||||
this.qemuProcess = execa(qemuArr[0], qemuArr.slice(1));
|
this.qemuProcess = execa(qemuArr[0], qemuArr.slice(1));
|
||||||
this.qemuProcess.catch(() => false);
|
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', () => {
|
this.qemuProcess.once('spawn', () => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await this.qmpClient.connect();
|
await this.qmpClient.connect();
|
||||||
@@ -83,10 +84,10 @@ export default class QEMUVM extends EventEmitter {
|
|||||||
clearTimeout(this.vncReconnectTimeout);
|
clearTimeout(this.vncReconnectTimeout);
|
||||||
this.processRestartErrorLevel++;
|
this.processRestartErrorLevel++;
|
||||||
if (this.processRestartErrorLevel > 4) {
|
if (this.processRestartErrorLevel > 4) {
|
||||||
console.error("[FATAL] QEMU failed to launch 5 times.");
|
log("FATAL", "QEMU failed to launch 5 times.");
|
||||||
process.exit(-1);
|
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.qmpClient.disconnect();
|
||||||
this.vnc?.end();
|
this.vnc?.end();
|
||||||
this.qemuRestartTimeout = setTimeout(() => this.Start(), 3000);
|
this.qemuRestartTimeout = setTimeout(() => this.Start(), 3000);
|
||||||
@@ -99,7 +100,7 @@ export default class QEMUVM extends EventEmitter {
|
|||||||
private qmpConnected() {
|
private qmpConnected() {
|
||||||
this.qmpErrorLevel = 0;
|
this.qmpErrorLevel = 0;
|
||||||
this.processRestartErrorLevel = 0;
|
this.processRestartErrorLevel = 0;
|
||||||
console.log("QMP Connected");
|
log("INFO", "QMP Connected");
|
||||||
setTimeout(() => this.startVNC(), 1000);
|
setTimeout(() => this.startVNC(), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,10 +124,10 @@ export default class QEMUVM extends EventEmitter {
|
|||||||
if (this.expectedExit) return;
|
if (this.expectedExit) return;
|
||||||
this.qmpErrorLevel++;
|
this.qmpErrorLevel++;
|
||||||
if (this.qmpErrorLevel > 4) {
|
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);
|
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);
|
this.qmpReconnectTimeout = setTimeout(() => this.qmpClient.connect(), 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,19 +136,20 @@ export default class QEMUVM extends EventEmitter {
|
|||||||
if (this.expectedExit) return;
|
if (this.expectedExit) return;
|
||||||
this.vncErrorLevel++;
|
this.vncErrorLevel++;
|
||||||
if (this.vncErrorLevel > 4) {
|
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);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.vnc?.end();
|
this.vnc?.end();
|
||||||
} catch {};
|
} 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);
|
this.vncReconnectTimeout = setTimeout(() => this.startVNC(), 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private vncConnected() {
|
private vncConnected() {
|
||||||
this.vncOpen = true;
|
this.vncOpen = true;
|
||||||
this.emit('vncconnect');
|
this.emit('vncconnect');
|
||||||
|
log("INFO", "VNC Connected");
|
||||||
this.vncErrorLevel = 0;
|
this.vncErrorLevel = 0;
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
this.onVNCSize({height: this.vnc.height, width: this.vnc.width});
|
this.onVNCSize({height: this.vnc.height, width: this.vnc.width});
|
||||||
@@ -222,7 +224,7 @@ export default class QEMUVM extends EventEmitter {
|
|||||||
this.vnc?.end();
|
this.vnc?.end();
|
||||||
clearInterval(this.vncUpdateInterval);
|
clearInterval(this.vncUpdateInterval);
|
||||||
var killTimeout = setTimeout(() => {
|
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);
|
this.qemuProcess?.kill(9);
|
||||||
}, 10000);
|
}, 10000);
|
||||||
var closep = new Promise<void>(async (reso, reje) => {
|
var closep = new Promise<void>(async (reso, reje) => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import EventEmitter from "events";
|
import EventEmitter from "events";
|
||||||
import { Socket } from "net";
|
import { Socket } from "net";
|
||||||
import { Mutex } from "async-mutex";
|
import { Mutex } from "async-mutex";
|
||||||
|
import log from "./log.js";
|
||||||
|
|
||||||
export default class QMPClient extends EventEmitter {
|
export default class QMPClient extends EventEmitter {
|
||||||
socketfile : string;
|
socketfile : string;
|
||||||
@@ -32,7 +33,7 @@ export default class QMPClient extends EventEmitter {
|
|||||||
this.onClose();
|
this.onClose();
|
||||||
}
|
}
|
||||||
this.connected = true;
|
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('data', (data) => this.onData(data));
|
||||||
this.socket.on('close', () => this.onClose());
|
this.socket.on('close', () => this.onClose());
|
||||||
this.once('connected', () => {res();});
|
this.once('connected', () => {res();});
|
||||||
@@ -69,13 +70,13 @@ export default class QMPClient extends EventEmitter {
|
|||||||
switch(msg.event) {
|
switch(msg.event) {
|
||||||
case "STOP":
|
case "STOP":
|
||||||
{
|
{
|
||||||
console.log("[INFO] Resetting QEMU...");
|
log("INFO", "The VM was shut down, restarting...");
|
||||||
this.reboot();
|
this.reboot();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "RESET":
|
case "RESET":
|
||||||
{
|
{
|
||||||
console.log("[INFO] QEMU reset event occurred");
|
log("INFO", "QEMU reset event occured");
|
||||||
this.resume();
|
this.resume();
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { isIP } from 'net';
|
|||||||
import QEMUVM from './QEMUVM.js';
|
import QEMUVM from './QEMUVM.js';
|
||||||
import { Canvas, createCanvas, CanvasRenderingContext2D } from 'canvas';
|
import { Canvas, createCanvas, CanvasRenderingContext2D } from 'canvas';
|
||||||
import { IPData } from './IPData.js';
|
import { IPData } from './IPData.js';
|
||||||
|
import log from './log.js';
|
||||||
|
|
||||||
export default class WSServer {
|
export default class WSServer {
|
||||||
private Config : IConfig;
|
private Config : IConfig;
|
||||||
@@ -157,14 +158,14 @@ export default class WSServer {
|
|||||||
this.onMessage(user, msg);
|
this.onMessage(user, msg);
|
||||||
});
|
});
|
||||||
user.sendMsg(this.getAdduserMsg());
|
user.sendMsg(this.getAdduserMsg());
|
||||||
console.log(`[Connect] From ${user.IP.address}`);
|
log("INFO", `Connect from ${user.IP.address}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
private connectionClosed(user : User) {
|
private connectionClosed(user : User) {
|
||||||
if(user.IP.vote != null) user.IP.vote = null;
|
if(user.IP.vote != null) user.IP.vote = null;
|
||||||
if (this.indefiniteTurn === user) this.indefiniteTurn = null;
|
if (this.indefiniteTurn === user) this.indefiniteTurn = null;
|
||||||
this.clients.splice(this.clients.indexOf(user), 1);
|
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 (!user.username) return;
|
||||||
if (this.TurnQueue.toArray().indexOf(user) !== -1) {
|
if (this.TurnQueue.toArray().indexOf(user) !== -1) {
|
||||||
var hadturn = (this.TurnQueue.peek() === user);
|
var hadturn = (this.TurnQueue.peek() === user);
|
||||||
@@ -506,12 +507,12 @@ export default class WSServer {
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
client.sendMsg(guacutils.encode("rename", "0", status, client.username));
|
client.sendMsg(guacutils.encode("rename", "0", status, client.username));
|
||||||
if (hadName) {
|
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) =>
|
this.clients.filter(c => c.username !== client.username).forEach((c) =>
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
c.sendMsg(guacutils.encode("rename", "1", oldname, client.username)));
|
c.sendMsg(guacutils.encode("rename", "1", oldname, client.username)));
|
||||||
} else {
|
} else {
|
||||||
console.log(`[RENAME] ${client.IP.address} to ${client.username}`);
|
log("INFO", `Rename ${client.IP.address} to ${client.username}`);
|
||||||
this.clients.forEach((c) =>
|
this.clients.forEach((c) =>
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
c.sendMsg(guacutils.encode("adduser", "1", client.username, client.rank)));
|
c.sendMsg(guacutils.encode("adduser", "1", client.username, client.rank)));
|
||||||
|
|||||||
@@ -3,20 +3,23 @@ import IConfig from './IConfig.js';
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import WSServer from './WSServer.js';
|
import WSServer from './WSServer.js';
|
||||||
import QEMUVM from './QEMUVM.js';
|
import QEMUVM from './QEMUVM.js';
|
||||||
|
import log from './log.js';
|
||||||
|
|
||||||
|
log("INFO", "CollabVM Server starting up");
|
||||||
|
|
||||||
// Parse the config file
|
// Parse the config file
|
||||||
|
|
||||||
var Config : IConfig;
|
var Config : IConfig;
|
||||||
|
|
||||||
if (!fs.existsSync("config.toml")) {
|
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);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var configRaw = fs.readFileSync("config.toml").toString();
|
var configRaw = fs.readFileSync("config.toml").toString();
|
||||||
Config = toml.parse(configRaw);
|
Config = toml.parse(configRaw);
|
||||||
} catch (e) {
|
} 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);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
src/log.ts
Normal file
3
src/log.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function log(loglevel : string, message : string) {
|
||||||
|
console[(loglevel === "ERROR" || loglevel === "FATAL") ? "error" : "log"](`[${new Date().toLocaleString()}] [${loglevel}] ${message}`);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user