diff options
Diffstat (limited to 'javascript/videojs/src/js/utils/create-logger.js')
| -rw-r--r-- | javascript/videojs/src/js/utils/create-logger.js | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/javascript/videojs/src/js/utils/create-logger.js b/javascript/videojs/src/js/utils/create-logger.js new file mode 100644 index 0000000..08a8c74 --- /dev/null +++ b/javascript/videojs/src/js/utils/create-logger.js @@ -0,0 +1,287 @@ +/** + * @file create-logger.js + * @module create-logger + */ +import window from 'global/window'; + +// This is the private tracking variable for the logging history. +let history = []; + +/** + * Log messages to the console and history based on the type of message + * + * @private + * @param {string} name + * The name of the console method to use. + * + * @param {Object} log + * The arguments to be passed to the matching console method. + * + * @param {string} [styles] + * styles for name + */ +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(resultName + ':'); + + // Add a clone of the args at this point to history. + if (history) { + history.push([].concat(args)); + + // only store 1000 history entries + const splice = history.length - 1000; + + history.splice(0, splice > 0 ? splice : 0); + } + + // If there's no console then don't try to output messages, but they will + // still be stored in history. + if (!window.console) { + return; + } + + // Was setting these once outside of this function, but containing them + // in the function makes it easier to test cases where console doesn't exist + // when the module is executed. + let fn = window.console[type]; + + if (!fn && type === 'debug') { + // Certain browsers don't have support for console.debug. For those, we + // should default to the closest comparable log. + fn = window.console.info || window.console.log; + } + + // Bail out if there's no console or if this type is not allowed by the + // current logging level. + if (!fn || !lvl || !lvlRegExp.test(type)) { + return; + } + + fn[Array.isArray(args) ? 'apply' : 'call'](window.console, args); +}; + +export default function createLogger(name, delimiter = ':', styles = '') { + // This is the private tracking variable for logging level. + let level = 'info'; + + // the curried logByType bound to the specific log and history + let logByType; + + /** + * Logs plain debug messages. Similar to `console.log`. + * + * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149) + * of our JSDoc template, we cannot properly document this as both a function + * and a namespace, so its function signature is documented here. + * + * #### Arguments + * ##### *args + * *[] + * + * Any combination of values that could be passed to `console.log()`. + * + * #### Return Value + * + * `undefined` + * + * @namespace + * @param {...*} args + * One or more messages or objects that should be logged. + */ + function log(...args) { + logByType('log', level, args); + } + + // This is the logByType helper that the logging methods below use + logByType = LogByTypeFactory(name, log, styles); + + /** + * Create a new subLogger which chains the old name to the new name. + * + * For example, doing `mylogger = videojs.log.createLogger('player')` and then using that logger will log the following: + * ```js + * mylogger('foo'); + * // > VIDEOJS: player: foo + * ``` + * + * @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.createNewLogger = (newName, newDelimiter, newStyles) => { + return createLogger(newName, newDelimiter, newStyles); + }; + + /** + * Enumeration of available logging levels, where the keys are the level names + * and the values are `|`-separated strings containing logging methods allowed + * in that logging level. These strings are used to create a regular expression + * matching the function name being called. + * + * Levels provided by Video.js are: + * + * - `off`: Matches no calls. Any value that can be cast to `false` will have + * this effect. The most restrictive. + * - `all`: Matches only Video.js-provided functions (`debug`, `log`, + * `log.warn`, and `log.error`). + * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls. + * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls. + * - `warn`: Matches `log.warn` and `log.error` calls. + * - `error`: Matches only `log.error` calls. + * + * @type {Object} + */ + log.levels = { + all: 'debug|log|warn|error', + off: '', + debug: 'debug|log|warn|error', + info: 'log|warn|error', + warn: 'warn|error', + error: 'error', + DEFAULT: level + }; + + /** + * Get or set the current logging level. + * + * If a string matching a key from {@link module:log.levels} is provided, acts + * as a setter. + * + * @param {'all'|'debug'|'info'|'warn'|'error'|'off'} [lvl] + * Pass a valid level to set a new logging level. + * + * @return {string} + * The current logging level. + */ + log.level = (lvl) => { + if (typeof lvl === 'string') { + if (!log.levels.hasOwnProperty(lvl)) { + throw new Error(`"${lvl}" in not a valid log level`); + } + level = lvl; + } + return level; + }; + + /** + * Returns an array containing everything that has been logged to the history. + * + * This array is a shallow clone of the internal history record. However, its + * contents are _not_ cloned; so, mutating objects inside this array will + * mutate them in history. + * + * @return {Array} + */ + log.history = () => history ? [].concat(history) : []; + + /** + * Allows you to filter the history by the given logger name + * + * @param {string} fname + * The name to filter by + * + * @return {Array} + * The filtered list to return + */ + log.history.filter = (fname) => { + return (history || []).filter((historyItem) => { + // if the first item in each historyItem includes `fname`, then it's a match + return new RegExp(`.*${fname}.*`).test(historyItem[0]); + }); + }; + + /** + * Clears the internal history tracking, but does not prevent further history + * tracking. + */ + log.history.clear = () => { + if (history) { + history.length = 0; + } + }; + + /** + * Disable history tracking if it is currently enabled. + */ + log.history.disable = () => { + if (history !== null) { + history.length = 0; + history = null; + } + }; + + /** + * Enable history tracking if it is currently disabled. + */ + log.history.enable = () => { + if (history === null) { + history = []; + } + }; + + /** + * Logs error messages. Similar to `console.error`. + * + * @param {...*} args + * One or more messages or objects that should be logged as an error + */ + log.error = (...args) => logByType('error', level, args); + + /** + * Logs warning messages. Similar to `console.warn`. + * + * @param {...*} args + * One or more messages or objects that should be logged as a warning. + */ + log.warn = (...args) => logByType('warn', level, args); + + /** + * Logs debug messages. Similar to `console.debug`, but may also act as a comparable + * log if `console.debug` is not available + * + * @param {...*} args + * One or more messages or objects that should be logged as debug. + */ + log.debug = (...args) => logByType('debug', level, args); + + return log; +} |
