import config from "config"; import {v4 as uuid} from "uuid"; import Log from "./models/Log"; import {bufferToUUID} from "./Utils"; export default class Logger { private static logLevel: LogLevelKeys = config.get('log_level'); private static dbLogLevel: LogLevelKeys = config.get('db_log_level'); private static verboseMode: boolean = false; public static verbose() { this.verboseMode = true; this.logLevel = LogLevel[LogLevel[this.logLevel] + 1] || this.logLevel; this.dbLogLevel = LogLevel[LogLevel[this.dbLogLevel] + 1] || this.dbLogLevel; Logger.info('Verbose mode'); } public static isVerboseMode(): boolean { return this.verboseMode; } public static silentError(error: Error, ...message: any[]): string { return this.log('ERROR', message, error, true) || ''; } public static error(error: Error, ...message: any[]): string { return this.log('ERROR', message, error) || ''; } public static warn(...message: any[]) { this.log('WARN', message); } public static info(...message: any[]) { this.log('INFO', message); } public static debug(...message: any[]) { this.log('DEBUG', message); } public static dev(...message: any[]) { this.log('DEV', message); } private static log(level: LogLevelKeys, message: any[], error?: Error, silent: boolean = false): string | null { const levelIndex = LogLevel[level]; if (levelIndex <= LogLevel[this.logLevel]) { if (error) { if (levelIndex > LogLevel.ERROR) this.warn(`Wrong log level ${level} with attached error.`); } else { if (levelIndex <= LogLevel.ERROR) this.warn(`No error attached with log level ${level}.`); } const computedMsg = message.map(v => { if (typeof v === 'string') { return v; } else { return JSON.stringify(v, (key: string, value: any) => { if (value instanceof Object) { if (value.type === 'Buffer') { return `Buffer<${Buffer.from(value.data).toString('hex')}>`; } else if (value !== v) { return `[object Object]`; } } if (typeof value === 'string' && value.length > 96) { return value.substr(0, 96) + '...'; } return value; }, 4); } }).join(' '); const shouldSaveToDB = levelIndex <= LogLevel[this.dbLogLevel]; let output = `[${level}] `; const pad = output.length; const logID = Buffer.alloc(16); uuid({}, logID); let strLogID = bufferToUUID(logID); if (shouldSaveToDB) output += `${strLogID} - `; output += computedMsg.replace(/\n/g, '\n' + ' '.repeat(pad)); switch (level) { case "ERROR": if (silent || !error) { console.error(output); } else { console.error(output, error); } break; case "WARN": console.warn(output); break; case "INFO": console.info(output); break; case "DEBUG": case "DEV": console.debug(output); break; } if (shouldSaveToDB) { const log = Log.create({}); log.setLevel(level); log.message = computedMsg; log.setError(error); log.setLogID(logID); log.save().catch(err => { if (!silent && err.message.indexOf('ECONNREFUSED') < 0) { console.error({save_err: err, error}); } }); } return strLogID; } return null; } private constructor() { } } export enum LogLevel { ERROR, WARN, INFO, DEBUG, DEV, } export type LogLevelKeys = keyof typeof LogLevel;