prod v1
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
|
||||
let logFilePath = '';
|
||||
let historyFilePath = '';
|
||||
|
||||
export function initServerLog(logDir: string): void {
|
||||
const dir = path.resolve(logDir);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
logFilePath = path.join(dir, 'matchmaker.log');
|
||||
historyFilePath = path.join(dir, 'history.log');
|
||||
}
|
||||
|
||||
function formatArg(msg: unknown): string {
|
||||
if (typeof msg === 'string') return msg;
|
||||
try {
|
||||
return util.inspect(msg, { depth: 8, colors: false, maxArrayLength: 200 });
|
||||
} catch {
|
||||
return String(msg);
|
||||
}
|
||||
}
|
||||
|
||||
function appendFileSafe(filePath: string, line: string): void {
|
||||
try {
|
||||
fs.appendFileSync(filePath, line, 'utf8');
|
||||
} catch (e) {
|
||||
console.error('server_log: append failed', filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Same visibility rules as console: always. */
|
||||
export function logInfo(...parts: unknown[]): void {
|
||||
const ts = new Date().toISOString();
|
||||
const text = parts.map(formatArg).join(' ');
|
||||
const line = `[${ts}] ${text}\n`;
|
||||
console.log(text);
|
||||
if (logFilePath) appendFileSafe(logFilePath, line);
|
||||
}
|
||||
|
||||
/** Same visibility rules as console: when logLevel > 0. */
|
||||
export function logDebug(logLevel: number, ...parts: unknown[]): void {
|
||||
if (logLevel <= 0) return;
|
||||
const ts = new Date().toISOString();
|
||||
const text = parts.map(formatArg).join(' ');
|
||||
const line = `[${ts}] ${text}\n`;
|
||||
console.log(text);
|
||||
if (logFilePath) appendFileSafe(logFilePath, line);
|
||||
}
|
||||
|
||||
/** Same visibility rules as console: when logLevel > 1. */
|
||||
export function logVerbose(logLevel: number, ...parts: unknown[]): void {
|
||||
if (logLevel <= 1) return;
|
||||
const ts = new Date().toISOString();
|
||||
const text = parts.map(formatArg).join(' ');
|
||||
const line = `[${ts}] ${text}\n`;
|
||||
console.log(text);
|
||||
if (logFilePath) appendFileSafe(logFilePath, line);
|
||||
}
|
||||
|
||||
/** Short human-readable timeline (always appended when called). */
|
||||
export function recordHistory(message: string): void {
|
||||
const safe = message.replace(/\r?\n/g, ' ').trim();
|
||||
if (!safe) return;
|
||||
const ts = new Date().toISOString();
|
||||
const line = `${ts}\t${safe}\n`;
|
||||
if (historyFilePath) appendFileSafe(historyFilePath, line);
|
||||
}
|
||||
|
||||
export function readLogTail(maxLines: number, maxBytes: number): string {
|
||||
if (!logFilePath || !fs.existsSync(logFilePath)) return '';
|
||||
return tailTextFile(logFilePath, maxLines, maxBytes);
|
||||
}
|
||||
|
||||
export function readHistoryTail(maxLines: number, maxBytes: number): string[] {
|
||||
if (!historyFilePath || !fs.existsSync(historyFilePath)) return [];
|
||||
const text = tailTextFile(historyFilePath, maxLines, maxBytes);
|
||||
return text
|
||||
.split('\n')
|
||||
.map((l) => l.trimEnd())
|
||||
.filter((l) => l.length > 0);
|
||||
}
|
||||
|
||||
function tailTextFile(filePath: string, maxLines: number, maxBytes: number): string {
|
||||
const st = fs.statSync(filePath);
|
||||
if (st.size === 0) return '';
|
||||
const readSize = Math.min(st.size, maxBytes);
|
||||
const fd = fs.openSync(filePath, 'r');
|
||||
try {
|
||||
const buf = Buffer.alloc(readSize);
|
||||
fs.readSync(fd, buf, 0, readSize, st.size - readSize);
|
||||
let s = buf.toString('utf8');
|
||||
if (st.size > readSize && !s.startsWith('\n')) {
|
||||
const firstNl = s.indexOf('\n');
|
||||
if (firstNl !== -1) s = s.slice(firstNl + 1);
|
||||
}
|
||||
const lines = s.split('\n');
|
||||
if (lines.length > maxLines) {
|
||||
return lines.slice(-maxLines).join('\n');
|
||||
}
|
||||
return s;
|
||||
} finally {
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
}
|
||||
|
||||
export function logGameStderr(port: number, chunk: Buffer): void {
|
||||
logInfo(`[game:${port}] stderr: ${chunk.toString('utf8').trimEnd()}`);
|
||||
}
|
||||
|
||||
export function logGameExit(port: number, code: number | null): void {
|
||||
logInfo(`Game instance with port ${port} exited with code ${code}`);
|
||||
recordHistory(`Game on port ${port} exited (code ${code == null ? 'unknown' : code})`);
|
||||
}
|
||||
Reference in New Issue
Block a user