UNPKG

node-red-contrib-uibuilder

Version:

Easily create data-driven web UI's for Node-RED. Single- & Multi-page. Multiple UI's. Work with existing web development workflows or mix and match with no-code/low-code features.

302 lines (267 loc) 11.4 kB
/** * @module logger * @description A simple logger for the front-end module. * Enhances standard console logging with additional features. * Ensures that the original calling context is preserved so that the correct calling line number is shown. * @version 1.0.0 * @license Apache-2.0 * @author Julian Knight (Totally Information) * @copyright (c) 2025-2025 Julian Knight (Totally Information) */ // TODO: // - Add a `level` property to set the logging level. // - Add a `styles` property to set the logging styles. // - Add a `version` property to show the version. // - Swap uibuilder and ui modules to use this logger. class Logger { version = '2025-02-06' // Retain a copy of the original native console object static nativeConsole = { ...window.console } // Set the default logging level level = 'info' LOG_STYLES = { // 0 error: { level: 0, css: 'background: red; color: black;', txtCss: 'color: red; ', pre: '⛔ ', console: 'error', // or trace }, // 1 warn: { level: 1, css: 'background: darkorange; color: black;', txtCss: 'color: darkorange; ', pre: '⚠ ', console: 'warn', }, // 2 info: { level: 2, css: 'background: aqua; color: black;', txtCss: 'color: aqua;', pre: '❗ ', console: 'info', }, // 3 log: { level: 3, css: 'background: grey; color: yellow;', txtCss: 'color: grey;', pre: '', console: 'log', }, // 4 debug: { level: 4, css: 'background: chartreuse; color: black;', txtCss: 'color: chartreuse;', pre: '', console: 'debug', }, // 5 trace: { level: 5, css: 'background: indigo; color: yellow;', txtCss: 'color: hotpink;', pre: '', console: 'trace', }, '_': { names: ['error', 'warn', 'info', 'log', 'debug', 'trace'], reset: 'color: inherit;', head: 'font-weight:bold; font-style:italic;', level: 'font-weight:bold; border-radius: 3px; padding: 2px 5px; display:inline-block;', }, } constructor(parameters) { // Attach the log styles to the callable function this.callable.styles = this.LOG_STYLES const LOG_STYLES = this.callable.styles // Attach the log level to the callable function - using a getter and setter Object.defineProperty(this.callable, 'level', { get: () => this.level, set: (newLevel) => { if (LOG_STYLES._.names.includes(newLevel)) { this.level = newLevel } else { this.callable.error(`Invalid logging level: ${newLevel}`) } } }) this.callable.valueOf = this.callable.toString = () => `Logger v${this.callable.version} - Level: ${this.callable.level}` // Add the ability to this.callable to return some help text to the console - ideally it would not need to be called as a function this.callable.help = () => { const helpText = [ `\tLogger v${this.version} - Level: ${this.callable.level}`, `\tAvailable logging levels: ${LOG_STYLES._.names.join(', ')}`, '\tAdditionally, all console methods are available.', '\tTo change the logging level, use: logger.setLevel(\'level\')', ] this.callable.info('UIBUILDER Custom Logger help', helpText.join('\n')) } // Attach enhanced console methods to the callable function Object.keys(Logger.nativeConsole).forEach(method => { if (typeof Logger.nativeConsole[method] === 'function') { // By using bind, we ensure that the original calling context is preserved because the stack is preserved. if (method in this.LOG_STYLES) { // Not all console methods are stylable this.callable[method] = Logger.nativeConsole[method].bind( Logger.nativeConsole, `%c${LOG_STYLES[method].pre}${method}%c [%s]\n`, // %s is the 1st actual arg `${LOG_STYLES._.level} ${LOG_STYLES[method].css}`, // 1st %c format `${LOG_STYLES._.head} ${LOG_STYLES[method].txtCss}`, // 2nd %c format ) } else { this.callable[method] = Logger.nativeConsole[method].bind( Logger.nativeConsole ) } } }) // Return the callable function return this.callable // NOTE: That this prevents access to class properties/methods when in use. // So if other properties/methods are needed, they will need to be attached to the returned function. } /** Define a callable function that defaults to console.log * This will be the main function that is returned by the class when creating an instance. * The other console methods will be attached as additional properties to the class instance. */ callable = Logger.nativeConsole['log'].bind( Logger.nativeConsole, '%c[Custom]%c', 'color: green; font-weight: bold;', '' ) } const log2 = new Logger() module.exports = { Logger, log2 } ;(function () { const originalConsole = { ...console } // Clone the original console const logger = function (first, ...rest) { const wrappedFirstArg = `%c[PRE]%c ${first} %c[POST]%c` const styles = [ 'color: lightblue; font-weight: bold;', '', // [PRE] styles 'color: red; font-weight: bold;', '', // [POST] styles ] originalConsole.log.call(originalConsole, wrappedFirstArg, ...styles, ...rest) }.bind(originalConsole) Object.keys(originalConsole).forEach(method => { if (typeof originalConsole[method] === 'function') { logger[method] = function (first, ...rest) { const wrappedFirstArg = `%c[PRE]%c ${first} %c[POST]%c`; const styles = [ 'color: lightblue; font-weight: bold;', '', // [PRE] styles 'color: red; font-weight: bold;', '', // [POST] styles ] originalConsole[method].call(originalConsole, wrappedFirstArg, ...styles, ...rest) // originalConsole[method] = originalConsole[method](wrappedFirstArg, ...styles, ...rest) }.bind(originalConsole) } }); // @ts-ignore globalThis.logger = logger // module.exports = { logger } })() // version = '2025-02-06' // // Retain a copy of the original native console object // static nativeConsole = { ...window.console } // // Set the default logging level // level = 'info' // LOG_STYLES = { // // 0 // error: { // level: 0, // css: 'background: red; color: black;', // txtCss: 'color: red; ', // pre: '⛔ ', // console: 'error', // or trace // }, // // 1 // warn: { // level: 1, // css: 'background: darkorange; color: black;', // txtCss: 'color: darkorange; ', // pre: '⚠ ', // console: 'warn', // }, // // 2 // info: { // level: 2, // css: 'background: aqua; color: black;', // txtCss: 'color: aqua;', // pre: '❗ ', // console: 'info', // }, // // 3 // log: { // level: 3, // css: 'background: grey; color: yellow;', // txtCss: 'color: grey;', // pre: '', // console: 'log', // }, // // 4 // debug: { // level: 4, // css: 'background: chartreuse; color: black;', // txtCss: 'color: chartreuse;', // pre: '', // console: 'debug', // }, // // 5 // trace: { // level: 5, // css: 'background: indigo; color: yellow;', // txtCss: 'color: hotpink;', // pre: '', // console: 'log', // }, // names: ['error', 'warn', 'info', 'log', 'debug', 'trace'], // reset: 'color: inherit;', // head: 'font-weight:bold; font-style:italic;', // level: 'font-weight:bold; border-radius: 3px; padding: 2px 5px; display:inline-block;', // } // constructor(parameters) { // // Attach the log styles to the callable function // const LOG_STYLES = this.callable.styles = this.LOG_STYLES // // Attach the log level to the callable function // this.callable.level = this.level // this.callable.valueOf = this.callable.toString = () => `Logger v${this.version} - Level: ${this.level}` // // Attach enhanced console methods to the callable function // Object.keys(Logger.nativeConsole).forEach(method => { // if (typeof Logger.nativeConsole[method] === 'function') { // // By using bind, we ensure that the original calling context is preserved because the stack is preserved. // // this.callable[method] = Logger.nativeConsole[method].bind( // // Logger.nativeConsole, // // '%c[Custom]%c', 'color: green; font-weight: bold;', // // `%c${LOG_STYLES[method].pre}${method}%c [${head}]`, // // `${LOG_STYLES.level} ${LOG_STYLES[method].css}`, // // `${LOG_STYLES.head} ${LOG_STYLES[method].txtCss}`, // // '' // // ) // this.callable[method] = function(first, ...rest) { // Logger.nativeConsole[method].call( // Logger.nativeConsole, // '%c[Custom]%c', 'color: green; font-weight: bold;', // `%c${LOG_STYLES[method].pre}${method}%c [${first}]`, // `${LOG_STYLES.level} ${LOG_STYLES[method].css}`, // `${LOG_STYLES.head} ${LOG_STYLES[method].txtCss}`, // '', // ...rest // ).bind(Logger.nativeConsole) // } // } // }) // // Return the callable function // return this.callable // // NOTE: That this prevents access to class properties/methods when in use. // // So if other properties/methods are needed, they will need to be attached to the returned function. // } // /** Define a callable function that defaults to console.log // * This will be the main function that is returned by the class when creating an instance. // * The other console methods will be attached as additional properties to the class instance. // */ // callable = Logger.nativeConsole['log'].bind( // Logger.nativeConsole, // '%c[Custom]%c', 'color: green; font-weight: bold;', '' // )