/* eslint no-prototype-builtins:1,no-restricted-syntax:[1, "ForInStatement"] */ 'use strict'; /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview log4js is a library to log in JavaScript in similar manner * than in log4j for Java. The API should be nearly the same. * *

Example:

*
 *  let logging = require('log4js');
 *  //add an appender that logs all messages to stdout.
 *  logging.addAppender(logging.consoleAppender());
 *  //add an appender that logs 'some-category' to a file
 *  logging.addAppender(logging.fileAppender('file.log'), 'some-category');
 *  //get a logger
 *  let log = logging.getLogger('some-category');
 *  log.setLevel(logging.levels.TRACE); //set the Level
 *
 *  ...
 *
 *  //call the log
 *  log.trace('trace me' );
 * 
* * NOTE: the authors below are the original browser-based log4js authors * don't try to contact them about bugs in this version :) * @version 1.0 * @author Stephan Strittmatter - http://jroller.com/page/stritti * @author Seth Chisamore - http://www.chisamore.com * @since 2005-05-20 * @static * Website: http://log4js.berlios.de */ const fs = require('fs'); const util = require('util'); const layouts = require('./layouts'); const levels = require('./levels'); const loggerModule = require('./logger'); const Logger = loggerModule.Logger; const ALL_CATEGORIES = '[all]'; let appenders = {}; const loggers = {}; const appenderMakers = {}; const appenderShutdowns = {}; const defaultConfig = { appenders: [ { type: 'console' } ], replaceConsole: false }; require('./appenders/console'); function hasLogger(logger) { return loggers.hasOwnProperty(logger); } levels.forName = function (levelStr, levelVal) { let level; if (typeof levelStr === 'string' && typeof levelVal === 'number') { const levelUpper = levelStr.toUpperCase(); level = new levels.Level(levelVal, levelUpper); loggerModule.addLevelMethods(level); } return level; }; function getBufferedLogger(categoryName) { const baseLogger = getLogger(categoryName); const logger = {}; logger.temp = []; logger.target = baseLogger; logger.flush = function () { for (let i = 0; i < logger.temp.length; i++) { const log = logger.temp[i]; logger.target[log.level](log.message); delete logger.temp[i]; } }; logger.trace = function (message) { logger.temp.push({ level: 'trace', message: message }); }; logger.debug = function (message) { logger.temp.push({ level: 'debug', message: message }); }; logger.info = function (message) { logger.temp.push({ level: 'info', message: message }); }; logger.warn = function (message) { logger.temp.push({ level: 'warn', message: message }); }; logger.error = function (message) { logger.temp.push({ level: 'error', message: message }); }; logger.fatal = function (message) { logger.temp.push({ level: 'fatal', message: message }); }; return logger; } function normalizeCategory(category) { return `${category}.`; } function doesLevelEntryContainsLogger(levelCategory, loggerCategory) { const normalizedLevelCategory = normalizeCategory(levelCategory); const normalizedLoggerCategory = normalizeCategory(loggerCategory); return normalizedLoggerCategory.substring(0, normalizedLevelCategory.length) === normalizedLevelCategory; } function doesAppenderContainsLogger(appenderCategory, loggerCategory) { const normalizedAppenderCategory = normalizeCategory(appenderCategory); const normalizedLoggerCategory = normalizeCategory(loggerCategory); return normalizedLoggerCategory.substring(0, normalizedAppenderCategory.length) === normalizedAppenderCategory; } /** * Get a logger instance. Instance is cached on categoryName level. * @static * @param loggerCategoryName * @return {Logger} instance of logger for the category */ function getLogger(loggerCategoryName) { // Use default logger if categoryName is not specified or invalid if (typeof loggerCategoryName !== 'string') { loggerCategoryName = Logger.DEFAULT_CATEGORY; } if (!hasLogger(loggerCategoryName)) { let level; /* jshint -W073 */ // If there's a 'levels' entry in the configuration if (levels.config) { // Goes through the categories in the levels configuration entry, // starting with the 'higher' ones. const keys = Object.keys(levels.config).sort(); for (let idx = 0; idx < keys.length; idx++) { const levelCategory = keys[idx]; if (doesLevelEntryContainsLogger(levelCategory, loggerCategoryName)) { // level for the logger level = levels.config[levelCategory]; } } } /* jshint +W073 */ // Create the logger for this name if it doesn't already exist loggers[loggerCategoryName] = new Logger(loggerCategoryName, level); /* jshint -W083 */ let appenderList; for (const appenderCategory in appenders) { if (doesAppenderContainsLogger(appenderCategory, loggerCategoryName)) { appenderList = appenders[appenderCategory]; appenderList.forEach(appender => { loggers[loggerCategoryName].addListener('log', appender); }); } } /* jshint +W083 */ if (appenders[ALL_CATEGORIES]) { appenderList = appenders[ALL_CATEGORIES]; appenderList.forEach(appender => { loggers[loggerCategoryName].addListener('log', appender); }); } } return loggers[loggerCategoryName]; } /** * args are appender, then zero or more categories */ function addAppender(...args) { const appender = args.shift(); if (args.length === 0 || args[0] === undefined) { args = [ALL_CATEGORIES]; } // argument may already be an array if (Array.isArray(args[0])) { args = args[0]; } args.forEach(appenderCategory => { addAppenderToCategory(appender, appenderCategory); if (appenderCategory === ALL_CATEGORIES) { addAppenderToAllLoggers(appender); } else { for (const loggerCategory in loggers) { if (doesAppenderContainsLogger(appenderCategory, loggerCategory)) { loggers[loggerCategory].addListener('log', appender); } } } }); } function addAppenderToAllLoggers(appender) { for (const logger in loggers) { if (hasLogger(logger)) { loggers[logger].addListener('log', appender); } } } function addAppenderToCategory(appender, category) { if (!appenders[category]) { appenders[category] = []; } appenders[category].push(appender); } function clearAppenders() { appenders = {}; for (const logger in loggers) { if (hasLogger(logger)) { loggers[logger].removeAllListeners('log'); } } } function configureAppenders(appenderList, options) { clearAppenders(); if (appenderList) { appenderList.forEach(appenderConfig => { loadAppender(appenderConfig.type); let appender; appenderConfig.makers = appenderMakers; try { appender = appenderMakers[appenderConfig.type](appenderConfig, options); addAppender(appender, appenderConfig.category); } catch (e) { throw new Error(`log4js configuration problem for ${util.inspect(appenderConfig)}`, e); } }); } } function configureLevels(_levels) { levels.config = _levels; // Keep it so we can create loggers later using this cfg if (_levels) { const keys = Object.keys(levels.config).sort(); /* eslint-disable guard-for-in */ for (const idx in keys) { const category = keys[idx]; if (category === ALL_CATEGORIES) { setGlobalLogLevel(_levels[category]); } for (const loggerCategory in loggers) { if (doesLevelEntryContainsLogger(category, loggerCategory)) { loggers[loggerCategory].setLevel(_levels[category]); } } } } } function setGlobalLogLevel(level) { Logger.prototype.level = levels.toLevel(level, levels.TRACE); } /** * Get the default logger instance. * @return {Logger} instance of default logger * @static */ function getDefaultLogger() { return getLogger(Logger.DEFAULT_CATEGORY); } const configState = {}; function loadConfigurationFile(filename) { if (filename) { return JSON.parse(fs.readFileSync(filename, 'utf8')); } return undefined; } function configureOnceOff(config, options) { if (config) { try { configureLevels(config.levels); configureAppenders(config.appenders, options); if (config.replaceConsole) { replaceConsole(); } else { restoreConsole(); } } catch (e) { throw new Error( `Problem reading log4js config ${util.inspect(config)}. Error was '${e.message}' (${e.stack})` ); } } } function reloadConfiguration(options) { const mtime = getMTime(configState.filename); if (!mtime) return; if (configState.lastMTime && (mtime.getTime() > configState.lastMTime.getTime())) { configureOnceOff(loadConfigurationFile(configState.filename), options); } configState.lastMTime = mtime; } function getMTime(filename) { let mtime; try { mtime = fs.statSync(configState.filename).mtime; } catch (e) { getLogger('log4js').warn(`Failed to load configuration file ${filename}`); } return mtime; } function initReloadConfiguration(filename, options) { if (configState.timerId) { clearInterval(configState.timerId); delete configState.timerId; } configState.filename = filename; configState.lastMTime = getMTime(filename); configState.timerId = setInterval(reloadConfiguration, options.reloadSecs * 1000, options); } function configure(configurationFileOrObject, options) { let config = configurationFileOrObject; config = config || process.env.LOG4JS_CONFIG; options = options || {}; if (config === undefined || config === null || typeof(config) === 'string') { if (options.reloadSecs) { initReloadConfiguration(config, options); } config = loadConfigurationFile(config) || defaultConfig; } else { if (options.reloadSecs) { getLogger('log4js').warn( 'Ignoring configuration reload parameter for "object" configuration.' ); } } configureOnceOff(config, options); } const originalConsoleFunctions = { log: console.log, debug: console.debug, info: console.info, warn: console.warn, error: console.error }; function replaceConsole(logger) { function replaceWith(fn) { return function (...args) { fn.apply(logger, args); }; } logger = logger || getLogger('console'); ['log', 'debug', 'info', 'warn', 'error'].forEach(item => { console[item] = replaceWith(item === 'log' ? logger.info : logger[item]); }); } function restoreConsole() { ['log', 'debug', 'info', 'warn', 'error'].forEach(item => { console[item] = originalConsoleFunctions[item]; }); } /* eslint global-require:0 */ /** * Load an appenderModule based on the provided appender filepath. Will first * check if the appender path is a subpath of the log4js 'lib/appenders' directory. * If not, it will attempt to load the the appender as complete path. * * @param {string} appender The filepath for the appender. * @returns {Object|null} The required appender or null if appender could not be loaded. * @private */ function requireAppender(appender) { let appenderModule; try { appenderModule = require(`./appenders/${appender}`); } catch (e) { appenderModule = require(appender); } return appenderModule; } /** * Load an appender. Provided the appender path to be loaded. If appenderModule is defined, * it will be used in place of requiring the appender module. * * @param {string} appender The path to the appender module. * @param {Object|void} [appenderModule] The pre-required appender module. When provided, * instead of requiring the appender by its path, this object will be used. * @returns {void} * @private */ function loadAppender(appender, appenderModule) { appenderModule = appenderModule || requireAppender(appender); if (!appenderModule) { throw new Error(`Invalid log4js appender: ${util.inspect(appender)}`); } module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule); if (appenderModule.shutdown) { appenderShutdowns[appender] = appenderModule.shutdown.bind(appenderModule); } appenderMakers[appender] = appenderModule.configure.bind(appenderModule); } /** * Shutdown all log appenders. This will first disable all writing to appenders * and then call the shutdown function each appender. * * @params {Function} cb - The callback to be invoked once all appenders have * shutdown. If an error occurs, the callback will be given the error object * as the first argument. */ function shutdown(cb) { // First, disable all writing to appenders. This prevents appenders from // not being able to be drained because of run-away log writes. loggerModule.disableAllLogWrites(); // Call each of the shutdown functions in parallel let completed = 0; let error; const shutdownFunctions = []; function complete(err) { error = error || err; completed++; if (completed >= shutdownFunctions.length) { cb(error); } } for (const category in appenderShutdowns) { if (appenderShutdowns.hasOwnProperty(category)) { shutdownFunctions.push(appenderShutdowns[category]); } } if (!shutdownFunctions.length) { return cb(); } shutdownFunctions.forEach(shutdownFct => { shutdownFct(complete); }); return null; } /** * * @name Log4js */ module.exports = { getBufferedLogger: getBufferedLogger, getLogger: getLogger, getDefaultLogger: getDefaultLogger, hasLogger: hasLogger, addAppender: addAppender, loadAppender: loadAppender, clearAppenders: clearAppenders, configure: configure, shutdown: shutdown, replaceConsole: replaceConsole, restoreConsole: restoreConsole, levels: levels, setGlobalLogLevel: setGlobalLogLevel, layouts: layouts, appenders: {}, appenderMakers: appenderMakers, connectLogger: require('./connect-logger').connectLogger }; // set ourselves up configure();