add winston logger (#289)

This commit is contained in:
evanpelle
2025-03-18 14:36:30 -07:00
committed by GitHub
parent bd2c270ba3
commit 6499569240
8 changed files with 362 additions and 75 deletions
+27 -23
View File
@@ -18,14 +18,18 @@ import { GameType } from "../core/game/Game";
import { archive, readGameRecord } from "./Archive";
import { gatekeeper, LimiterType } from "./Gatekeeper";
import { metrics } from "./WorkerMetrics";
import { logger } from "./Logger";
const config = getServerConfigFromServer();
let log = logger.child({ component: "Worker" });
// Worker setup
export function startWorker() {
// Get worker ID from environment variable
const workerId = parseInt(process.env.WORKER_ID || "0");
console.log(`Worker ${workerId} starting...`);
log = log.child({ workerId: workerId });
log.info(`Worker ${workerId} starting...`);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -34,7 +38,7 @@ export function startWorker() {
const server = http.createServer(app);
const wss = new WebSocketServer({ server });
const gm = new GameManager(config);
const gm = new GameManager(config, log);
// Set up periodic metrics updates
setInterval(() => {
@@ -81,7 +85,7 @@ export function startWorker() {
gatekeeper.httpHandler(LimiterType.Post, async (req, res) => {
const id = req.params.id;
if (!id) {
console.warn(`cannot create game, id not found`);
log.warn(`cannot create game, id not found`);
return;
}
// TODO: if game is public make sure request came from localhohst!!!
@@ -91,7 +95,7 @@ export function startWorker() {
gc?.gameType == GameType.Public &&
req.headers[config.adminHeader()] !== config.adminToken()
) {
console.warn(
log.warn(
`cannot create public game ${id}, ip ${clientIP} incorrect admin token`,
);
return res.status(400);
@@ -100,7 +104,7 @@ export function startWorker() {
// Double-check this worker should host this game
const expectedWorkerId = config.workerIndex(id);
if (expectedWorkerId !== workerId) {
console.warn(
log.warn(
`This game ${id} should be on worker ${expectedWorkerId}, but this is worker ${workerId}`,
);
return res.status(400);
@@ -108,7 +112,7 @@ export function startWorker() {
const game = gm.createGame(id, gc);
console.log(
log.info(
`Worker ${workerId}: IP ${clientIP} creating game ${game.isPublic() ? "Public" : "Private"} with id ${id}`,
);
res.json(game.gameInfo());
@@ -119,14 +123,14 @@ export function startWorker() {
app.post(
"/api/start_game/:id",
gatekeeper.httpHandler(LimiterType.Post, async (req, res) => {
console.log(`starting private lobby with id ${req.params.id}`);
log.info(`starting private lobby with id ${req.params.id}`);
const game = gm.game(req.params.id);
if (!game) {
return;
}
if (game.isPublic()) {
const clientIP = req.ip || req.socket.remoteAddress || "unknown";
console.log(
log.info(
`cannot start public game ${game.id}, game is public, ip: ${clientIP}`,
);
return;
@@ -142,7 +146,7 @@ export function startWorker() {
// TODO: only update public game if from local host
const lobbyID = req.params.id;
if (req.body.gameType == GameType.Public) {
console.log(`cannot update game ${lobbyID} to public`);
log.info(`cannot update game ${lobbyID} to public`);
return res.status(400);
}
const game = gm.game(lobbyID);
@@ -151,7 +155,7 @@ export function startWorker() {
}
if (game.isPublic()) {
const clientIP = req.ip || req.socket.remoteAddress || "unknown";
console.warn(`cannot update public game ${game.id}, ip: ${clientIP}`);
log.warn(`cannot update public game ${game.id}, ip: ${clientIP}`);
return res.status(400);
}
game.updateGameConfig({
@@ -182,7 +186,7 @@ export function startWorker() {
gatekeeper.httpHandler(LimiterType.Get, async (req, res) => {
const game = gm.game(req.params.id);
if (game == null) {
console.log(`lobby ${req.params.id} not found`);
log.info(`lobby ${req.params.id} not found`);
return res.status(404);
}
res.json(game.gameInfo());
@@ -206,7 +210,7 @@ export function startWorker() {
config.env() != GameEnv.Dev &&
gameRecord.gitCommit != config.gitCommit()
) {
console.warn(
log.warn(
`git commit mismatch for game ${req.params.id}, expected ${config.gitCommit()}, got ${gameRecord.gitCommit}`,
);
return res.status(409).json({
@@ -235,7 +239,7 @@ export function startWorker() {
const clientIP = req.ip || req.socket.remoteAddress || "unknown";
if (!gameRecord) {
console.log("game record not found in request");
log.info("game record not found in request");
res.status(404).json({ error: "Game record not found" });
return;
}
@@ -253,7 +257,7 @@ export function startWorker() {
if (req.headers[config.adminHeader()] !== config.adminToken()) {
return res.status(403).end("Access denied");
}
console.log(`metrics requested on worker ${workerId}`);
log.info(`metrics requested on worker ${workerId}`);
try {
const metricsData = await metrics.register.metrics();
@@ -284,7 +288,7 @@ export function startWorker() {
// Verify this worker should handle this game
const expectedWorkerId = config.workerIndex(clientMsg.gameID);
if (expectedWorkerId !== workerId) {
console.warn(
log.warn(
`Worker mismatch: Game ${clientMsg.gameID} should be on worker ${expectedWorkerId}, but this is worker ${workerId}`,
);
return;
@@ -306,7 +310,7 @@ export function startWorker() {
);
if (!wasFound) {
console.log(
log.info(
`game ${clientMsg.gameID} not found on worker ${workerId}`,
);
// Handle game not found case
@@ -315,7 +319,7 @@ export function startWorker() {
// Handle other message types
} catch (error) {
console.warn(
log.warn(
`error handling websocket message for ${ip}: ${error}`.substring(
0,
250,
@@ -335,21 +339,21 @@ export function startWorker() {
// The load balancer will handle routing to this server based on path
const PORT = config.workerPortByIndex(workerId);
server.listen(PORT, () => {
console.log(`Worker ${workerId} running on http://localhost:${PORT}`);
console.log(`Handling requests with path prefix /w${workerId}/`);
log.info(`Worker ${workerId} running on http://localhost:${PORT}`);
log.info(`Handling requests with path prefix /w${workerId}/`);
// Signal to the master process that this worker is ready
if (process.send) {
process.send({
type: "WORKER_READY",
workerId: workerId,
});
console.log(`Worker ${workerId} signaled ready state to master`);
log.info(`Worker ${workerId} signaled ready state to master`);
}
});
// Global error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(`Error in ${req.method} ${req.path}:`, err);
log.error(`Error in ${req.method} ${req.path}:`, err);
slog({
logKey: "server_error",
msg: `Unhandled exception in ${req.method} ${req.path}: ${err.message}`,
@@ -361,7 +365,7 @@ export function startWorker() {
// Process-level error handlers
process.on("uncaughtException", (err) => {
console.error(`Worker ${workerId} uncaught exception:`, err);
log.error(`Worker ${workerId} uncaught exception:`, err);
slog({
logKey: "uncaught_exception",
msg: `Worker ${workerId} uncaught exception: ${err.message}`,
@@ -371,7 +375,7 @@ export function startWorker() {
});
process.on("unhandledRejection", (reason, promise) => {
console.error(
log.error(
`Worker ${workerId} unhandled rejection at:`,
promise,
"reason:",