Source: index.js

// See https://github.com/jenkinsci/js-storage
var storage = require('@jenkins-cd/storage');
var jenkinsNS = storage.jenkinsNamespace();
var logging = jenkinsNS.subspace('logging');
var categories = logging.subspace('categories');

//
// Make sure the console log functions are supported in the environment.
//
if (!console.debug) {
    console.debug = console.log;
}
if (!console.info) {
    console.info = console.log;
}
if (!console.warn) {
    console.warn = console.log;
}
if (!console.error) {
    console.error = console.log;
}

// Output an info log with a hyperlink to the docs
console.info('Browser configuration of @jenkins-cd/logging is explained at https://tfennelly.github.io/jenkins-js-logging/index.html#browser-config');

/**
 * Log Level enum.
 * <p>
 * Defines simple logging levels i.e. {@link Level.DEBUG}, {@link Level.LOG}, {@link Level.INFO}, {@link Level.WARN}, {@link Level.ERROR}.
 * <p>
 * Do not construct this class; just use the above listed static property enums.
 * @constructor
 * @see <a href="./global.html#setLogLevel">setLogLevel</a>.
 */
function Level() {
    throw new Error('Unexpected construction of the Level class. Please use the static property enums i.e. Level.DEBUG etc.');
}
/**
 * Debug log level (console.debug).
 */
Level.DEBUG = { id: 'DEBUG', precedence: 0 };
/**
 * Log log level (console.log).
 */
Level.LOG = { id: 'LOG', precedence: 1 };
/**
 * Info log level (console.info).
 */
Level.INFO = { id: 'INFO', precedence: 2 };
/**
 * Warn log level (console.warn).
 */
Level.WARN = { id: 'WARN', precedence: 3 };
/**
 * Error log level (console.error).
 */
Level.ERROR = { id: 'ERROR', precedence: 4 };

exports.Level = Level;

var LEVEL_PARENTDOTCHECK_CONSTRAINTS = [Level.DEBUG.id, Level.LOG.id, Level.INFO.id, Level.WARN.id, Level.ERROR.id];

/**
 * Get the categories {StorageNamespace}.
 * <p>
 * External entities should not directly store anything in this namespace.
 * @returns {StorageNamespace} See https://github.com/jenkinsci/js-storage.
 * @see <a href="./global.html#setLogLevel">setLogLevel</a>.
 */
exports.getCategoriesStorageNS = function() {
    return categories;
};

/**
 * Set the log {Level} for a logging category.
 * @param {string} category The category name. Use dot separated naming convention to create a category hierarchy (ala Log4J).
 * @param {Level} level The logging level for the category.
 */
exports.setLogLevel = function(category, level) {
    if (level !== Level.ERROR && level !== Level.WARN && level !== Level.INFO && level !== Level.LOG && level !== Level.DEBUG) {
        throw new Error('Unexpected arg type for "level". Expected one of the predefined Level enums.');
    }
    categories.set(category, level.id);
};

/**
 * Create a new Logger instance for the specified category.
 * @param {string} category The category name. Use dot separated naming convention to create a category hierarchy (ala Log4J).
 * @constructor
 * @example
 * const logging = require('@jenkins-cd/logging');
 * const logger = logging.logger('org.jenkinsci.sse');
 *
 * if (logger.isDebugEnabled()) {
 *     logger.debug('Log a message for x and y values: ', x , y);
 * }
 */
function Logger(category) {
    if (category === undefined) {
        throw new Error('Cannot create logger. Log "category" name must be specified.');
    }
    this.category = category;

    function stringToLogLevel(logLevelId) {
        if (!logLevelId || logLevelId === '_') {
            return undefined;
        }
        return Level[logLevelId];
    }

    // Look for an exact matching category. If not configured, set it to a blank value
    // so as to make it discoverable in the browsers Storage Inspector.
    var logLevelId = categories.get(category);
    if (logLevelId === undefined) {
        categories.set(category, '_');
    }

    this.logLevel = stringToLogLevel(logLevelId);
    if (!this.logLevel) {
        logLevelId = categories.get(category, {checkDotParent: LEVEL_PARENTDOTCHECK_CONSTRAINTS});
        this.logLevel = stringToLogLevel(logLevelId);
        if (!this.logLevel) {
            // Default
            this.logLevel = Level.ERROR;
        }
    }
}
Logger.prototype = {

    /**
     * Is the specific logging level enabled for this logger instance.
     * @param {Level} level The logging level.
     * @returns {boolean} True if the level is enabled, otherwiser false.
     * @example
     * if (logger.isEnabled(Level.DEBUG)) {
     *     logger.debug('Log a message for x and y values: ', x , y);
     * }
     */
    isEnabled: function(level) {
        return level.precedence >= this.logLevel.precedence;
    },

    /**
     * Is the {@link Level#DEBUG Level.DEBUG} logging level enabled for this logger instance.
     * <p>
     * Shorthand for <code>logger.isEnabled(Level.DEBUG)</code>.
     * @returns {boolean} True if the level is enabled, otherwiser false.
     * @example
     * if (logger.isDebugEnabled()) {
     *     logger.debug('Log a message for x and y values: ', x , y);
     * }
     */
    isDebugEnabled: function () {
        return this.isEnabled(Level.DEBUG);
    },

    /**
     * Is the {@link Level#LOG Level.LOG} logging level enabled for this logger instance.
     * <p>
     * Shorthand for <code>logger.isEnabled(Level.LOG)</code>.
     * @returns {boolean} True if the level is enabled, otherwiser false.
     * @example
     * if (logger.isLogEnabled()) {
     *     logger.log('Log a message for x and y values: ', x , y);
     * }
     */
    isLogEnabled: function () {
        return this.isEnabled(Level.LOG);
    },

    /**
     * Is the {@link Level#INFO Level.INFO} logging level enabled for this logger instance.
     * <p>
     * Shorthand for <code>logger.isEnabled(Level.INFO)</code>.
     * @returns {boolean} True if the level is enabled, otherwiser false.
     * @example
     * if (logger.isInfoEnabled()) {
     *     logger.info('Log a message for x and y values: ', x , y);
     * }
     */
    isInfoEnabled: function () {
        return this.isEnabled(Level.INFO);
    },

    /**
     * Is the {@link Level#WARN Level.WARN} logging level enabled for this logger instance.
     * <p>
     * Shorthand for <code>logger.isEnabled(Level.WARN)</code>.
     * @returns {boolean} True if the level is enabled, otherwiser false.
     * @example
     * if (logger.isWarnEnabled()) {
     *     logger.warn('Log a message for x and y values: ', x , y);
     * }
     */
    isWarnEnabled: function () {
        return this.isEnabled(Level.WARN);
    },

    /**
     * Log a debug message.
     * @param {...*} message Message arguments.
     */
    debug: function (message) {
        if (this.isEnabled(Level.DEBUG)) {
            console.debug.apply(console, logArgs(Level.DEBUG, this.category, arguments));
        }
    },

    /**
     * Log an "log" level message.
     * @param {...*} message Message arguments.
     */
    log: function (message) {
        if (this.isEnabled(Level.LOG)) {
            console.log.apply(console, logArgs(Level.LOG, this.category, arguments));
        }
    },

    /**
     * Log an info message.
     * @param {...*} message Message arguments.
     */
    info: function (message) {
        if (this.isEnabled(Level.INFO)) {
            console.info.apply(console, logArgs(Level.INFO, this.category, arguments));
        }
    },

    /**
     * Log a warn message.
     * @param {...*} message Message arguments.
     */
    warn: function (message) {
        if (this.isEnabled(Level.WARN)) {
            console.warn.apply(console, logArgs(Level.WARN, this.category, arguments));
        }
    },

    /**
     * Log an error message.
     * @param {...*} message Message arguments.
     */
    error: function (message) {
        // Error messages are always logged. No need to check.
        console.error.apply(console, logArgs(Level.ERROR, this.category, arguments));
    }
};

function logArgs(level, category, callArgs) {
    if (!callArgs || callArgs.length === 0) {
        return callArgs;
    }

    // Make an array copy of the callArgs - it's not
    // a real array (it's "array-like").
    var callArgsCopy = [];
    for (var i = 0; i < callArgs.length; i++) {
        callArgsCopy.push(callArgs[i]);
    }

    // Create the message prefix, concatenating the log info with
    // the first string arg. This will allow formatted log strings
    // to still work.
    var prefix = '[' + level.id + ' - ' + category + '] ';
    if (typeof callArgsCopy[0] === 'string') {
        prefix += callArgsCopy.shift();
    }

    // Put them all back on the "array-like" args object
    // and return it.
    callArgs[0] = prefix;
    for (var ii = 0; ii < callArgsCopy.length; ii++) {
        callArgs[ii + 1] = callArgsCopy[ii];
    }

    return callArgs;
}

/**
 * Create a {Logger} instance for the specified category.
 * @param {string} category The category name. Use dot separated naming convention to create a category hierarchy (ala Log4J).
 * @returns {Logger} The {Logger} instance.
 */
exports.logger = function(category) {
    return new Logger(category);
};