/** @module logging */
import * as Types from './types.js';
import { getNumActivePlayers } from './util.js';
const INFO_DUMP_INTERVAL_MS = 60_000;
const INFO_DUMP_DIVIDER_LENGTH = 30;
const GREEN_FONT = '\x1b[32m';
const YELLOW_FONT = '\x1b[33m';
const RED_FONT = '\x1b[31m';
const GRAY_FONT = '\x1b[90m';
const MAGENTA_FONT = '\x1b[35m';
const CYAN_FONT = '\x1b[36m';
const CYAN_BG = '\x1b[46m';
const RESET_FORMAT = '\x1b[0m';
/**
* Encapsulates text in ANSI codes for CLI styling
* @param { string } text text to style
* @param { string } fontStyle constant font style flag, e.g., RED_FONT
* @returns { string } styled text
*/
function styleText(text, fontStyle) {
return `${fontStyle}${text}${RESET_FORMAT}`;
} /* styleText */
/**
* Constructs text fragment associated to severity variant
* @param { 'log'|'warn'|'error' } severity severity of message
* @returns { string } styled severity fragment
*/
function getSeverityFragment(severity) {
switch (severity) {
case 'log':
return `[${styleText('LOG', GREEN_FONT)}] `;
case 'warn':
return `[${styleText('WARN', YELLOW_FONT)}] `;
case 'error':
return `[${styleText('ERROR', RED_FONT)}] `;
default:
return '';
}
} /* getSeverityFragment */
/**
* Constructs text fragment associated to game instance information
* @param { Types.GameInstance } gameInstance associated game instance
* @returns { string } fragment denoting game code
*/
function getGameInstanceFragment(gameInstance) {
return gameInstance ? `[Game ${gameInstance.gameCode}] ` : '';
} /* getGameInstanceFragment */
/**
* Constructs text fragment associated to connection/profile information
* @param { Types.WSConnection } webSocketConnection associated WS connection
* @returns { string } fragment denoting UUID of connected profile
*/
function getConnectionFragment(webSocketConnection) {
return webSocketConnection
? `[UUID ${webSocketConnection.profile.uuid}] `
: '';
} /* getConnectionFragment */
/**
* Styles and displays message along with its attached identifiers
* @param { string } logMessage central message to log
* @param { {
* webSocketConnection: Types.WSConnection,
* gameInstance: Types.GameInstance,
* severity: 'log'|'warn'|'error'|'connection'|'raw'
* } } [info] optional variants and additional identifying information
*/
export function log(
logMessage,
{ webSocketConnection, gameInstance, severity } = {},
) {
if (severity === 'raw') {
console.log(styleText(logMessage, GRAY_FONT));
return;
}
if (severity === 'connection') {
console.log(`[${styleText('CONN', MAGENTA_FONT)}] ${logMessage}`);
return;
}
if (!['log', 'warn', 'error'].includes(severity)) severity = 'log';
console[severity](
getSeverityFragment(severity) +
getGameInstanceFragment(gameInstance) +
getConnectionFragment(webSocketConnection) +
logMessage,
);
} /* log */
/**
* Prints to the console an overview of current overall game information,
* such as total active games and total active users
* @param { Record<number, Types.GameInstance> } gameInstancesByGameCode map of game codes/instances
* @param { Record<Types.UUID, Types.GameInstance> } gameInstancesByPlayerUUID map of game instances/player UUIDs
*/
function logOverview(gameInstancesByGameCode, gameInstancesByPlayerUUID) {
const totalCurrentGamesCount = Object.keys(gameInstancesByGameCode).length;
const activeCurrentGamesCount = Object.values(gameInstancesByGameCode).filter(
(gameInstance) => gameInstance.gameState.isStarted,
).length;
const totalCurrentPlayersCount = Object.keys(
gameInstancesByPlayerUUID,
).length;
const activeCurrentPlayersCount = Object.values(
gameInstancesByGameCode,
).reduce((sum, gameInstance) => sum + getNumActivePlayers(gameInstance), 0);
console.log(
`${styleText('-'.repeat(INFO_DUMP_DIVIDER_LENGTH), CYAN_FONT)}
${styleText('INFO DUMP', CYAN_BG)}
Total current games: ${totalCurrentGamesCount}
Active current games: ${activeCurrentGamesCount}
Total current players: ${totalCurrentPlayersCount}
Active current players: ${activeCurrentPlayersCount}
${styleText('-'.repeat(INFO_DUMP_DIVIDER_LENGTH), CYAN_FONT)}`,
);
} /* logOverview */
/**
* Starts interval to print overview to log every X amount of time
* @param { Record<number, Types.GameInstance> } gameInstancesByGameCode drilled arg
* @param { Record<Types.UUID, Types.GameInstance> } gameInstancesByPlayerUUID drilled arg
*/
export function initializeLoggingOverviews(
gameInstancesByGameCode,
gameInstancesByPlayerUUID,
) {
setInterval(
() => logOverview(gameInstancesByGameCode, gameInstancesByPlayerUUID),
INFO_DUMP_INTERVAL_MS,
);
}