"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:

*
 *  var 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
 *  var 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 */ var events = require('events') , fs = require('fs') , path = require('path') , util = require('util') , layouts = require('./layouts') , levels = require('./levels') , loggerModule = require('./logger') , LoggingEvent = loggerModule.LoggingEvent , Logger = loggerModule.Logger , ALL_CATEGORIES = '[all]' , appenders = {} , loggers = {} , appenderMakers = {} , appenderShutdowns = {} , defaultConfig = { appenders: [ { type: "stdout" } ], replaceConsole: false }; function hasLogger(logger) { return loggers.hasOwnProperty(logger); } levels.forName = function(levelStr, levelVal) { var level; if (typeof levelStr === "string" && typeof levelVal === "number") { var levelUpper = levelStr.toUpperCase(); level = new levels.Level(levelVal, levelUpper); loggerModule.addLevelMethods(level); } return level; }; levels.getLevel = function(levelStr) { var level; if (typeof levelStr === "string") { var levelUpper = levelStr.toUpperCase(); level = levels.toLevel(levelStr); } return level; }; function getBufferedLogger(categoryName) { var base_logger = getLogger(categoryName); var logger = {}; logger.temp = []; logger.target = base_logger; logger.flush = function () { for (var i = 0; i < logger.temp.length; i++) { var 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) { var normalizedLevelCategory = normalizeCategory(levelCategory); var normalizedLoggerCategory = normalizeCategory(loggerCategory); return normalizedLoggerCategory.substring(0, normalizedLevelCategory.length) == normalizedLevelCategory; //jshint ignore:line } function doesAppenderContainsLogger (appenderCategory, loggerCategory) { var normalizedAppenderCategory = normalizeCategory(appenderCategory); var normalizedLoggerCategory = normalizeCategory(loggerCategory); return normalizedLoggerCategory.substring(0, normalizedAppenderCategory.length) == normalizedAppenderCategory; //jshint ignore:line } /** * Get a logger instance. Instance is cached on categoryName level. * @param {String} categoryName name of category to log to. * @return {Logger} instance of logger for the category * @static */ function getLogger (loggerCategoryName) { // Use default logger if categoryName is not specified or invalid if (typeof loggerCategoryName !== "string") { loggerCategoryName = Logger.DEFAULT_CATEGORY; } if (!hasLogger(loggerCategoryName)) { var 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. var keys = Object.keys(levels.config).sort(); for (var idx = 0; idx < keys.length; idx++) { var 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 */ var appenderList; for(var appenderCategory in appenders) { if (doesAppenderContainsLogger(appenderCategory, loggerCategoryName)) { appenderList = appenders[appenderCategory]; appenderList.forEach(function(appender) { loggers[loggerCategoryName].addListener("log", appender); }); } } /* jshint +W083 */ if (appenders[ALL_CATEGORIES]) { appenderList = appenders[ALL_CATEGORIES]; appenderList.forEach(function(appender) { loggers[loggerCategoryName].addListener("log", appender); }); } } return loggers[loggerCategoryName]; } /** * args are appender, optional shutdown function, then zero or more categories */ function addAppender () { var args = Array.prototype.slice.call(arguments); var appender = args.shift(); //check for a shutdown fn if (args.length > 0 && typeof args[0] === 'function') { appenderShutdowns[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(function(appenderCategory) { addAppenderToCategory(appender, appenderCategory); if (appenderCategory === ALL_CATEGORIES) { addAppenderToAllLoggers(appender); } else { for(var loggerCategory in loggers) { if (doesAppenderContainsLogger(appenderCategory,loggerCategory)) { loggers[loggerCategory].addListener("log", appender); } } } }); } function addAppenderToAllLoggers(appender) { for (var 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 () { //if we're calling clearAppenders, we're probably getting ready to write //so turn log writes back on, just in case this is after a shutdown loggerModule.enableAllLogWrites(); appenders = {}; for (var logger in loggers) { if (hasLogger(logger)) { loggers[logger].removeAllListeners("log"); } } } function configureAppenders(appenderList, options) { clearAppenders(); if (appenderList) { appenderList.forEach(function(appenderConfig) { loadAppender(appenderConfig.type); var 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) { var keys = Object.keys(levels.config).sort(); for (var idx in keys) { var category = keys[idx]; if(category === ALL_CATEGORIES) { setGlobalLogLevel(_levels[category]); } /* jshint -W073 */ for(var loggerCategory in loggers) { if (doesLevelEntryContainsLogger(category, loggerCategory)) { loggers[loggerCategory].setLevel(_levels[category]); } } /* jshint +W073 */ } } } 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); } var configState = {}; function loadConfigurationFile(filename) { if (filename) { return JSON.parse(fs.readFileSync(filename, "utf8")); } return undefined; } function configureOnceOff(config, options) { if (config) { try { restoreConsole(); configureLevels(config.levels); configureAppenders(config.appenders, options); if (config.replaceConsole) { replaceConsole(); } } catch (e) { throw new Error( "Problem reading log4js config " + util.inspect(config) + ". Error was \"" + e.message + "\" (" + e.stack + ")" ); } } } function reloadConfiguration(options) { var 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) { var 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) { var 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); } var originalConsoleFunctions = { log: console.log, debug: console.debug, info: console.info, warn: console.warn, error: console.error }; function replaceConsole(logger) { function replaceWith(fn) { return function() { fn.apply(logger, arguments); }; } logger = logger || getLogger("console"); ['log','debug','info','warn','error'].forEach(function (item) { console[item] = replaceWith(item === 'log' ? logger.info : logger[item]); }); } function restoreConsole() { ['log', 'debug', 'info', 'warn', 'error'].forEach(function (item) { console[item] = originalConsoleFunctions[item]; }); } /** * 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) { var 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. * @returns {void} */ 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(); //turn off config reloading if (configState.timerId) { clearInterval(configState.timerId); } // Call each of the shutdown functions in parallel var completed = 0; var error; var shutdownFcts = []; var complete = function(err) { error = error || err; completed++; if (completed >= shutdownFcts.length) { cb(error); } }; for (var category in appenderShutdowns) { if (appenderShutdowns.hasOwnProperty(category)) { shutdownFcts.push(appenderShutdowns[category]); } } if (!shutdownFcts.length) { return cb(); } shutdownFcts.forEach(function(shutdownFct) { shutdownFct(complete); }); } 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();