From cf681e0e76925d1c0c844d1313e2d0ddd8f8a00a Mon Sep 17 00:00:00 2001 From: Dzianis Dashkevich <98566601+dzianis-dashkevich@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:50:01 -0400 Subject: [PATCH] feat: enhanced logger (#8444) * Added the createNewLogger method, to create a logger without a naming chain. * Added optional custom delimiter and custom styles. * Several improvements in jsDoc for proper types(d.ts) generation. --------- Co-authored-by: Dzianis Dashkevich --- src/js/utils/create-logger.js | 55 ++++++++++++--- test/unit/utils/log.test.js | 126 ++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 10 deletions(-) diff --git a/src/js/utils/create-logger.js b/src/js/utils/create-logger.js index 8954c5287..68e597074 100644 --- a/src/js/utils/create-logger.js +++ b/src/js/utils/create-logger.js @@ -11,24 +11,34 @@ let history = []; * Log messages to the console and history based on the type of message * * @private - * @param {string} type + * @param {string} name * The name of the console method to use. * - * @param {Array} args + * @param {Object} log * The arguments to be passed to the matching console method. + * + * @param {string} [styles] + * styles for name */ -const LogByTypeFactory = (name, log) => (type, level, args) => { +const LogByTypeFactory = (name, log, styles) => (type, level, args) => { const lvl = log.levels[level]; const lvlRegExp = new RegExp(`^(${lvl})$`); + let resultName = name; + if (type !== 'log') { // Add the type to the front of the message when it's not "log". args.unshift(type.toUpperCase() + ':'); } + if (styles) { + resultName = `%c${name}`; + args.unshift(styles); + } + // Add console prefix after adding to history. - args.unshift(name + ':'); + args.unshift(resultName + ':'); // Add a clone of the args at this point to history. if (history) { @@ -66,7 +76,7 @@ const LogByTypeFactory = (name, log) => (type, level, args) => { fn[Array.isArray(args) ? 'apply' : 'call'](window.console, args); }; -export default function createLogger(name) { +export default function createLogger(name, delimiter = ':', styles = '') { // This is the private tracking variable for logging level. let level = 'info'; @@ -99,10 +109,10 @@ export default function createLogger(name) { }; // This is the logByType helper that the logging methods below use - logByType = LogByTypeFactory(name, log); + logByType = LogByTypeFactory(name, log, styles); /** - * Create a new sublogger which chains the old name to the new name. + * Create a new subLogger which chains the old name to the new name. * * For example, doing `videojs.log.createLogger('player')` and then using that logger will log the following: * ```js @@ -110,11 +120,36 @@ export default function createLogger(name) { * // > VIDEOJS: player: foo * ``` * - * @param {string} name + * @param {string} subName * The name to add call the new logger + * @param {string} [subDelimiter] + * Optional delimiter + * @param {string} [subStyles] + * Optional styles * @return {Object} */ - log.createLogger = (subname) => createLogger(name + ': ' + subname); + log.createLogger = (subName, subDelimiter, subStyles) => { + const resultDelimiter = subDelimiter !== undefined ? subDelimiter : delimiter; + const resultStyles = subStyles !== undefined ? subStyles : styles; + const resultName = `${name} ${resultDelimiter} ${subName}`; + + return createLogger(resultName, resultDelimiter, resultStyles); + }; + + /** + * Create a new logger. + * + * @param {string} newName + * The name for the new logger + * @param {string} [newDelimiter] + * Optional delimiter + * @param {string} [newStyles] + * Optional styles + * @return {Object} + */ + log.createNewLogger = (newName, newDelimiter, newStyles) => { + return createLogger(newName, newDelimiter, newStyles); + }; /** * Enumeration of available logging levels, where the keys are the level names @@ -151,7 +186,7 @@ export default function createLogger(name) { * If a string matching a key from {@link module:log.levels} is provided, acts * as a setter. * - * @param {string} [lvl] + * @param {'all'|'debug'|'info'|'warn'|'error'|'off'} [lvl] * Pass a valid level to set a new logging level. * * @return {string} diff --git a/test/unit/utils/log.test.js b/test/unit/utils/log.test.js index 49103a7f6..e27abce22 100644 --- a/test/unit/utils/log.test.js +++ b/test/unit/utils/log.test.js @@ -239,3 +239,129 @@ QUnit.test('history only retains 1000 items', function(assert) { assert.equal(hist.length, 1000, 'only 1000 items in history'); assert.deepEqual([hist[0], hist[hist.length - 1 ]], [['VIDEOJS:', 6], ['VIDEOJS:', 1005]], 'keeps most recent items'); }); + +QUnit.test('create logger should create sub-logger with naming chain', function(assert) { + log.history.clear(); + + const subLogger = log.createLogger('SubModule'); + + subLogger.level('debug'); + + subLogger('log1', 'log2'); + subLogger.debug('debug1', 'debug2'); + subLogger.warn('warn1', 'warn2'); + subLogger.error('error1', 'error2'); + + assert.ok(window.console.log.called, 'console.log was called'); + assert.ok(window.console.debug.called, 'console.debug was called'); + assert.ok(window.console.warn.called, 'console.warn was called'); + assert.ok(window.console.error.called, 'console.error called'); + + const history = log.history(); + + assert.equal(history.length, 4, 'four messages in history'); + assert.deepEqual(history[0], ['VIDEOJS : SubModule:', 'log1', 'log2'], 'history is maintained'); + assert.deepEqual(history[1], ['VIDEOJS : SubModule:', 'DEBUG:', 'debug1', 'debug2'], 'history is maintained'); + assert.deepEqual(history[2], ['VIDEOJS : SubModule:', 'WARN:', 'warn1', 'warn2'], 'history is maintained'); + assert.deepEqual(history[3], ['VIDEOJS : SubModule:', 'ERROR:', 'error1', 'error2'], 'history is maintained'); +}); + +QUnit.test('create a new logger should override existing sub names', function(assert) { + log.history.clear(); + + const newLogger = log.createNewLogger('Module'); + + newLogger.level('debug'); + + newLogger('log1', 'log2'); + newLogger.debug('debug1', 'debug2'); + newLogger.warn('warn1', 'warn2'); + newLogger.error('error1', 'error2'); + + assert.ok(window.console.log.called, 'console.log was called'); + assert.ok(window.console.debug.called, 'console.debug was called'); + assert.ok(window.console.warn.called, 'console.warn was called'); + assert.ok(window.console.error.called, 'console.error called'); + + const history = log.history(); + + assert.equal(history.length, 4, 'four messages in history'); + assert.deepEqual(history[0], ['Module:', 'log1', 'log2'], 'history is maintained'); + assert.deepEqual(history[1], ['Module:', 'DEBUG:', 'debug1', 'debug2'], 'history is maintained'); + assert.deepEqual(history[2], ['Module:', 'WARN:', 'warn1', 'warn2'], 'history is maintained'); + assert.deepEqual(history[3], ['Module:', 'ERROR:', 'error1', 'error2'], 'history is maintained'); +}); + +QUnit.test('create logger applies delimiter and styles if presented', function(assert) { + log.history.clear(); + + const subLogger = log.createLogger('SubModule', '>', 'background: #333; padding: 3px; color: #bada55'); + + subLogger.level('debug'); + + subLogger('log1', 'log2'); + subLogger.debug('debug1', 'debug2'); + subLogger.warn('warn1', 'warn2'); + subLogger.error('error1', 'error2'); + + assert.ok(window.console.log.called, 'console.log was called'); + assert.ok(window.console.debug.called, 'console.debug was called'); + assert.ok(window.console.warn.called, 'console.warn was called'); + assert.ok(window.console.error.called, 'console.error called'); + + const history = log.history(); + + assert.equal(history.length, 4, 'four messages in history'); + assert.deepEqual(history[0], ['%cVIDEOJS > SubModule:', 'background: #333; padding: 3px; color: #bada55', 'log1', 'log2'], 'history is maintained'); + assert.deepEqual(history[1], ['%cVIDEOJS > SubModule:', 'background: #333; padding: 3px; color: #bada55', 'DEBUG:', 'debug1', 'debug2'], 'history is maintained'); + assert.deepEqual(history[2], ['%cVIDEOJS > SubModule:', 'background: #333; padding: 3px; color: #bada55', 'WARN:', 'warn1', 'warn2'], 'history is maintained'); + assert.deepEqual(history[3], ['%cVIDEOJS > SubModule:', 'background: #333; padding: 3px; color: #bada55', 'ERROR:', 'error1', 'error2'], 'history is maintained'); +}); + +QUnit.test('create new logger applies delimiter and styles if presented', function(assert) { + log.history.clear(); + + const newLogger = log.createNewLogger('Module', '>', 'background: #333; padding: 3px; color: #bada55'); + const subModule1 = newLogger.createLogger('SubModule1'); + const subModule2 = subModule1.createLogger('SubModule2', '->', ''); + + newLogger.level('debug'); + + newLogger('log1', 'log2'); + newLogger.debug('debug1', 'debug2'); + newLogger.warn('warn1', 'warn2'); + newLogger.error('error1', 'error2'); + + subModule1('log1', 'log2'); + subModule1.debug('debug1', 'debug2'); + subModule1.warn('warn1', 'warn2'); + subModule1.error('error1', 'error2'); + + subModule2('log1', 'log2'); + subModule2.debug('debug1', 'debug2'); + subModule2.warn('warn1', 'warn2'); + subModule2.error('error1', 'error2'); + + assert.ok(window.console.log.called, 'console.log was called'); + assert.ok(window.console.debug.called, 'console.debug was called'); + assert.ok(window.console.warn.called, 'console.warn was called'); + assert.ok(window.console.error.called, 'console.error called'); + + const history = log.history(); + + assert.equal(history.length, 12, '12 messages in history'); + assert.deepEqual(history[0], ['%cModule:', 'background: #333; padding: 3px; color: #bada55', 'log1', 'log2'], 'history is maintained'); + assert.deepEqual(history[1], ['%cModule:', 'background: #333; padding: 3px; color: #bada55', 'DEBUG:', 'debug1', 'debug2'], 'history is maintained'); + assert.deepEqual(history[2], ['%cModule:', 'background: #333; padding: 3px; color: #bada55', 'WARN:', 'warn1', 'warn2'], 'history is maintained'); + assert.deepEqual(history[3], ['%cModule:', 'background: #333; padding: 3px; color: #bada55', 'ERROR:', 'error1', 'error2'], 'history is maintained'); + + assert.deepEqual(history[4], ['%cModule > SubModule1:', 'background: #333; padding: 3px; color: #bada55', 'log1', 'log2'], 'history is maintained'); + assert.deepEqual(history[5], ['%cModule > SubModule1:', 'background: #333; padding: 3px; color: #bada55', 'DEBUG:', 'debug1', 'debug2'], 'history is maintained'); + assert.deepEqual(history[6], ['%cModule > SubModule1:', 'background: #333; padding: 3px; color: #bada55', 'WARN:', 'warn1', 'warn2'], 'history is maintained'); + assert.deepEqual(history[7], ['%cModule > SubModule1:', 'background: #333; padding: 3px; color: #bada55', 'ERROR:', 'error1', 'error2'], 'history is maintained'); + + assert.deepEqual(history[8], ['Module > SubModule1 -> SubModule2:', 'log1', 'log2'], 'history is maintained'); + assert.deepEqual(history[9], ['Module > SubModule1 -> SubModule2:', 'DEBUG:', 'debug1', 'debug2'], 'history is maintained'); + assert.deepEqual(history[10], ['Module > SubModule1 -> SubModule2:', 'WARN:', 'warn1', 'warn2'], 'history is maintained'); + assert.deepEqual(history[11], ['Module > SubModule1 -> SubModule2:', 'ERROR:', 'error1', 'error2'], 'history is maintained'); +});