UNPKG

simpl-loggar

Version:

Simple node.js logger

332 lines (331 loc) 13 kB
import chalk from 'chalk'; import Utils from './utils.js'; import * as enums from '../enums/index.js'; /** * Log passed data and save it in local files. */ export default class Log { static _counter = []; static _prefix = null; static _styleJson = true; static _logRules = new Map(); static _config = undefined; static _memoryOnly = false; /** * Get current date. * @returns Formatted date for log files. */ static getDate() { const date = new Date(); const h = date.getHours().toString().length === 1 ? `0${date.getHours()}:` : `${date.getHours()}:`; const m = date.getMinutes().toString().length === 1 ? `0${date.getMinutes()}:` : `${date.getMinutes()}:`; const s = date.getSeconds().toString().length === 1 ? `0${date.getSeconds()}` : `${date.getSeconds()}`; return `${h}${m}${s}`; } /** * Sets a rule for logs. If the rule returns true, the log will be shown; otherwise, it will not. * This is useful for adding additional rules to control logging behavior in production environments. * This rule will only be used to validate messages. Targets will not be validated. * If param used in this logger is not a string, it will be JSON.stringify. Keep this in mind, that certain params like full error objects might not work. * @param rule The rule to validate logs against. * @param target The log type to which this rule should be assigned. */ static setLogRule(rule, target) { Log.logRules.set(target, rule); } /** * Set prefix for logs location. Useful if you want to group all logs from 1 project into 1 location. * @param prefix Prefix to use. */ static setPrefix(prefix) { Log.prefix = prefix; } /** * Set custom winston config. * @param config Config to set. */ static setWinstonRules(config) { Log.config = config; } /** * Disable winston. */ static disableWinston() { Log.memoryOnly = true; } static get config() { return this._config; } static set config(val) { this._config = val; } static get counter() { return Log._counter; } static set counter(val) { Log._counter = val; } static get styleJson() { return Log._styleJson; } static set styleJson(val) { Log._styleJson = val; } static get logRules() { return Log._logRules; } static get prefix() { return Log._prefix; } static set prefix(prefix) { Log._prefix = prefix; } static get memoryOnly() { return Log._memoryOnly; } static set memoryOnly(value) { Log._memoryOnly = value; } /** * Add spaces to json stringify. * Setting this to false will simply stringify logs in files without formatting them to more readable state. * This is useful, for when you have custom gui for logs like gcp. This will make logs more readable. * Default val: true. * @param val Boolean marking if json should include spaces. */ static formatJson(val) { Log.styleJson = val; } /** * Log new error. * @param target Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. */ static error(target, ...messages) { messages.forEach((m) => { Log.buildLog(() => chalk.red(`Log.ERROR: ${target}`), enums.ELogTypes.Error, target, m); }); } /** * Log new error in decorator. * @param targetMessage Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. * @returns Decorator data. */ static decorateError(targetMessage, ...messages) { return function (target, _context) { return Log.createDecorator(target, () => { messages.forEach((m) => { Log.buildLog(() => chalk.red(`Log.ERROR: ${targetMessage}`), enums.ELogTypes.Error, targetMessage, m); }); }); }; } /** * Log new warning. * @param target Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. */ static warn(target, ...messages) { messages.forEach((m) => { Log.buildLog(() => chalk.yellow(`Log.WARN: ${target}`), enums.ELogTypes.Warn, target, m); }); } /** * Log new warning in decorator. * @param targetMessage Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. * @returns Decorator data. */ static decorateWarn(targetMessage, ...messages) { return function (target, _context) { return Log.createDecorator(target, () => { messages.forEach((m) => { Log.buildLog(() => chalk.yellow(`Log.WARN: ${targetMessage}`), enums.ELogTypes.Warn, targetMessage, m); }); }); }; } /** * Log new log. * @param target Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. */ static log(target, ...messages) { messages.forEach((m) => { Log.buildLog(() => chalk.blue(`Log.LOG: ${target}`), enums.ELogTypes.Log, target, m); }); } /** * Log new log in decorator. * @param targetMessage Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. * @returns Decorator data. */ static decorateLog(targetMessage, ...messages) { return function (target, _context) { return Log.createDecorator(target, () => { messages.forEach((m) => { Log.buildLog(() => chalk.blue(`Log.LOG: ${targetMessage}`), enums.ELogTypes.Log, targetMessage, m); }); }); }; } /** * Log new debug. * This log will not show up, when NODE_ENV is set to production. * @param target Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. */ static debug(target, ...messages) { if (process.env.NODE_ENV === 'production') return; messages.forEach((m) => { Log.buildLog(() => chalk.magenta(`Log.Debug: ${target}`), enums.ELogTypes.Debug, target, m); }); } /** * Log new log in decorator. * This log will not show up, when NODE_ENV is set to production. * @param targetMessage Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. * @returns Decorator data. */ static decorateDebug(targetMessage, ...messages) { return function (target, _context) { return Log.createDecorator(target, () => { if (process.env.NODE_ENV !== 'production') { messages.forEach((m) => { Log.buildLog(() => chalk.magenta(`Log.Debug: ${targetMessage}`), enums.ELogTypes.Debug, targetMessage, m); }); } }); }; } /** * Start counting time. * To end time counting, run `log.endtime` with the same target. * @param target Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. */ static time(target, ...messages) { Log.counter.push({ target, start: Date.now() }); messages.forEach((m) => { Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${target}`), enums.ELogTypes.Log, target, m); }); } /** * End counting time. * @param target Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. */ static endTime(target, ...messages) { const localTarget = Log.counter.filter((e) => e.target === target); if (localTarget.length === 0) { Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${target}`), enums.ELogTypes.Log, target, 'Could not find time start'); } else { Log.counter = Log.counter.filter((e) => e.target !== localTarget[0].target && e.start !== localTarget[0].start); Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${target}`), enums.ELogTypes.Log, target, `Time passed: ${((Date.now() - localTarget[0].start) / 1000).toFixed(2)}s`); } messages.forEach((m) => { Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${target}`), enums.ELogTypes.Log, target, m); }); } /** * Decorator, which will counts how much time function took to run. * @param targetMessage Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. * @returns Decorator data. */ static decorateTime(targetMessage, ...messages) { return function (target, _context) { return async function (...args) { const start = Date.now(); messages.forEach((m) => { Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${targetMessage}`), enums.ELogTypes.Log, targetMessage, m); }); const result = await target.apply(this, args); Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${targetMessage}`), enums.ELogTypes.Log, targetMessage, `Time passed: ${((Date.now() - start) / 1000).toFixed(2)}s`); return result; }; }; } /** * Decorator, which will counts how much time function took to run. * This log will not show up, when NODE_ENV is set to production. * @param targetMessage Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. * @returns Decorator data. */ static decorateDebugTime(targetMessage, ...messages) { return function (target, _context) { return async function (...args) { if (process.env.NODE_ENV === 'production') return target.apply(this, args); const start = Date.now(); messages.forEach((m) => { Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${targetMessage}`), enums.ELogTypes.Log, targetMessage, m); }); const result = await target.apply(this, args); Log.buildLog(() => chalk.bgBlue(`Log.TIME: ${targetMessage}`), enums.ELogTypes.Log, targetMessage, `Time passed: ${((Date.now() - start) / 1000).toFixed(2)}s`); return result; }; }; } /** * Trace selected data and log related params. * @param target Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. */ static trace(target, ...messages) { console.trace(chalk.yellowBright(target)); messages.forEach((m) => { Log.buildLog(() => chalk.yellowBright(`Log.TRACE: ${target}`), enums.ELogTypes.Log, target, m); }); } /** * Trace selected data and log related params in decorator. * @param targetMessage Log target used as prefix for log. * @param {...unknown} messages All messages that you want to log. * @returns Decorator data. */ static decorateTrace(targetMessage, ...messages) { return function (target, _context) { return Log.createDecorator(target, () => { console.trace(chalk.yellowBright(target)); messages.forEach((m) => { Log.buildLog(() => chalk.yellowBright(`Log.TRACE: ${targetMessage}`), enums.ELogTypes.Log, targetMessage, m); }); }); }; } /** * Console.log data from log and push it to function, which saves it. * @param color Chalks function, which colours logs. * @param type Category of log. * @param header Header to user. * @param message Messages to save. */ static buildLog(color, type, header, message) { if (Log.logRules.get(type)) { const shouldLog = Log.logRules.get(type)(Utils.toString(message, Log.styleJson)); if (typeof shouldLog === 'boolean' && shouldLog === false) return; } console.info(`[${chalk.gray(Log.getDate())}] ${color()} ${Utils.toString(message, Log.styleJson)}`); const mess = Utils.toString(message, Log.styleJson); if (!this.memoryOnly) { Utils.saveLog(`${header} - ${mess}`, type, Log.prefix, { winston: Log.config }); } } static createDecorator(target, logic) { return async function (...args) { const logicOutput = logic(); if (logicOutput instanceof Promise) { await logicOutput; } return target.apply(this, args); }; } }