import { createLogger, format, Logger, transports } from "winston"; import { SPLITCHAR } from "../helpers/objectMethods"; /** * Contains the Valid Types of the Loger. */ export type LoggerLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly'; const order: {[K in LoggerLevel]: number} = { error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6 } const reverseOrder: {[index:number]: LoggerLevel} = {}; Object.getOwnPropertyNames(order).map((key: LoggerLevel) => reverseOrder[order[key]] = key); function _selectLevel(master: LoggerLevel, slave: LoggerLevel){ const masterLevel = order[master]; const slaveLevel = order[slave]; return reverseOrder[Math.min(masterLevel,slaveLevel)]; } /** * Helper Function to generate default Logger Options. * This results in creating a Logger with a specific label, * and rendering format. * * @export * @param {LoggerLevel} level The Level, which should be rendered * @param {string} [label=''] An Lable for the Logger. Every Message beginns with that lable. * @return {*} The Loger-Options as Object */ function _generateLoggerOptions(level: LoggerLevel, label: string = '') { let readableFormat: any; if (label) { readableFormat = format.printf(({ level, message, timestamp, label }) => { return `[${timestamp} ${level}] - ${label}: ${message}`; }); } else { readableFormat = format.printf(({ level, message, timestamp }) => { return `[${timestamp} ${level}]: ${message}`; }); } return { level, format: label ? format.combine( format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), format.label({ label }), format.errors({ stack: true }), readableFormat ) : format.combine( format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }), format.errors({ stack: true }), readableFormat ), exitOnError: false, transports: [ new (transports.Console)({ format: format.combine( format.colorize(), readableFormat ) }), ] } } /** * Helper Function, to create a Logger. * Therefore it uses a specific Level and a Lable of the * Logger * * @export * @param {LoggerLevel} level The Level, which should be rendered * @param {string} [label=''] An Lable for the Logger. Every Message beginns with that lable. * @return {*} Returns a Logger. */ function _getLogger(level: LoggerLevel, label: string = '') { return createLogger(_generateLoggerOptions(level, label)); } export class NopeLogger { protected _logger: Logger; protected _loggers: Map; set level(value: LoggerLevel) { if (!this.isLocked) { this._level = value; this.setLoglevel('core' + SPLITCHAR, value); } } get level() { return this._level; } public _level: LoggerLevel = 'debug'; /** * Create or returns the provided Logger * @param name * @param level */ getLogger(name: string = '', level: LoggerLevel = this.level) { if (!this._loggers.has(name)) { this._loggers.set( name, _getLogger(_selectLevel(this.level, level), name) ); } return this._loggers.get(name) as Logger; } /** * Function to a Stream to the Logger * @param stream */ addStream(stream) { // Store the Stream for new Streams this._logger.add(stream) } /** * Function to determine the Logger Level * @param loggerGroup The specified group, which should be addressed * @param level The Level to Set */ setLoglevel(loggerGroup: string, level: LoggerLevel) { if (!loggerGroup.startsWith('core' + SPLITCHAR)) { loggerGroup = 'core' + SPLITCHAR + loggerGroup; } if (!loggerGroup.endsWith(SPLITCHAR)) { loggerGroup += SPLITCHAR; } /** Iterate over all Loggers and close the unused Loggers */ for (let [name, logger] of this._loggers.entries()) { if (!name.startsWith('core' + SPLITCHAR)) { name = 'core' + SPLITCHAR + name; } if (!name.endsWith(SPLITCHAR)) { name += SPLITCHAR; } if (name == loggerGroup || name.startsWith(loggerGroup)) { /** Update the Level */ logger.level = level; } } } /** * Function, to check, whether a specific logger is logging or not * @param loggerName Name of the Logger * @param level The Log-Level to Test */ isLogging(loggerName: string, level: LoggerLevel) { return order[this.getLogger(loggerName).level] >= order[level]; } /** * Creates A Logger-Manager */ constructor() { this._loggers = new Map(); this._logger = _getLogger('debug', 'root'); this._loggers.set('', this._logger); } protected _pw: string = ''; /** * Lock the Logger Level with a password * * @param {string} pw The Password to Lock the Logger * @memberof NopeLogger */ lock(pw: string) { if (this._pw !== '') { this._loggers.get('').error('Please unlock before'); } else { this._pw = pw; } } /** * Unlock the Logger, therefore the password used for locking is * required. After unlocking, the log-level can be adapted. * * @param {string} pw * @memberof NopeLogger */ unlock(pw: string) { if (this._pw === pw) { this._pw = ''; } else { this._loggers.get('').error('Unlocking wasnt successfull'); } } /** * Flag indicating, whether the logger is locked or not. * * @readonly * @memberof NopeLogger */ public get isLocked() { return this._pw !== ''; } }