Skip to content

Commit

Permalink
feat: enhanced logger (#8444)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
dzianis-dashkevich and Dzianis Dashkevich authored Sep 25, 2023
1 parent 38b165d commit cf681e0
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 10 deletions.
55 changes: 45 additions & 10 deletions src/js/utils/create-logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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';

Expand Down Expand Up @@ -99,22 +109,47 @@ 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
* mylogger('foo');
* // > 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, 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.createLogger = (subname) => createLogger(name + ': ' + subname);
log.createNewLogger = (newName, newDelimiter, newStyles) => {
return createLogger(newName, newDelimiter, newStyles);
};

/**
* Enumeration of available logging levels, where the keys are the level names
Expand Down Expand Up @@ -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}
Expand Down
126 changes: 126 additions & 0 deletions test/unit/utils/log.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});

0 comments on commit cf681e0

Please sign in to comment.