type LogLevel = "debug" | "info" | "warn" | "error"; interface LogEntry { level: LogLevel; message: string; timestamp: string; requestId?: string; module?: string; [key: string]: unknown; } const LOG_LEVELS: Record = { debug: 0, info: 1, warn: 2, error: 3, }; const currentLevel: LogLevel = (process.env.LOG_LEVEL as LogLevel) ?? (process.env.NODE_ENV === "production" ? "info" : "debug"); let requestIdCounter = 0; export function generateRequestId(): string { requestIdCounter += 1; return `${Date.now().toString(36)}-${requestIdCounter.toString(36)}`; } function shouldLog(level: LogLevel): boolean { // eslint-disable-next-line security/detect-object-injection -- LOG_LEVELS keyed by LogLevel union return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel]; } function formatLog(entry: LogEntry): string { return JSON.stringify(entry); } function writeLog(entry: LogEntry): void { if (!shouldLog(entry.level)) return; const formatted = formatLog(entry); switch (entry.level) { case "error": console.error(formatted); break; case "warn": console.warn(formatted); break; default: console.log(formatted); break; } } export const logger = { debug(message: string, meta: Record = {}): void { writeLog({ level: "debug", message, timestamp: new Date().toISOString(), ...meta }); }, info(message: string, meta: Record = {}): void { writeLog({ level: "info", message, timestamp: new Date().toISOString(), ...meta }); }, warn(message: string, meta: Record = {}): void { writeLog({ level: "warn", message, timestamp: new Date().toISOString(), ...meta }); }, error(message: string, meta: Record = {}): void { writeLog({ level: "error", message, timestamp: new Date().toISOString(), ...meta }); }, };