nope/lib/logger/nopeLogger.ts
Martin Karkowski f0032eef63 - Fixes:
- log-to-file: is now storing the last logs as well
  - nope-package-loader: is transmitting the correct parameters.
- Optimazations:
  - nope-package-loader: now storing elements with stringifyWithFunctions and is capable to read functions.
  - pub-sub-system: Adding partial changes of the topic structure. This speeds up the entire system.
2022-03-19 07:39:34 +01:00

362 lines
8.6 KiB
TypeScript

/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2021-11-11 11:27:44
* @modify date 2021-11-11 11:27:44
* @desc [description]
*/
import * as Logger from "js-logger";
import { ILogger, ILoggerOpts } from "js-logger";
import { SPLITCHAR } from "../helpers/objectMethods";
import { RUNNINGINNODE } from "../helpers/runtimeMethods";
const colors = {
Reset: "\x1b[0m",
Bright: "\x1b[1m",
Dim: "\x1b[2m",
Underscore: "\x1b[4m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
Hidden: "\x1b[8m",
FgBlack: "\x1b[30m",
FgRed: "\x1b[31m",
FgGreen: "\x1b[32m",
FgYellow: "\x1b[33m",
FgBlue: "\x1b[34m",
FgMagenta: "\x1b[35m",
FgCyan: "\x1b[36m",
FgWhite: "\x1b[37m",
BgBlack: "\x1b[40m",
BgRed: "\x1b[41m",
BgGreen: "\x1b[42m",
BgYellow: "\x1b[43m",
BgBlue: "\x1b[44m",
BgMagenta: "\x1b[45m",
BgCyan: "\x1b[46m",
BgWhite: "\x1b[47m",
};
const colorMatching = {
ERROR: colors.FgRed,
WARN: colors.FgYellow,
INFO: colors.FgWhite,
DEBUG: colors.FgGreen,
TRACE: colors.FgCyan,
};
const spacer = {
ERROR: "",
WARN: " ",
INFO: " ",
DEBUG: "",
TRACE: "",
};
export const formatMsgForConsole = RUNNINGINNODE
? function (message, context) {
return [
colors.FgBlue,
new Date().toISOString(),
colors.Reset,
"-",
colorMatching[context.level.name],
context.level.name + spacer[context.level.name],
colors.Reset,
"-",
colors.BgWhite + colors.FgBlack,
context.name,
colors.Reset,
":",
...message,
];
}
: function (message, context) {
return [
new Date().toISOString(),
"-",
context.level.name + spacer[context.level.name],
"-",
context.name,
":",
...message,
];
};
Logger.useDefaults({
defaultLevel: (Logger as any).DEBUG,
formatter: RUNNINGINNODE
? function (messages, context) {
messages.unshift(
colors.FgBlue,
new Date().toISOString(),
colors.Reset,
"-",
colorMatching[context.level.name],
context.level.name + spacer[context.level.name],
colors.Reset,
"-",
colors.BgWhite + colors.FgBlack,
context.name,
colors.Reset,
":"
);
}
: function (messages, context) {
messages.unshift(
context.level.name + spacer[context.level.name],
"-",
context.name,
":"
);
},
});
/**
* Contains the Valid Types of the Loger.
*/
export type LoggerLevel = "error" | "warn" | "info" | "debug" | "trace";
/**
* Array containg the Valid Types of the Loger. see {@link LoggerLevel}
*/
export const LoggerLevels = ["error", "warn", "info", "debug", "trace"];
const order: { [K in LoggerLevel]: number } = {
error: 8,
warn: 5,
info: 3,
debug: 2,
trace: 1,
};
const mapping: { [K in LoggerLevel]: any } = {
error: (Logger as any).ERROR,
warn: (Logger as any).WARN,
info: (Logger as any).INFO,
debug: (Logger as any).DEBUG,
trace: (Logger as any).TRACE,
};
const reverseOrder: { [index: number]: LoggerLevel } = {};
Object.getOwnPropertyNames(order).map(
(key: LoggerLevel) => (reverseOrder[order[key]] = key)
);
const reverseMapping: any = {};
Object.getOwnPropertyNames(mapping).map(
(key: LoggerLevel) => (reverseMapping[mapping[key]] = key)
);
function _selectLevel(master: LoggerLevel, slave: LoggerLevel) {
const masterLevel = order[master];
const slaveLevel = order[slave];
return reverseOrder[Math.max(masterLevel, slaveLevel)];
}
/**
* 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 = ""): ILogger {
const logger = Logger.get(label);
logger.setLevel(mapping[level]);
return logger;
}
/**
* The Nope-Logger. It consits o
*/
export class NopeLogger {
protected _logger: ILogger;
protected _loggers: Map<string, ILogger>;
/**
* Sets the Level of the logger. Sets all logger to the defined value.
*
* @author M.Karkowski
* @memberof NopeLogger
*/
set level(value: LoggerLevel) {
if (!this.isLocked) {
this._level = value;
this.setLoglevel("core" + SPLITCHAR, value);
}
}
/**
* Returns the general logger-level.
*
* @author M.Karkowski
* @type {LoggerLevel}
* @memberof NopeLogger
*/
get level(): LoggerLevel {
return this._level;
}
/**
* Helper to readout the level
*
* @author M.Karkowski
* @type {LoggerLevel}
* @memberof NopeLogger
*/
public _level: LoggerLevel = "debug";
/**
* Creates a sublogger with the given name and the
* level.
*
* @author M.Karkowski
* @param {string} [name=""] The name of the logger
* @param {LoggerLevel} [level=this.level] The level of the logger.
* @return {ILogger} The Logger
* @memberof NopeLogger
*/
getLogger(name = "", level: LoggerLevel = this.level): ILogger {
if (!this._loggers.has(name)) {
this._loggers.set(
name,
_getLogger(_selectLevel(this.level, level), name)
);
}
return this._loggers.get(name) as ILogger;
}
/**
* Helper function, to update the Logger Defaults. Therefore you should checkout the [description](https://www.npmjs.com/package/js-logger)
*
* @param {ILoggerOpts} defaults
* @memberof NopeLogger
*/
setDefaults(defaults: ILoggerOpts): void {
Logger.useDefaults(defaults);
}
/**
* Rewrites the Stream of the Logger to the
* given handler. Watch out, only one handler
* is active.
*
* @param {(msg, context) => void} handler
* @memberof NopeLogger
*/
setHandler(handler: (msg, context) => void): void {
Logger.setHandler(handler);
}
/**
* 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): void {
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.setLevel(mapping[level]);
}
}
}
/**
* Function, to check, whether a specific logger is enabled for logging the given log level
*
* @author M.Karkowski
* @param {string} loggerName Name of the Logger
* @param {LoggerLevel} level The Log-Level to Test
* @return {boolean} Result of the Test.
* @memberof NopeLogger
*/
isLogging(loggerName: string, level: LoggerLevel): boolean {
return (
order[reverseMapping[this.getLogger(loggerName).getLevel().name]] >=
order[level]
);
}
/**
* Creates an instance of NopeLogger.
* @author M.Karkowski
* @memberof NopeLogger
*/
constructor() {
this._loggers = new Map<string, ILogger>();
this._logger = _getLogger("debug", "root");
this._loggers.set("", this._logger);
}
protected _pw = "";
/**
* Lock the Logger Level with a password. This prevents chaning the
* log level to other log-levels. you must {@link NopeLogger.unlock} to
* update the Level again.
*
* @param {string} pw The Password to Lock the Logger
* @memberof NopeLogger
*/
lock(pw: string): void {
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.
*
* @author M.Karkowski
* @param {string} pw The password to unlock the logger
* @memberof NopeLogger
*/
unlock(pw: string): void {
if (this._pw === pw) {
this._pw = "";
} else {
this._loggers.get("").error("Unlocking wasnt successfull");
}
}
/**
* Flag indicating, whether the logger is locked or not.
*
* @author M.Karkowski
* @readonly
* @type {boolean}
* @memberof NopeLogger
*/
public get isLocked(): boolean {
return this._pw !== "";
}
}