import * as moment from 'moment'; import * as colors from 'colors/safe'; import * as util from 'util'; import * as path from 'path'; import * as StackTrace from '../stack-trace/stack-trace'; /** * Pads a string toward the center * @param x The value to pad * @param p The padding to use * @param n The end-length of the string * @returns The padded string */ function padCenter(x: string, p: string, n: number) { let z = false; while (x.length < n) { if (z) { z = false; x = p + x; } else { z = true; x = x + p; } } return x; } enum LoggerLevel { Fatal, Error, Warn, Info, Debug, Silly }; /** * Colorizes text * @param level The color level * @param text The text to colorize * @returns The colored text */ function colorize(level: LoggerLevel, text: string): string { switch (level) { case LoggerLevel.Fatal: return colors.bgRed(colors.white(text)); case LoggerLevel.Error: return colors.red(text); case LoggerLevel.Warn: return colors.yellow(text); case LoggerLevel.Info: return colors.green(text); case LoggerLevel.Debug: return colors.blue(text); case LoggerLevel.Silly: return colors.magenta(text); default: return colors.bgWhite(text); } } export default class Logger { private name: string; private console: Console; constructor(name: string, processConsole?: Console) { this.name = name; this.console = processConsole ?? console; } private log(level: LoggerLevel, message: string | null, data?: Error | any): void { let frames: StackTrace.Frame[] = StackTrace.parse(new Error()); let frame = frames[2]; let filePath = frame.fileName; let nameDisplay = filePath ? path.join('.', path.relative(path.join(__dirname, '..'), filePath.replace('.js', '.ts'))) : this.name; // this.name; let prefix: string = `[ ${colorize(level, padCenter(LoggerLevel[level].toLowerCase(), ' ', 5))} | ${nameDisplay} | ${moment().format('HH:mm:ss.SSS')} ]`; let out: string = ''; if (message !== null) { out += message.split('\n').map(o => `${prefix}: ${o}`).join('\n'); } function handleData(data: Error | any) { if (data) { if (message !== null) { out += '\n'; } if (data instanceof Error) { if (data.stack) { out += `${prefix}# ${data.stack.split('\n').join(`\n${prefix}# `)}`; } else { out += `${prefix}# ${data.name}: ${data.message}`; } } else { let s = util.inspect(data, { colors: true }); s = s.split('\n').map(o => `${prefix}$ ${o}`).join('\n'); out += s; } } } if (Array.isArray(data)) { data.forEach(handleData); } else { handleData(data); } this.console.log(out); } public fatal(message: string | null, data?: Error | any): void { this.log(LoggerLevel.Fatal, message, data); } public error(message: string | null, data?: Error | any): void { this.log(LoggerLevel.Error, message, data); } public warn( message: string | null, data?: Error | any): void { this.log(LoggerLevel.Warn, message, data); } public info( message: string | null, data?: Error | any): void { this.log(LoggerLevel.Info, message, data); } public debug(message: string | null, data?: Error | any): void { this.log(LoggerLevel.Debug, message, data); } public silly(message: string | null, data?: Error | any): void { this.log(LoggerLevel.Silly, message, data); } public inspect(object: any, showHidden?: boolean, depth?: number, color?: boolean): string { return util.inspect(object, showHidden, depth, color); } }