diff --git a/manager/src/command.ts b/manager/src/command.ts index d3cf39a..94ed2ef 100644 --- a/manager/src/command.ts +++ b/manager/src/command.ts @@ -1,5 +1,7 @@ import * as amqp from "amqplib"; +import { getLogger } from "./logger"; +const logger = getLogger("CommandService"); export class CommandService { @@ -27,8 +29,8 @@ export class CommandService { ); - console.log( - "Command gesendet:", + logger.debug( + "Command sent", command ); diff --git a/manager/src/config.ts b/manager/src/config.ts new file mode 100644 index 0000000..092d6f8 --- /dev/null +++ b/manager/src/config.ts @@ -0,0 +1,13 @@ +export const RABBITMQ_URL = "amqp://manager:gigi1337@192.168.1.74"; + +export const LIFECYCLE_CHECK_INTERVAL = 10000; +export const LIFECYCLE_EMPTY_TIMEOUT = 300000; +export const LIFECYCLE_STARTUP_GRACE_PERIOD = 60000; +export const LIFECYCLE_MIN_UPTIME = 300000; +export const LIFECYCLE_SCALE_DOWN_THRESHOLD = 0.30; +export const LIFECYCLE_SCALE_COOLDOWN = 60000; +export const LIFECYCLE_CRASH_TIMEOUT = 30000; +export const DEFAULT_DESIRED_SERVERS = 1; + +export const GAME_PORT_START = 7777; +export const QUERY_PORT_START = 27015; diff --git a/manager/src/index.ts b/manager/src/index.ts index 18ed8a5..2317e83 100644 --- a/manager/src/index.ts +++ b/manager/src/index.ts @@ -1,4 +1,4 @@ -import { Rabbit } from "./rabbit"; +import { Rabbit } from "./rabbit"; import { WrapperManager } from "./wrapperManager"; import { WrapperRegistry } from "./wrapperRegistry"; import { ServerRegistry } from "./serverRegistry"; @@ -6,6 +6,9 @@ import { ServerManager } from "./serverManager"; import { PortManager } from "./portManager"; import { ServerStatusManager } from "./serverStatusManager"; import { ServerLifecycle } from "./serverLifecycle"; +import { getLogger } from "./logger"; + +const logger = getLogger("Index"); const wrapperRegistry = @@ -34,8 +37,16 @@ async function main(){ await rabbit.connect(); + logger.info("RabbitMQ-Verbindung hergestellt"); + await portManager.init(); + logger.info("Persistenz initialisiert"); + await wrapperRegistry.init(); + logger.debug("WrapperRegistry initialisiert"); + + await serverRegistry.init(); + logger.debug("ServerRegistry initialisiert"); const wrapperManager = new WrapperManager( @@ -51,6 +62,7 @@ async function main(){ await wrapperManager.start(); + logger.info("WrapperManager erfolgreich gestartet"); const serverManager = @@ -77,6 +89,7 @@ async function main(){ lifecycle.start(); + logger.debug("ServerLifecycle gestartet"); const statusManager = new ServerStatusManager( @@ -93,13 +106,11 @@ async function main(){ - console.log( - "Manager gestartet" - ); + logger.info("Manager gestartet"); } -main(); \ No newline at end of file +main(); diff --git a/manager/src/logger.ts b/manager/src/logger.ts new file mode 100644 index 0000000..74ee7ec --- /dev/null +++ b/manager/src/logger.ts @@ -0,0 +1,62 @@ +declare const process: { + env: Record; +}; + +type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR"; + +const LEVELS: Record = { + DEBUG: 0, + INFO: 1, + WARN: 2, + ERROR: 3, +}; + +const DEFAULT_LOG_LEVEL: LogLevel = "INFO"; + +const configuredLevel = + (process.env.LOG_LEVEL ?? "").toUpperCase() as LogLevel; + +const currentLogLevel: LogLevel = + configuredLevel in LEVELS ? configuredLevel : DEFAULT_LOG_LEVEL; + +const currentLogLevelValue = LEVELS[currentLogLevel]; + +function shouldLog(level: LogLevel): boolean { + return LEVELS[level] >= currentLogLevelValue; +} + +function formatMessage( + level: LogLevel, + component: string, + message: unknown +): string { + return `${new Date().toISOString()} ${level} [${component}] ${message}`; +} + +class Logger { + constructor(private component: string) {} + + debug(message: unknown, ...args: unknown[]): void { + if (!shouldLog("DEBUG")) return; + console.log(formatMessage("DEBUG", this.component, message), ...args); + } + + info(message: unknown, ...args: unknown[]): void { + if (!shouldLog("INFO")) return; + console.log(formatMessage("INFO", this.component, message), ...args); + } + + warn(message: unknown, ...args: unknown[]): void { + if (!shouldLog("WARN")) return; + console.warn(formatMessage("WARN", this.component, message), ...args); + } + + error(message: unknown, ...args: unknown[]): void { + if (!shouldLog("ERROR")) return; + console.error(formatMessage("ERROR", this.component, message), ...args); + } +} + +export function getLogger(component: string): Logger { + return new Logger(component); +} diff --git a/manager/src/persistence/jsonFileStore.ts b/manager/src/persistence/jsonFileStore.ts new file mode 100644 index 0000000..aaae617 --- /dev/null +++ b/manager/src/persistence/jsonFileStore.ts @@ -0,0 +1,42 @@ +import { promises as fs } from "fs"; +import { PersistenceStore } from "./persistenceStore"; +import { getLogger } from "../logger"; + +const logger = getLogger("JsonFileStore"); + +export class JsonFileStore implements PersistenceStore { + constructor( + private readonly filePath: string + ) {} + + async load(): Promise { + try { + const fileContents = await fs.readFile(this.filePath, "utf-8"); + return JSON.parse(fileContents) as T; + } + catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return null; + } + logger.error( + `Fehler beim Laden der Persistenzdatei ${this.filePath}:`, + error + ); + throw error; + } + } + + async save(data: T): Promise { + try { + const fileContents = JSON.stringify(data, null, 4); + await fs.writeFile(this.filePath, fileContents, "utf-8"); + } + catch (error) { + logger.error( + `Fehler beim Speichern der Persistenzdatei ${this.filePath}:`, + error + ); + throw error; + } + } +} diff --git a/manager/src/persistence/persistenceStore.ts b/manager/src/persistence/persistenceStore.ts new file mode 100644 index 0000000..fc6ebdb --- /dev/null +++ b/manager/src/persistence/persistenceStore.ts @@ -0,0 +1,4 @@ +export interface PersistenceStore { + load(): Promise; + save(data: T): Promise; +} diff --git a/manager/src/portManager.ts b/manager/src/portManager.ts index 2a7bae0..a96f742 100644 --- a/manager/src/portManager.ts +++ b/manager/src/portManager.ts @@ -1,118 +1,124 @@ +import { GAME_PORT_START, QUERY_PORT_START } from "./config"; +import { JsonFileStore } from "./persistence/jsonFileStore"; +import { getLogger } from "./logger"; + +const logger = getLogger("PortManager"); + +interface PortManagerState { + usedPorts: number[]; + usedQueryPorts: number[]; +} + export class PortManager { - - private usedPorts = new Set(); - - private usedQueryPorts = new Set(); - - - - private gamePortStart = - 7777; - + GAME_PORT_START; private queryPortStart = - 27015; + QUERY_PORT_START; + private readonly persistence = + new JsonFileStore( + "portManager-state.json" + ); + async init(){ + const state = await this.persistence.load(); + if(!state) + return; + this.usedPorts = + new Set(state.usedPorts); + + this.usedQueryPorts = + new Set(state.usedQueryPorts); + } allocate(){ - - let port = this.gamePortStart; - - while( this.usedPorts.has(port) ){ - port++; - } - - - let queryPort = this.queryPortStart; - - while( this.usedQueryPorts.has(queryPort) ){ - queryPort++; - } - - - - this.usedPorts.add( port ); - this.usedQueryPorts.add( queryPort ); + logger.debug(`Port erfolgreich reserviert: ${port}`); + logger.debug(`Query-Port erfolgreich reserviert: ${queryPort}`); - + this.saveState(); return { - - port, - - queryPort - - }; - - } - - - - - - release( port:number, queryPort:number ){ - - this.usedPorts.delete( + const removedPort = this.usedPorts.delete( port ); - - this.usedQueryPorts.delete( + const removedQueryPort = this.usedQueryPorts.delete( queryPort ); + if (!removedPort || !removedQueryPort) { + logger.warn( + `Unerwarteter Port-Zustand während der Freigabe: port=${port}, queryPort=${queryPort}` + ); + } else { + logger.debug(`Port wieder freigegeben: ${port}`); + logger.debug(`Query-Port wieder freigegeben: ${queryPort}`); + } + + this.saveState(); } + private saveState(){ + const state: PortManagerState = { + usedPorts: Array.from(this.usedPorts), + usedQueryPorts: Array.from(this.usedQueryPorts) + }; - - + this.persistence + .save(state) + .catch(error => { + logger.error( + "Fehler beim Speichern des PortManager-Zustands:", + error + ); + }); + } } diff --git a/manager/src/rabbit.ts b/manager/src/rabbit.ts index 1a216c6..9499860 100644 --- a/manager/src/rabbit.ts +++ b/manager/src/rabbit.ts @@ -1,5 +1,8 @@ import * as amqp from "amqplib"; +import { RABBITMQ_URL } from "./config"; +import { getLogger } from "./logger"; +const logger = getLogger("Rabbit"); export class Rabbit { @@ -12,7 +15,7 @@ export class Rabbit { this.connection = await amqp.connect( - "amqp://manager:gigi1337@192.168.1.74" + RABBITMQ_URL ); @@ -20,8 +23,8 @@ export class Rabbit { await this.connection.createChannel(); - console.log( - "RabbitMQ verbunden" + logger.info( + "Connected to RabbitMQ" ); } diff --git a/manager/src/serverLifecycle.ts b/manager/src/serverLifecycle.ts index 3313fb6..d44c139 100644 --- a/manager/src/serverLifecycle.ts +++ b/manager/src/serverLifecycle.ts @@ -1,20 +1,33 @@ +import { getLogger } from "./logger"; import { ServerRegistry } from "./serverRegistry"; import { ServerManager } from "./serverManager"; +import { + LIFECYCLE_CHECK_INTERVAL, + LIFECYCLE_EMPTY_TIMEOUT, + LIFECYCLE_STARTUP_GRACE_PERIOD, + LIFECYCLE_MIN_UPTIME, + LIFECYCLE_SCALE_DOWN_THRESHOLD, + LIFECYCLE_SCALE_COOLDOWN, + LIFECYCLE_CRASH_TIMEOUT, + DEFAULT_DESIRED_SERVERS +} from "./config"; +const logger = getLogger("ServerLifecycle"); + export class ServerLifecycle { private creatingServers = 0; - private emptyTimeout = 300000; // 5 Minuten + private emptyTimeout = LIFECYCLE_EMPTY_TIMEOUT; private lastScaleUp = 0; - private scaleCooldown = 60000; // 1 Minute + private scaleCooldown = LIFECYCLE_SCALE_COOLDOWN; @@ -25,7 +38,7 @@ export class ServerLifecycle { private serverManager: ServerManager, - private desiredServers:number = 1 + private desiredServers:number = DEFAULT_DESIRED_SERVERS ){} @@ -36,7 +49,7 @@ export class ServerLifecycle { start(){ - console.log( + logger.info( "Server Lifecycle gestartet" ); @@ -47,7 +60,7 @@ export class ServerLifecycle { this.checkServers(); - },10000); + },LIFECYCLE_CHECK_INTERVAL); } @@ -59,7 +72,7 @@ export class ServerLifecycle { setDesiredServers(count:number){ - console.log( + logger.debug( "Neue Soll Server Anzahl:", count ); @@ -110,13 +123,19 @@ export class ServerLifecycle { server.status = "RUNNING"; + server.startedAt = + Date.now(); + + server.emptySince = + undefined; + this.serverRegistry.update( server ); - console.log( + logger.info( "Server RUNNING:", server.name, "PID:", @@ -147,7 +166,7 @@ export class ServerLifecycle { Date.now() - server.lastSeen > - 30000 + LIFECYCLE_CRASH_TIMEOUT ){ @@ -160,7 +179,7 @@ export class ServerLifecycle { ); - console.log( + logger.error( "Server abgestürzt:", server.name ); @@ -186,17 +205,23 @@ export class ServerLifecycle { if(!server.emptySince){ + if( + server.startedAt && + Date.now() - + server.startedAt < + LIFECYCLE_STARTUP_GRACE_PERIOD + ){ + break; + } server.emptySince = Date.now(); - this.serverRegistry.update( server ); - - console.log( + logger.debug( "Server leer:", server.name ); @@ -213,7 +238,7 @@ export class ServerLifecycle { if(server.emptySince){ - console.log( + logger.debug( "Server wieder aktiv:", server.name ); @@ -248,7 +273,7 @@ export class ServerLifecycle { - console.log( + logger.error( "Server abgestürzt erkannt:", server.name ); @@ -359,7 +384,7 @@ export class ServerLifecycle { - console.log( + logger.debug( "Kapazität:", @@ -452,7 +477,7 @@ export class ServerLifecycle { - console.log( + logger.info( "Auslastung hoch - starte zusätzlichen Server" @@ -501,11 +526,20 @@ export class ServerLifecycle { server.emptySince && + server.startedAt && + + Date.now() - + server.startedAt + >= + LIFECYCLE_MIN_UPTIME && Date.now() - server.emptySince > - this.emptyTimeout + this.emptyTimeout && + + usage < + LIFECYCLE_SCALE_DOWN_THRESHOLD ) @@ -559,18 +593,20 @@ export class ServerLifecycle { - console.log( - + logger.info( "Stoppe leeren Server:", - server.name - ); + logger.info( + "Stoppe leeren Server:", + server.name + ); + this.serverManager.stopServer( server.id @@ -597,7 +633,7 @@ export class ServerLifecycle { - console.log( + logger.info( "Starte Server:", @@ -647,7 +683,7 @@ export class ServerLifecycle { - console.error( + logger.error( "Server Erstellung fehlgeschlagen:", diff --git a/manager/src/serverManager.ts b/manager/src/serverManager.ts index 90d9fb5..028f729 100644 --- a/manager/src/serverManager.ts +++ b/manager/src/serverManager.ts @@ -1,3 +1,4 @@ +import { getLogger } from "./logger"; import { Rabbit } from "./rabbit"; import { WrapperRegistry } from "./wrapperRegistry"; import { ServerRegistry, ServerInfo } from "./serverRegistry"; @@ -6,6 +7,8 @@ import { generateServerName } from "./nameGenerator"; import { PortManager } from "./portManager"; import { ServerStartCommand, ServerStopCommand } from "server-manager-shared"; +const logger = getLogger("ServerManager"); + export class ServerManager { @@ -39,7 +42,7 @@ export class ServerManager { if(!wrapper){ - console.log( + logger.warn( "Kein Wrapper verfügbar" ); @@ -208,11 +211,11 @@ export class ServerManager { - console.log( + logger.info( - "Server Start gesendet:", + "Starting server:", - server + server.id ); @@ -232,8 +235,8 @@ export class ServerManager { if(!server){ - console.log( - "Server nicht gefunden:", + logger.warn( + "Server not found:", serverId ); @@ -251,8 +254,8 @@ export class ServerManager { if(!wrapper){ - console.log( - "Wrapper nicht gefunden:", + logger.warn( + "Wrapper not found:", server.wrapperId ); @@ -298,9 +301,9 @@ export class ServerManager { - console.log( - "Server Stop gesendet:", - server.name + logger.info( + "Stopping server:", + serverId ); diff --git a/manager/src/serverRegistry.ts b/manager/src/serverRegistry.ts index 4950dc2..545687c 100644 --- a/manager/src/serverRegistry.ts +++ b/manager/src/serverRegistry.ts @@ -1,4 +1,8 @@ +import { getLogger } from "./logger"; import { WrapperStatusMessage } from "server-manager-shared"; +import { JsonFileStore } from "./persistence/jsonFileStore"; + +const logger = getLogger("ServerRegistry"); export interface ServerInfo { @@ -33,9 +37,7 @@ export interface ServerInfo { emptySince?:number; - - created:number; - + startedAt?:number; lastSeen:number; @@ -43,16 +45,84 @@ export interface ServerInfo { } +interface ServerRegistryState { + servers: Array<{ + id:string; + name:string; + wrapperId:string; + port:number; + queryPort:number; + created:number; + }>; +} + + export class ServerRegistry { - private servers = new Map(); + private readonly persistence = + new JsonFileStore( + "serverRegistry-state.json" + ); + + + async init(): Promise { + const state = await this.persistence.load(); + + if (!state) + return; + + for (const serverData of state.servers) { + this.servers.set( + serverData.id, + { + id: serverData.id, + name: serverData.name, + wrapperId: serverData.wrapperId, + port: serverData.port, + queryPort: serverData.queryPort, + created: serverData.created, + status: "STARTING", + players: 0, + maxPlayers: 0, + lastSeen: Date.now() + } + ); + } + } + + private toState(): ServerRegistryState { + return { + servers: Array.from(this.servers.values()).map( + server => ({ + id: server.id, + name: server.name, + wrapperId: server.wrapperId, + port: server.port, + queryPort: server.queryPort, + created: server.created + }) + ) + }; + } + + private async saveState(): Promise { + await this.persistence.save( + this.toState() + ); + } + + + private persist(): void { + void this.saveState(); + } + @@ -69,12 +139,13 @@ export class ServerRegistry { ); - console.log( - "Server registriert:", - server + logger.info( + "Server added:", + server.id ); + this.persist(); } @@ -128,6 +199,7 @@ export class ServerRegistry { ); + this.persist(); } @@ -144,12 +216,13 @@ export class ServerRegistry { ); - console.log( - "Server entfernt:", + logger.info( + "Server removed:", id ); + this.persist(); } @@ -162,8 +235,8 @@ export class ServerRegistry { list(){ - console.log( - "\n=== SERVER ÜBERSICHT ===" + logger.info( + "Server list output" ); @@ -177,7 +250,7 @@ export class ServerRegistry { if(servers.length === 0){ - console.log( + logger.info( "Keine Server vorhanden" ); @@ -192,7 +265,7 @@ export class ServerRegistry { for(const server of servers){ - console.log({ + logger.info({ id: @@ -260,8 +333,8 @@ export class ServerRegistry { if(server.status === "STOPPING"){ - console.log( - "Server sauber beendet:", + logger.debug( + "Server cleanly stopped:", server.name ); @@ -285,8 +358,8 @@ export class ServerRegistry { ); - console.log( - "Server abgestürzt:", + logger.warn( + "Server crashed:", server.name ); @@ -354,8 +427,8 @@ export class ServerRegistry { if(oldStatus !== server.status){ - console.log( - "Status Änderung:", + logger.debug( + "Status change:", server.name, oldStatus, "->", diff --git a/manager/src/serverStatusManager.ts b/manager/src/serverStatusManager.ts index e9b9a3e..11f3def 100644 --- a/manager/src/serverStatusManager.ts +++ b/manager/src/serverStatusManager.ts @@ -1,7 +1,9 @@ +import { getLogger } from "./logger"; import { Rabbit } from "./rabbit"; import { ServerRegistry } from "./serverRegistry"; import { ServerStartedEvent, ServerStoppedEvent } from "server-manager-shared"; +const logger = getLogger("ServerStatusManager"); export class ServerStatusManager { @@ -35,7 +37,7 @@ export class ServerStatusManager { - console.log( + logger.info( "Warte auf Server Status..." ); @@ -102,7 +104,7 @@ export class ServerStatusManager { default: - console.log( + logger.warn( "Unbekannter Server Status:", data ); @@ -168,11 +170,11 @@ export class ServerStatusManager { - console.log( + logger.info( - "Server läuft:", + "Server started:", - server.name + data.serverId ); @@ -221,11 +223,11 @@ export class ServerStatusManager { - console.log( + logger.info( - "Server crashed:", + "Server stopped:", - server.name + data.serverId ); diff --git a/manager/src/wrapperManager.ts b/manager/src/wrapperManager.ts index ca853c8..0d68591 100644 --- a/manager/src/wrapperManager.ts +++ b/manager/src/wrapperManager.ts @@ -1,3 +1,4 @@ +import { getLogger } from "./logger"; import { Rabbit } from "./rabbit"; import { randomUUID } from "crypto"; import { WrapperRegistry, WrapperInfo } from "./wrapperRegistry"; @@ -15,6 +16,8 @@ type WrapperMessage = | WrapperHeartbeatMessage | WrapperStatusMessage; +const logger = getLogger("WrapperManager"); + export class WrapperManager { @@ -43,7 +46,7 @@ export class WrapperManager { - console.log( + logger.info( "Warte auf Wrapper..." ); @@ -99,7 +102,12 @@ export class WrapperManager { case "wrapper.status": - this.registry.updateStatus( + logger.debug( + "Status update received from wrapper-id", + data.wrapperId + ); + + this.registry.updateStatus( data ); @@ -115,7 +123,7 @@ export class WrapperManager { default: - console.log( +logger.warn( "Unbekannte Nachricht:", data ); @@ -185,7 +193,7 @@ export class WrapperManager { - console.log( + logger.debug( "Neue Wrapper ID:", id ); @@ -414,7 +422,7 @@ export class WrapperManager { - console.log( + logger.debug( "Heartbeat:", @@ -523,23 +531,9 @@ export class WrapperManager { - console.log( + logger.info( - "\n======================" - - ); - - - console.log( - - " WRAPPER ÜBERSICHT " - - ); - - - console.log( - - "======================\n" + "Wrapper Übersicht ausgegeben" ); @@ -550,7 +544,7 @@ export class WrapperManager { if(wrappers.length===0){ - console.log( + logger.info( "Keine Wrapper registriert" @@ -571,7 +565,7 @@ export class WrapperManager { - console.log(` + logger.info(` ID: ${wrapper.id} diff --git a/manager/src/wrapperRegistry.ts b/manager/src/wrapperRegistry.ts index e64d2bc..818f587 100644 --- a/manager/src/wrapperRegistry.ts +++ b/manager/src/wrapperRegistry.ts @@ -1,4 +1,8 @@ +import { getLogger } from "./logger"; import { WrapperStatusMessage } from "server-manager-shared"; +import { JsonFileStore } from "./persistence/jsonFileStore"; + +const logger = getLogger("WrapperRegistry"); export interface WrapperInfo { @@ -25,35 +29,91 @@ export interface WrapperInfo { } +interface WrapperRegistryState { + wrappers: Array<{ + id:string; + hostname:string; + commandQueue:string; + cpu:number; + ram:number; + }>; +} + export class WrapperRegistry { - private wrappers = new Map(); + private readonly persistence = + new JsonFileStore( + "wrapperRegistry-state.json" + ); + + async init(): Promise { + const state = await this.persistence.load(); + + if (!state) + return; + + for (const wrapperData of state.wrappers) { + this.wrappers.set( + wrapperData.id, + { + id: wrapperData.id, + hostname: wrapperData.hostname, + commandQueue: wrapperData.commandQueue, + cpu: wrapperData.cpu, + ram: wrapperData.ram, + ramFree: wrapperData.ram, + online: false, + lastHeartbeat: 0, + servers: [] + } + ); + } + } + + private toState(): WrapperRegistryState { + return { + wrappers: Array.from(this.wrappers.values()).map( + wrapper => ({ + id: wrapper.id, + hostname: wrapper.hostname, + commandQueue: wrapper.commandQueue, + cpu: wrapper.cpu, + ram: wrapper.ram + }) + ) + }; + } + + private async saveState(): Promise { + await this.persistence.save( + this.toState() + ); + } + add(wrapper:WrapperInfo){ - this.wrappers.set( wrapper.id, wrapper ); - - console.log( - "Wrapper gespeichert:", + logger.info( + "Wrapper added:", wrapper.id ); - + void this.saveState(); } @@ -110,13 +170,12 @@ export class WrapperRegistry { update(wrapper:WrapperInfo){ - this.wrappers.set( wrapper.id, wrapper ); - + void this.saveState(); } @@ -140,7 +199,7 @@ export class WrapperRegistry { wrapper.online = false; - console.log( + logger.warn( "Wrapper offline:", wrapper.id ); @@ -156,7 +215,6 @@ export class WrapperRegistry { updateStatus(data: WrapperStatusMessage){ - const wrapper = this.wrappers.get( data.wrapperId diff --git a/wrapper/src/index.ts b/wrapper/src/index.ts index c7852b4..5c76c75 100644 --- a/wrapper/src/index.ts +++ b/wrapper/src/index.ts @@ -10,6 +10,9 @@ import { ServerStartCommand, ServerStopCommand } from "server-manager-shared"; +import { getLogger } from "./logger"; + +const logger = getLogger("Wrapper"); class Wrapper { @@ -36,6 +39,7 @@ class Wrapper { const config = loadConfig(); + logger.info("Konfiguration geladen"); if(config.wrapperId){ @@ -46,7 +50,7 @@ class Wrapper { - console.log( + logger.debug( "Starte mit vorhandener ID:", this.id ); @@ -55,7 +59,7 @@ class Wrapper { }else{ - console.log( + logger.warn( "Keine Wrapper ID vorhanden - neue Registrierung" ); @@ -77,8 +81,8 @@ class Wrapper { - console.log( - "RabbitMQ verbunden" + logger.info( + "RabbitMQ Verbindung hergestellt" ); @@ -136,9 +140,9 @@ class Wrapper { - console.log( + logger.debug( "Antwort vom Manager:", - data + data.type ); @@ -162,14 +166,17 @@ class Wrapper { - console.log( + logger.info( + "Registrierung beim Manager erfolgreich" + ); + logger.debug( "Wrapper ID gespeichert:", this.id ); if(!this.id){ - console.error( + logger.error( "Keine Wrapper ID erhalten!" ); @@ -272,7 +279,7 @@ class Wrapper { - console.log( + logger.debug( "Registrierung gesendet", this.id ?? "ohne ID" ); @@ -319,7 +326,7 @@ class Wrapper { message?: unknown; }; - console.log( + logger.debug( "TEST COMMAND:", command.message ); @@ -356,9 +363,9 @@ class Wrapper { default: - console.log( + logger.warn( "Unbekannter Command:", - rawCommand + commandType ); @@ -376,7 +383,7 @@ class Wrapper { - console.log( + logger.debug( "Warte auf Commands:", queue ); @@ -392,7 +399,7 @@ class Wrapper { heartbeat(){ - console.log("Heartbeat gestartet"); + logger.debug("Heartbeat gestartet"); setInterval(()=>{ @@ -438,7 +445,7 @@ class Wrapper { - console.log( + logger.debug( "Heartbeat gesendet" ); diff --git a/wrapper/src/logger.ts b/wrapper/src/logger.ts new file mode 100644 index 0000000..74ee7ec --- /dev/null +++ b/wrapper/src/logger.ts @@ -0,0 +1,62 @@ +declare const process: { + env: Record; +}; + +type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR"; + +const LEVELS: Record = { + DEBUG: 0, + INFO: 1, + WARN: 2, + ERROR: 3, +}; + +const DEFAULT_LOG_LEVEL: LogLevel = "INFO"; + +const configuredLevel = + (process.env.LOG_LEVEL ?? "").toUpperCase() as LogLevel; + +const currentLogLevel: LogLevel = + configuredLevel in LEVELS ? configuredLevel : DEFAULT_LOG_LEVEL; + +const currentLogLevelValue = LEVELS[currentLogLevel]; + +function shouldLog(level: LogLevel): boolean { + return LEVELS[level] >= currentLogLevelValue; +} + +function formatMessage( + level: LogLevel, + component: string, + message: unknown +): string { + return `${new Date().toISOString()} ${level} [${component}] ${message}`; +} + +class Logger { + constructor(private component: string) {} + + debug(message: unknown, ...args: unknown[]): void { + if (!shouldLog("DEBUG")) return; + console.log(formatMessage("DEBUG", this.component, message), ...args); + } + + info(message: unknown, ...args: unknown[]): void { + if (!shouldLog("INFO")) return; + console.log(formatMessage("INFO", this.component, message), ...args); + } + + warn(message: unknown, ...args: unknown[]): void { + if (!shouldLog("WARN")) return; + console.warn(formatMessage("WARN", this.component, message), ...args); + } + + error(message: unknown, ...args: unknown[]): void { + if (!shouldLog("ERROR")) return; + console.error(formatMessage("ERROR", this.component, message), ...args); + } +} + +export function getLogger(component: string): Logger { + return new Logger(component); +} diff --git a/wrapper/src/processManager.ts b/wrapper/src/processManager.ts index c985c3d..4627e1a 100644 --- a/wrapper/src/processManager.ts +++ b/wrapper/src/processManager.ts @@ -1,6 +1,9 @@ import { spawn, ChildProcess } from "child_process"; import { ServerStartCommand } from "server-manager-shared"; import { SERVER_SCRIPT_PATH, SERVER_WORKING_DIRECTORY } from "./config"; +import { getLogger } from "./logger"; + +const logger = getLogger("ProcessManager"); interface ServerProcess { @@ -46,7 +49,7 @@ export class ProcessManager { - console.log( + logger.debug( "Starte Game Server:", data.name ); @@ -89,11 +92,9 @@ export class ProcessManager { data=>{ - console.log( - + logger.debug( `[${data.name}]`, data.toString() - ); @@ -112,7 +113,7 @@ export class ProcessManager { data=>{ - console.error( + logger.error( "SERVER ERROR:", data.toString() @@ -141,7 +142,7 @@ export class ProcessManager { if(server){ - console.log( + logger.info( "Server beendet:", server.name, code @@ -199,18 +200,12 @@ export class ProcessManager { - console.log( - + logger.info( "Server gestartet:", - { - id:data.serverId, - pid:process.pid - } - ); @@ -238,7 +233,7 @@ export class ProcessManager { if(!server){ - console.log( + logger.warn( "Server nicht gefunden:", @@ -256,12 +251,9 @@ export class ProcessManager { - console.log( - + logger.debug( "Stoppe Server:", - server.name - ); diff --git a/wrapper/src/queryManager.ts b/wrapper/src/queryManager.ts index 48484f6..4a71360 100644 --- a/wrapper/src/queryManager.ts +++ b/wrapper/src/queryManager.ts @@ -1,7 +1,9 @@ import { queryGameServerInfo } from "steam-server-query"; import { ProcessManager } from "./processManager"; import { WRAPPER_QUERY_INTERVAL } from "./config"; +import { getLogger } from "./logger"; +const logger = getLogger("QueryManager"); export class QueryManager { @@ -16,7 +18,7 @@ export class QueryManager { start(){ - console.log( + logger.debug( "Query Manager gestartet" ); @@ -73,12 +75,10 @@ export class QueryManager { - console.log( - - "Query:", + logger.debug( + "Query erfolgreich:", server.name, `${server.players}/${server.maxPlayers}` - ); @@ -86,11 +86,9 @@ export class QueryManager { catch(err){ - console.log( - + logger.warn( "Query fehlgeschlagen:", server.name - ); diff --git a/wrapper/src/statusReporter.ts b/wrapper/src/statusReporter.ts index 40e593e..78b4210 100644 --- a/wrapper/src/statusReporter.ts +++ b/wrapper/src/statusReporter.ts @@ -4,7 +4,9 @@ import os from "os"; import { QueryManager } from "./queryManager"; import { WrapperStatusMessage } from "server-manager-shared"; import { WRAPPER_STATUS_INTERVAL } from "./config"; +import { getLogger } from "./logger"; +const logger = getLogger("StatusReporter"); export class StatusReporter { @@ -25,7 +27,7 @@ export class StatusReporter { start(){ - console.log( + logger.debug( "Status Reporter gestartet" ); @@ -147,12 +149,9 @@ export class StatusReporter { - console.log( - + logger.debug( "Status gesendet:", - servers.length, - "Server" );