Home Reference Source

src/logger.js

/**
 * A class decorator that adds a logger object to class instances.
 * The logger object prepends a specified name to all log output.
 *
 * Unless you specify a custom `accessor`, the logger object is available via `this.logger` and you can use it as in `this.logger.warn('oh noes!')`.
 * A special case is `accessor: 'this'`- it leads to the log methods being available onthe decorated instance directly and you can use it as in `this.warn('oh noes!')`.
 *
 * @param {string|function} [name='logger'] - The name prefix. Prepended to all output of the created logger. If it is a function, the function should return a string.
 * @param {object} [config] - An optional configuration object.
 * @param {object|array} [config.backend = window.console] - The logger object that will be used. Can also be an array of logger objects.
 * @param {array} [config.functions = ['log', 'info', 'warn', 'error', 'trace', 'table', 'debug']] - An array of function names supported by the `logger` object..
 * @param {string} [config.accessor='logger'] - The name by which the logger object can be accessed on the decorated instance.
 * @param {function} [config.prefixer = (name) => `[${name}]`] - A function that creates the prefix. Receives the `name` string and should return a string.
 * @param {string|number} [config.level = debug] - A log level name. Should be either a number or one of the available `functions` (the array index is the numeric level), decides whether the message will be printed. E.g. the lowest level is `log` - only `logger.log()` will be printed, while the highest level is `debug` (last array element)
 * @return {bool} - Always returns `true`. (Allows usage as in `(args) => this.logger.log(args) && doSomething(args)`)
 *
 * @example
 * import React from 'react';
 * import logger from '@xailabs/logger';
 * @logger('App')
 * class App extends React.Component {
 *      componentDidMount() {
 *          this.logger.warn('watch out!'); // logs '[App] watch out!'
 *      }
 * }
 */
export default function logger(name = 'logger', {
    backend = console,
    functions = ['log', 'info', 'warn', 'error', 'trace', 'table', 'debug', 'dir'],
    accessor = 'logger',
    prefixer = (name) => `[${name}]`,
    level = 'debug'
} = {}) {
    return function decorateClass(target) {
        if (!target) {
            target = Object.create(null);
            accessor = 'this';
        }
        const customLogger = functions.reduce((result, fn) => {
            result[fn] = (function(...args) {
                const fnLevel = functions.indexOf(fn);
                const allowedLevel = typeof level === 'number' ? level : functions.indexOf(level);
                if (!allowedLevel || fnLevel > allowedLevel) {
                    return;
                }
                const prefix = prefixer(typeof name === 'function' ? name({target, args}) : name);
                const logArgs = [prefix, ...args];
                const backends = Array.isArray(backend) ? backend : [backend];
                backends.forEach(backend => backend[fn].apply(backend, logArgs));
                return true;
            }).bind(result);
            return result;
        }, {});
        if (accessor === 'this') {
            Object.assign((target.prototype || target), customLogger);
        }
        else {
            (target.prototype || target)[accessor] = customLogger;
        }
        return target;
    };
}