@pyrologic/pyrologjs
Version:
Lightweight JavaScript logger
1,384 lines (1,370 loc) • 40.2 kB
JavaScript
/**
* logging levels
*/
var Level;
(function (Level) {
/** logs everything */
Level[Level["ALL"] = 0] = "ALL";
/** TRACE level */
Level[Level["TRACE"] = 1] = "TRACE";
/** DEBUG level */
Level[Level["DEBUG"] = 2] = "DEBUG";
/** INFO level */
Level[Level["INFO"] = 3] = "INFO";
/** WARN level */
Level[Level["WARN"] = 4] = "WARN";
/** ERROR level */
Level[Level["ERROR"] = 5] = "ERROR";
/** FATAL level */
Level[Level["FATAL"] = 6] = "FATAL";
/** loggers at this level do not log at all */
Level[Level["OFF"] = 7] = "OFF";
})(Level || (Level = {}));
/**
* retrieves the name of a logging level
* @param level logging level
* @returns the corresponding name (string)
*/
function Level2String(level) {
switch (level) {
case Level.ALL:
return "ALL";
case Level.TRACE:
return "TRACE";
case Level.DEBUG:
return "DEBUG";
case Level.INFO:
return "INFO";
case Level.WARN:
return "WARN";
case Level.ERROR:
return "ERROR";
case Level.FATAL:
return "FATAL";
case Level.OFF:
return "OFF";
}
}
/**
* converts a logging level into the matching level identifier
* @param level the logging level
* @returns the matching level identifier
*/
function Level2LevelString(level) {
return Level2String(level);
}
/**
* converts an arbitrary string into a valid level identifier
* @param s arbitrary string
* @returns the corresponding level identifier
*/
function String2LevelString(s) {
switch (s) {
case "ALL":
return "ALL";
case "TRACE":
return "TRACE";
case "DEBUG":
return "DEBUG";
case "INFO":
return "INFO";
case "WARN":
return "WARN";
case "ERROR":
return "ERROR";
case "FATAL":
return "FATAL";
case "OFF":
return "OFF";
default:
return "INFO";
}
}
/**
* converts a string providing a level name into the corresponding Level enumeration value
* @param s string providing a level name
* @returns the corresponding Level enumeration value
*/
function String2Level(s) {
const level = Level[String2LevelString(s)];
return level !== undefined ? level : Level.INFO;
}
/**
* executes a callback function for each logging level
* @param f the callback function
*/
function forEachLevel(f) {
for (let l = Level.ALL; l <= Level.OFF; ++l) {
f(l);
}
}
class CallbackAppender {
constructor(cf) {
if (typeof cf !== 'function') {
throw new Error('Invalid callback function specified!');
}
this._cf = cf;
}
/**
* @override
*/
appendLog(...logs) {
this._cf(logs);
}
}
class PyroConfigItem {
constructor(name, level, wf) {
this._name = name;
this._level = level;
this._writeFnc = wf;
this._levelStyles = new Map();
Object.freeze(this);
}
get name() {
return this._name;
}
get level() {
return this._level;
}
get writeFnc() {
return this._writeFnc;
}
get levelStyles() {
return this._levelStyles;
}
/**
* @override
*/
addLevelStyle(level, style) {
this._levelStyles.set(level, style);
}
}
/**
* Global options for all loggers
* this class is a singleton
*/
class GlobalOptions {
/**
* constructs a new instance
*/
constructor() {
this._globalStyles = new Map();
this._useDebug = false;
this._suspended = false;
}
/**
* returns the singleton instance
* @returns the singleton instance
*/
static getInstance() {
return globalOptions;
}
/**
* use console.debug()
* @returns {boolean} true if DEBUG level and below should use console.debug(); false if console.log() should be used instead
*/
get useDebug() {
return this._useDebug;
}
/**
* sets the "use console.debug()" flag
*/
set useDebug(d) {
this._useDebug = !!d;
}
/**
* @returns {boolean} true if logging is currently suspended; false otherwise
*/
get suspended() {
return this._suspended;
}
/**
* sets the "suspended" flag
*/
set suspended(s) {
this._suspended = s;
}
/**
* sets global options
* @param o an object providing one or more global options
*/
setOptions(o) {
if (typeof o === 'object') {
if (('useDebug' in o) && (typeof o.useDebug === 'boolean')) {
this._useDebug = !!o.useDebug;
}
if (('suspended' in o) && (typeof o.suspended === 'boolean')) {
this._suspended = !!o.suspended;
}
}
}
/**
*
* @param level the logging level
* @returns the global style definition for the given level or undefined if there's no matching global style definition
*/
getLevelStyle(level) {
return this._globalStyles.get(level);
}
/**
* sets the global style definition for a logging level
* @param level the logging level
* @param style the global style definition
*/
setLevelStyle(level, style) {
this._globalStyles.set(level, style);
}
}
const globalOptions = new GlobalOptions();
/**
* styling of logging messages
* see https://developer.chrome.com/docs/devtools/console/format-style
*/
/**
* all supported color values
*/
const Colors = {
BLACK: { fgRef: 30, bgRef: 40 },
RED: { fgRef: 31, bgRef: 41 },
GREEN: { fgRef: 32, bgRef: 42 },
ORANGE: { fgRef: 33, bgRef: 43 },
DARKBLUE: { fgRef: 34, bgRef: 44 },
PURPLE: { fgRef: 35, bgRef: 45 },
TURQUOISE: { fgRef: 36, bgRef: 46 },
GRAY: { fgRef: 37, bgRef: 47 },
DARKGRAY: { fgRef: 90, bgRef: 100 },
LIGHTRED: { fgRef: 91, bgRef: 101 },
LIGHTGREEN: { fgRef: 92, bgRef: 102 },
YELLOW: { fgRef: 93, bgRef: 103 },
BLUE: { fgRef: 94, bgRef: 104 },
PINK: { fgRef: 95, bgRef: 105 },
CYAN: { fgRef: 96, bgRef: 106 },
WHITE: { fgRef: 97, bgRef: 107 },
NONE: { fgRef: 0, bgRef: 0 },
};
/**
* general utility class
*/
class Utils {
/**
* indicates whether styled console output is possible
* @returns true if styled console output is possible; false otherwise
*/
static canConsoleStyles() {
if (Utils._can_console_styles === undefined) {
let webkit = false;
let nodejs = false;
try {
// check for WebKit / Chromium based browser
webkit = typeof navigator !== "undefined" && !!(navigator.userAgent) && navigator.userAgent.includes("AppleWebKit");
}
catch (e) {
webkit = false;
}
if (!webkit) {
// check for nodejs / deno
try {
nodejs = typeof process !== "undefined";
}
catch (e) {
nodejs = false;
}
}
Utils._can_console_styles = nodejs || webkit;
}
return Utils._can_console_styles;
}
/**
* checks whether the specified object is a real, non-empty string
* @param str the object supposed to be a string
* @returns true if the given object is a non-empty string; false other wise
*/
static isString(str) {
return (typeof str === 'string') && (str.length > 0);
}
/**
* ensures that a string is returned
* @param str the object supposed to be a string
* @returns the given string if it is a valid string; an empty string otherwise
*/
static ensureString(str) {
return Utils.isString(str) ? str : '';
}
/**
* checks whether the given name parameter is a valid string; throws an error if not
* @param name a string specifying a name
* @returns the given string
*/
static ensureName(name) {
if (!Utils.isString(name)) {
throw new Error(`Invalid name specified: "${name}" (type: ${typeof name})!`);
}
return name;
}
/**
* checks whether all elements of the given array are valid strings; throws an error if not
* @param names array of strings to be
* @param errmsg optional error message that's thrown in the case of an error
*/
static checkNames(names, errmsg = "") {
const cnt = names.length;
for (let i = 0; i < cnt; ++i) {
const n = names[i];
if (!Utils.isString(n)) {
const msg = Utils.isString(errmsg) ? errmsg : `Invalid element at index #${i}: "${n}"!`;
throw new Error(msg);
}
}
return names;
}
/**
* splits a path string into parts
* @param path the path string
* @returns {string[]} the configuration path
*/
static splitPath(path) {
const p = [];
path.split('.').forEach((s) => p.push(s.trim()));
return p;
}
/**
* splits a logger name into a configuration path
* @param {string} name logger name
* @returns {string[]} the configuration path
*/
static getPath(name) {
return Utils.checkNames(Utils.splitPath(name), `Invalid logger path "${name}"!`);
}
/**
* normalizes a logger name or path
* @param name the logger name or path
* @returns the normalized logger path
*/
static normalizePath(name) {
const path = Utils.getPath(name);
return path.join('.');
}
/**
* retrieves the stack trace
* @param skip number of stack entries to skip
* @returns the stack trace as string
*/
static getStack(skip = 1) {
const err = new Error();
const stack = Utils.ensureString(err.stack);
if (Utils.isString(stack)) {
let s = stack;
let c = skip;
if (!stack.startsWith('Error')) {
// Chromium has an additional line beginning with "Error\n" - Firefox and Safari do not!
--c;
}
while (c >= 0) {
const index = s.indexOf('\n');
if ((index < 0) || ((s.length - 1) < (index + 1))) {
break;
}
s = s.substring(index + 1);
--c;
}
return s;
}
else {
return '';
}
}
/**
* retrieves the name of the calling function
* @param skip number of stack entries to skip; 0 used default
* @param separator optional separator character that replaces '.'
* @returns the name of the calling function
*/
static getFunctionName(skip = 0, separator = '#') {
const eff_skip = (typeof skip === 'number') ? skip + 2 : 2;
const stack = Utils.getStack(eff_skip);
if (Utils.isString(stack)) {
let s = stack.trim();
if (s.startsWith('at ')) {
s = s.substring(3);
}
const si = s.indexOf(' (');
if ((si !== -1) && ((si + 1) < (s.length - 1))) {
s = s.substring(0, si);
}
const ai = s.indexOf('@');
if ((ai >= 0) && ((s.length - 1) >= (ai + 1))) {
s = s.substring(0, ai);
}
const pi = s.indexOf('/<'); // that's sometimes the case in Firefox
if (pi !== -1) {
s = s.substring(0, pi);
}
let hi = s.indexOf('https://');
if (hi === -1) {
hi = s.indexOf('http://');
}
if (hi !== -1) {
s = (hi > 0) ? s.substring(0, hi) : '<anonymous>';
}
if ((s.length > 0) && Utils.isString(separator)) {
s = s.replace('.', separator);
}
return s;
}
else {
return '';
}
}
}
Utils._can_console_styles = undefined;
class PyroLogger {
/**
* constructs a new instance
* @param n logger name
* @param l initial logging level
* @param wf flag whether to write the name of the calling function / method
* @param a the optional appender
*/
constructor(n, l, wf, pg, sp, a) {
this._styleProvider = sp;
this._options = GlobalOptions.getInstance();
this._styles = new Map();
this._name = n;
this._level = l;
this._writeFnc = !!wf;
this._fncOffset = 0;
this._suspended = false;
this._pfxGenerator = pg;
this._appender = a;
this.updateStyles();
}
/**
* @returns the name of this logger
*/
get name() {
return this._name;
}
/**
* @returns {Level} the current logging level of this logger
*/
get level() {
return this._level;
}
;
/**
* @returns the flag whether to write the name of the calling function / method along with each logging output
*/
get writeFnc() {
return this._writeFnc;
}
/**
* @returns the offset for the call stack used to get the name of the calling function
*/
get fncOffset() {
return this._fncOffset;
}
/**
* @returns the "suspended" state of this logger
*/
get suspended() {
return this._suspended;
}
/**
* sets a new logging level of this logger
* @param {Level} l new logging level of this logger
*/
setLevel(l) {
this._level = l;
}
/**
* sets a new appender
* @param a the new appender; may be null
*/
setAppender(a) {
this._appender = a;
}
/**
* sets a new prefix generator
* @param pg the new prefix generator; must not be null
*/
setPrefixGenerator(pg) {
this._pfxGenerator = pg;
}
/**
* sets the flag whether to write the name of the calling function / method along with each logging output
* @param wf new value
*/
setWriteFnc(wf) {
this._writeFnc = !!wf;
}
/**
* updates the style definitions for this logger
*/
updateStyles() {
this._styles.clear();
const styleProvider = this._styleProvider;
const self = this;
forEachLevel((level) => {
const sd = styleProvider.getStyleDef(self.name, level);
if (sd !== undefined) {
self._styles.set(level, sd);
}
});
}
/**
* @returns {String} a short informational string about this logger
*/
toString() {
return `{Logger "${this._name}" [${Level2String(this._level)}]}`;
}
/**
* @override
*/
isEnabledFor(l) {
return !this._options.suspended && (l >= this.level) && (l !== Level.OFF);
}
/**
* @override
*/
isDebugEnabled() {
return this.isEnabledFor(Level.DEBUG);
}
/**
* @override
*/
isTraceEnabled() {
return this.isEnabledFor(Level.TRACE);
}
/**
* creates the prefix text that's prepended to each logging output
* @param l logging level
* @returns the prefix text
*/
_getPrefix(l) {
try {
this._fncOffset += 4;
return this._pfxGenerator.createPrefix(this, l);
}
finally {
this._fncOffset -= 4;
}
}
/**
* @returns true if this logger is currently suspended; false otherwise
*/
get _isSuspended() {
return this.suspended || this._options.suspended;
}
/**
* adds a style value to the given style descriptor
* @param dsc the current style descriptor
* @param value the style value
* @returns the new style descriptor
*/
_addStyleDsc(dsc, value) {
return `${dsc}${dsc.length ? ';' : ''}${value}`;
}
/**
* creates a style descriptor from a style definition
* @param style the style definition
* @returns the style descriptor
*/
_createStyleDescriptor(style) {
let dsc = '';
if (Utils.canConsoleStyles()) {
// the environment supports this
if (style.color && style.color !== Colors.NONE) {
dsc = this._addStyleDsc(dsc, style.color.fgRef);
}
if (style.background && style.background !== Colors.NONE) {
dsc = this._addStyleDsc(dsc, style.background.bgRef);
}
if (style.styles.bold) {
dsc = this._addStyleDsc(dsc, 1);
}
if (style.styles.italic) {
dsc = this._addStyleDsc(dsc, 3);
}
if (style.styles.underline) {
dsc = this._addStyleDsc(dsc, 4);
}
if (style.styles.linethrough) {
dsc = this._addStyleDsc(dsc, 9);
}
}
return dsc;
}
/**
* applies the style definition to the data to be logged
* @param style the style definition
* @param prefix the prefix text
* @param data data to be logged
* @returns the styled data to be logged
*/
_applyStyle(style, prefix, data) {
if (style !== undefined) {
const dsc = this._createStyleDescriptor(style);
if (Utils.isString(dsc)) {
// we've got a valid, non-empty style descriptor
const styled_data = [];
let text = `\x1B[${dsc}m`;
// add prefix text if present
if (Utils.isString(prefix)) {
text += `${prefix} `;
}
// add placeholders for each value according to its type
for (let i = 0; i < data.length; ++i) {
if (i > 0) {
text += ' ';
}
const value = data[i];
if (Utils.isString(value)) {
text += '%s';
}
else if (typeof value === 'number') {
text += Number.isInteger(value) ? '%d' : '%f';
}
else if (value instanceof HTMLElement) {
text += '%o';
}
else {
text += '%O';
}
}
// reset formatting
text += '\x1B[0m';
// first item is the prefix text with styling and all placeholders
styled_data.push(text);
// all other items are the actual data to be logged
styled_data.push(...data);
// done
return styled_data;
}
}
// use data "as is" but consider the prefix
if (Utils.isString(prefix)) {
const all_data = [];
all_data.push(prefix, ...data);
return all_data;
}
else {
// no prefix, nothing to do
return data;
}
}
/**
* @override
*/
writeLog(l, ...data) {
if (!this._isSuspended && (this._level !== Level.OFF) && (l !== Level.OFF) && this.isEnabledFor(l)) {
const prefix = this._getPrefix(l);
const style = this._styles.get(l);
const styled_data = this._applyStyle(style, prefix, data);
switch (l) {
case Level.ALL:
case Level.TRACE:
case Level.DEBUG:
if (this._options.useDebug) {
console.debug(...styled_data);
}
else {
console.log(...styled_data);
}
break;
case Level.INFO:
console.info(...styled_data);
break;
case Level.WARN:
console.warn(...styled_data);
break;
case Level.ERROR:
case Level.FATAL:
console.error(...styled_data);
break;
default:
console.log(...styled_data);
break;
}
if (this._appender !== null) {
this._appender.appendLog(prefix, ...data);
}
}
}
/**
* @override
*/
trace(...data) {
this.writeLog(Level.TRACE, ...data);
}
/**
* @override
*/
debug(...data) {
this.writeLog(Level.DEBUG, ...data);
}
/**
* @override
*/
info(...data) {
this.writeLog(Level.INFO, ...data);
}
/**
* @override
*/
warn(...data) {
this.writeLog(Level.WARN, ...data);
}
/**
* @override
*/
error(...data) {
this.writeLog(Level.ERROR, ...data);
}
/**
* @override
*/
fatal(...data) {
this.writeLog(Level.FATAL, ...data);
}
/**
* @override
*/
writeStackTrace(l, skip, message) {
if (this.isEnabledFor(l)) {
this.writeLog(l, (Utils.isString(message) ? message : '') + '\n' + Utils.getStack(skip));
}
}
/**
* @override
*/
setFncOffset(offs) {
this._fncOffset = offs;
}
/**
* @override
*/
setSuspended(suspended) {
this._suspended = suspended;
}
/**
* @override
*/
addStyle(level, style) {
this._styles.set(level, style);
}
}
/**
* some common constants
*/
/**
* the name of the default configuration
*/
const DEFAULT_CONFIG = '@default';
/**
* the name of configuration's root node
*/
const ROOT_NODE_NAME = '@rootNode';
/**
* configuration tree
*/
/**
* an internal tree node class
*/
class Node {
constructor(n, p, nc) {
this._name = Utils.ensureName(n);
if ((p !== null) && (n === ROOT_NODE_NAME)) {
throw new Error('Cannot create child node with internal root node name!');
}
this._parent = p;
this._config = nc;
this._children = new Map();
if (p !== null) {
p._addChild(this);
}
}
get name() {
return this._name;
}
get parent() {
return this._parent;
}
get config() {
return this._config;
}
hasConfig() {
return this._config !== null;
}
setConfig(cf) {
this._config = cf;
}
hasChild(name) {
return this._children.has(name);
}
getChild(name) {
return this._children.get(name);
}
clear() {
if (this._children.size > 0) {
try {
this._children.forEach((c) => c.clear());
}
finally {
this._children.clear();
}
}
}
_addChild(child) {
if (child._parent !== this) {
throw new Error('Invalid child node with different parent node!');
}
if (!this.hasChild(child.name)) {
this._children.set(Utils.ensureName(child.name), child);
}
else {
throw new Error(`Duplicate node "${child.name}"!`);
}
}
}
/**
* the class ConfigTree holds the hierarchical logger configuration
*/
class ConfigTree {
/**
* constructs a new instance
* @param {ConfigItem} rc root configuration item; provides the default configuration
*/
constructor(rc) {
this._rootNode = new Node(ROOT_NODE_NAME, null, rc);
}
/**
* @returns {ConfigItem} the default configurations
*/
get defaultConfig() {
return this._rootNode.config;
}
/**
* clears the configuration tree, drops all configuration nodes
*/
clear() {
this._rootNode.clear();
}
/**
* searches for a matching logger configuration in the configuration tree
* @param {string} name logger name
* @returns {ConfigItem} the matching logger configuration or null if something went wrong
*/
findConfig(name) {
let node = this._findNode(this._rootNode, Utils.getPath(name), 1);
while ((node !== null) && (node.config === null)) {
node = node.parent;
}
return (node !== null) ? node.config : this._rootNode.config;
}
/**
* searches for a node in the configuration tree
* @param {Node} node parent node
* @param {string[]} path configuration node path
* @param {number} level recursion level
* @returns {Node} the matching node
*/
_findNode(node, path, level) {
if (level > path.length) {
return node;
}
const child = node.getChild(path[level - 1]);
if (child !== undefined) {
return this._findNode(child, path, level + 1);
}
else {
return node;
}
}
/**
* applies a logger configuration
* @param config array of configuration items
*/
_applyConfiguration(config) {
for (let ci of config) {
try {
const names = Utils.getPath(ci.name);
const node = this._ensureNode(this._rootNode, names, 1);
node.setConfig(ci);
console.debug(`Configuration node "${names.join('.')}" is set to level "${ci.level}."`);
}
catch (error) {
console.error('Skipping invalid configuration item.', error);
}
}
}
/**
* ensures that a tree node exists for a given path
* @param {Node} node parent node
* @param {string[]} path configuration node path
* @param {Number} level current recursion level
* @returns {Node} the child node referred by the path
*/
_ensureNode(node, path, level) {
const name = path[level - 1];
if (!node.hasChild(name)) {
new Node(name, node, null);
}
const child = node.getChild(name);
return level < path.length ? this._ensureNode(child, path, level + 1) : child;
}
/**
* applies a logger configuration
* @param config array of configuration items
*/
static applyConfiguration(config) {
let nci = [];
let dci = null;
for (let ci of config) {
const name = Utils.normalizePath(ci.name);
const level = Level[ci.level];
if (typeof level === 'undefined') {
throw new Error(`Invalid level "${ci.level}"!`);
}
if (DEFAULT_CONFIG === name) {
if (dci === null) {
dci = ci;
}
}
else {
nci.push(ci);
}
}
if (dci === null) {
// create default configuration
dci = new PyroConfigItem(DEFAULT_CONFIG, 'ERROR', false);
}
const tree = new ConfigTree(dci);
if (nci.length > 0) {
tree._applyConfiguration(nci);
}
return tree;
}
}
class PyroPrefixGenerator {
/**
* constructs a new instance
*/
constructor() {
// empty so far
}
/**
* @returns the singleton instance
*/
static getInstance() {
return pyroPrefixGenerator;
}
/**
* @override
*/
createPrefix(logger, level) {
return `${(new Date()).toISOString()} ${logger.name} [${Level2String(level)}]` + (logger.writeFnc ? ` (${Utils.getFunctionName(logger.fncOffset)})` : '') + ':';
}
}
const pyroPrefixGenerator = new PyroPrefixGenerator();
/**
* a callback prefix generator that wraps a callback function
*/
class CallbackPrefixGenerator {
constructor(fn) {
this._pgf = fn;
}
/**
* @override
*/
createPrefix(logger, level) {
return this._pgf(logger, level);
}
}
class LoggerFactory {
/**
* constructs the instance
*/
constructor() {
this._loggers = new Map();
this._defLevel = Level.INFO;
this._writeFnc = null;
this._config = null;
this._appender = null;
this._pfxGenerator = null;
}
/**
* returns the logger factory instance
* @returns {LoggerFactory} the logger factory instance
*/
static getInstance() {
return loggerFactory;
}
/**
* the name of the default configuration item
*/
get defaultName() {
return DEFAULT_CONFIG;
}
/**
* the default level for new loggers
*/
get defaultLevel() {
return this._defLevel;
}
/**
* sets the default level
*/
set defaultLevel(l) {
this._defLevel = l;
}
/**
* flag whether to write the name of the calling function / method along with each output
* @returns {Boolean | null} true if new loggers should write the function name along with each log output; false if not; null if not specified
*/
get writeFnc() {
return this._writeFnc;
}
/**
* sets the "write function name" flag
*/
set writeFnc(wf) {
this._writeFnc = wf;
}
/**
* returns a logger
* @param name logger name
* @returns {Logger} the logger
*/
getLogger(name) {
const path = Utils.normalizePath(name);
if (!this._loggers.has(path)) {
// time to create it
this._loggers.set(path, new PyroLogger(path, this.getLevel(path), this._getEffWriteFnc(this.getWriteFnc(path)), this.prefixGenerator, this, this._appender));
}
return this._loggers.get(path);
}
/**
* retrieves a logger configuration item
* @param name logger name
* @returns the matching configuration item or null if no matching configuration item was found
*/
_getConfig(name) {
var _a;
if (this._config !== null) {
return (_a = this._config) === null || _a === void 0 ? void 0 : _a.findConfig(name);
}
else {
return null;
}
}
/**
* retrieves the parent configuration item of a configuration item
* @param config configuration item
* @returns the parent configuration item of the given item or null if there's no parent item
*/
_getParentConfig(config) {
const name = config.name;
const pos = name.lastIndexOf('.');
if (pos > 0) {
return this._getConfig(name.substring(0, pos));
}
return null;
}
/**
* retrieves the effective "write function name" setting
* @param wf logger's "write function name" flag
* @returns the effective "write function name" setting
*/
_getEffWriteFnc(wf) {
return (wf !== null) ? wf.valueOf() : ((this._writeFnc !== null) ? this._writeFnc.valueOf() : false);
}
/**
* retrieves the configured level of a logger
* @param name logger name
* @returns the level for that logger; if no configuration exists for the specified logger then the default level is returned
*/
getLevel(name) {
const config = this._getConfig(name);
return config !== null ? Level[config.level] : this._defLevel;
}
/**
* retrieves the "write function name" flag for the specified logger
* @param name logger name
* @returns true if the specified logger should write the function name along with each log output; false if not; null if not specified
*/
getWriteFnc(name) {
const config = this._getConfig(name);
return config !== null ? config.writeFnc : this.writeFnc;
}
/**
* @override
*/
getStyleDef(name, level) {
const config = this._getConfig(name);
let style = undefined;
if (config !== null) {
const ls = Level2LevelString(level);
style = config.levelStyles.get(ls);
let parent = this._getParentConfig(config);
while ((style === undefined) && (parent !== null)) {
style = parent.levelStyles.get(ls);
parent = this._getParentConfig(parent);
}
}
return style !== undefined ? style : GlobalOptions.getInstance().getLevelStyle(level);
}
/**
* creates a new callback appender
* @param cf callback function
* @param set flag whether to set this appender immediately
* @returns the created appender
*/
createAppender(cf, set) {
const appender = new CallbackAppender(cf);
if (set) {
this.setAppender(appender);
}
return appender;
}
/**
* sets a new appender
* @param appender the new appender, may be null
*/
setAppender(appender) {
this._appender = appender;
this._loggers.forEach((l) => l.setAppender(appender));
}
/**
* sets a new prefix generator
* @param generator new prefix generator
*/
setPrefixGenerator(generator) {
this._pfxGenerator = generator;
const pg = this.prefixGenerator;
this._loggers.forEach((l) => l.setPrefixGenerator(pg));
}
/**
* creates a prefix generator
* @param fn actual prefix generator function
* @param set flag whether to set this prefix generator immediately as current prefix generator
* @returns the created prefix generator instance
*/
createPrefixGenerator(fn, set) {
const pg = new CallbackPrefixGenerator(fn);
if (set) {
this.setPrefixGenerator(pg);
}
return pg;
}
/**
* @returns the default prefix generator
*/
get prefixGenerator() {
return this._pfxGenerator !== null ? this._pfxGenerator : PyroPrefixGenerator.getInstance();
}
/**
* creates a level style descriptor
* @param param0
* @returns the level style descriptor
*/
createLevelStyle({ color = Colors.NONE, background = Colors.NONE, bold = false, italic = false, underline = false, linethrough = false }) {
const textStyle = {
bold: !!bold,
italic: !!italic,
underline: !!underline,
linethrough: !!linethrough,
};
const styleDef = {
color: color ? color : Colors.NONE,
background: background ? background : Colors.NONE,
styles: textStyle,
};
return styleDef;
}
/**
* creates a configuration item
* @param name logger name
* @param level logging level
* @param wf flag whether to write the name of the calling function / method
* @returns the created configuration item
*/
createConfigItem(name, level, wf) {
return new PyroConfigItem(Utils.normalizePath(name), level, wf);
}
/**
* applies a logger configuration
* @param config array of configuration items
*/
applyConfiguration(config) {
if (this._config !== null) {
// drop old configuration
this._config.clear();
this._config = null;
}
// read and evaluate configuration items
const cfg = ConfigTree.applyConfiguration(config);
this._config = cfg;
this._defLevel = Level[cfg.defaultConfig.level];
this._writeFnc = cfg.defaultConfig.writeFnc;
if (this._loggers.size > 0) {
// update existing loggers
const self = this;
this._loggers.forEach((pl) => {
pl.setLevel(self.getLevel(pl.name));
pl.setWriteFnc(self._getEffWriteFnc(self.getWriteFnc(pl.name)));
pl.updateStyles();
});
}
}
}
const loggerFactory = new LoggerFactory();
class PyroLog {
constructor() {
this._lf = LoggerFactory.getInstance();
Object.freeze(this);
}
static _create() {
return new PyroLog();
}
/**
* returns the singleton instance
* @returns the singleton instance
*/
static getInstance() {
return pyroLog;
}
/**
* the logger factory instance
*/
get Factory() {
return this._lf;
}
/**
* the name of the default configuration item
*/
get defaultName() {
return this._lf.defaultName;
}
/**
* the default level for new loggers
*/
get defaultLevel() {
return this._lf.defaultLevel;
}
/**
* the stack trace as string
*/
get stackTrace() {
return Utils.getStack(2);
}
/**
* the name of the calling function as string
*/
get functionName() {
return Utils.getFunctionName(1, '#');
}
/**
* returns a logger
* @param name logger name
* @returns {Logger} the logger
*/
getLogger(name) {
return this._lf.getLogger(name);
}
/**
* sets global options
* @param o an object providing one or more global options
*/
setGlobalOptions(o) {
GlobalOptions.getInstance().setOptions(o);
}
/**
* creates a level style descriptor
* @param param0
* @returns the level style descriptor
*/
createLevelStyle({ color = Colors.NONE, background = Colors.NONE, bold = false, italic = false, underline = false, linethrough = false }) {
return this._lf.createLevelStyle({ color, background, bold, italic, underline, linethrough });
}
/**
* creates a new callback appender
* @param cf callback function
* @param set flag whether to set this appender immediately
* @returns the created appender
*/
createAppender(cf, set) {
return this._lf.createAppender(cf, set);
}
/**
* sets a new appender
* @param appender the new appender, may be null
*/
setAppender(appender) {
this._lf.setAppender(appender);
}
/**
* sets a new prefix generator
* @param generator new prefix generator
*/
setPrefixGenerator(generator) {
this._lf.setPrefixGenerator(generator);
}
/**
* creates a prefix generator
* @param fn actual prefix generator function
* @param set flag whether to set this prefix generator immediately as current prefix generator
* @returns the created prefix generator instance
*/
createPrefixGenerator(fn, set) {
return this._lf.createPrefixGenerator(fn, set);
}
/**
* creates a logger configuration item
* @param name logger name
* @param level logging level
* @param wf flag whether to write the name of the calling function / method
* @returns the created configuration item
*/
createConfigItem(name, level, wf = null) {
return this._lf.createConfigItem(name, level, wf);
}
/**
* applies a logger configuration
* @param config array of configuration items
*/
applyConfiguration(config) {
this._lf.applyConfiguration(config);
}
/**
* sets the global style definition for a logging level
* @param level the logging level
* @param style the global style definition
*/
setLevelStyle(level, style) {
GlobalOptions.getInstance().setLevelStyle(level, style);
}
/**
* writes the current stack trace to the specified logger
* @param logger target logger
* @param level logging level
* @param message optional message text
*/
writeStackTrace(logger, level, message) {
logger.writeStackTrace(Level[level], 3, message);
}
}
// create the singleton instance
const pyroLog = PyroLog._create();
// create Level enumeration as JS object
const JsLevel = Object.freeze({
ALL: Level.ALL,
TRACE: Level.TRACE,
DEBUG: Level.DEBUG,
INFO: Level.INFO,
WARN: Level.WARN,
ERROR: Level.ERROR,
FATAL: Level.FATAL,
OFF: Level.OFF
});
export { Colors, JsLevel, Level, Level2LevelString, Level2String, PyroLog, Utils as PyroLogUtils, PyroLogger, String2Level, String2LevelString, forEachLevel };
//# sourceMappingURL=pyrolog.js.map