Could not delete date-file-test.log { [Error: ENOENT, unlink '/Users/garethjones/log4js-node/.bob/instrumented/test/date-file-test.log'] errno: 34, code: 'ENOENT', path: '/Users/garethjones/log4js-node/.bob/instrumented/test/date-file-test.log' } levels.js: [ hits: 28, misses: 0, sloc: 28, coverage: 100.00% ] 82 | "use strict"; | 82 | function Level(level, levelStr) { 656 | this.level = level; 656 | this.levelStr = levelStr; | } | | /** | * converts given String to corresponding Level | * @param {String} sArg String value of Level OR Log4js.Level | * @param {Log4js.Level} defaultLevel default Level, if no String representation | * @return Level object | * @type Log4js.Level | */ 82 | function toLevel(sArg, defaultLevel) { | 1726 | if (!sArg) { 16 | return defaultLevel; | } | 1710 | if (typeof sArg == "string") { 1636 | var s = sArg.toUpperCase(); 1636 | if (module.exports[s]) { 1632 | return module.exports[s]; | } else { 4 | return defaultLevel; | } | } | 74 | return toLevel(sArg.toString()); | } | 82 | Level.prototype.toString = function() { 556 | return this.levelStr; | }; | 82 | Level.prototype.isLessThanOrEqualTo = function(otherLevel) { 1086 | if (typeof otherLevel === "string") { 151 | otherLevel = toLevel(otherLevel); | } 1086 | return this.level <= otherLevel.level; | }; | 82 | Level.prototype.isGreaterThanOrEqualTo = function(otherLevel) { 103 | if (typeof otherLevel === "string") { 7 | otherLevel = toLevel(otherLevel); | } 103 | return this.level >= otherLevel.level; | }; | 82 | Level.prototype.isEqualTo = function(otherLevel) { 78 | if (typeof otherLevel == "string") { 3 | otherLevel = toLevel(otherLevel); | } 78 | return this.level === otherLevel.level; | }; | 82 | module.exports = { | ALL: new Level(Number.MIN_VALUE, "ALL"), | TRACE: new Level(5000, "TRACE"), | DEBUG: new Level(10000, "DEBUG"), | INFO: new Level(20000, "INFO"), | WARN: new Level(30000, "WARN"), | ERROR: new Level(40000, "ERROR"), | FATAL: new Level(50000, "FATAL"), | OFF: new Level(Number.MAX_VALUE, "OFF"), | toLevel: toLevel | }; layouts.js: [ hits: 115, misses: 0, sloc: 115, coverage: 100.00% ] 45 | "use strict"; 45 | var dateFormat = require('./date_format') | , os = require('os') | , eol = os.EOL || '\n' | , util = require('util') | , replacementRegExp = /%[sdj]/g | , layoutMakers = { 7 | "messagePassThrough": function() { return messagePassThroughLayout; }, 1 | "basic": function() { return basicLayout; }, 1 | "colored": function() { return colouredLayout; }, 1 | "coloured": function() { return colouredLayout; }, | "pattern": function (config) { 1 | return patternLayout(config && config.pattern, config && config.tokens); | } | } | , colours = { | ALL: "grey", | TRACE: "blue", | DEBUG: "cyan", | INFO: "green", | WARN: "yellow", | ERROR: "red", | FATAL: "magenta", | OFF: "grey" | }; | 45 | function wrapErrorsWithInspect(items) { 81 | return items.map(function(item) { 155 | if ((item instanceof Error) && item.stack) { 6 | return { inspect: function() { return util.format(item) + '\n' + item.stack; } }; | } else { 152 | return item; | } | }); | } | 45 | function formatLogData(logData) { 81 | var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments); 81 | return util.format.apply(util, wrapErrorsWithInspect(data)); | } | 45 | var styles = { | //styles | 'bold' : [1, 22], | 'italic' : [3, 23], | 'underline' : [4, 24], | 'inverse' : [7, 27], | //grayscale | 'white' : [37, 39], | 'grey' : [90, 39], | 'black' : [90, 39], | //colors | 'blue' : [34, 39], | 'cyan' : [36, 39], | 'green' : [32, 39], | 'magenta' : [35, 39], | 'red' : [31, 39], | 'yellow' : [33, 39] | }; | 45 | function colorizeStart(style) { 24 | return style ? '\x1B[' + styles[style][0] + 'm' : ''; | } 45 | function colorizeEnd(style) { 24 | return style ? '\x1B[' + styles[style][1] + 'm' : ''; | } | /** | * Taken from masylum's fork (https://github.com/masylum/log4js-node) | */ 45 | function colorize (str, style) { 23 | return colorizeStart(style) + str + colorizeEnd(style); | } | 45 | function timestampLevelAndCategory(loggingEvent, colour) { 23 | var output = colorize( | formatLogData( | '[%s] [%s] %s - ' | , dateFormat.asString(loggingEvent.startTime) | , loggingEvent.level | , loggingEvent.categoryName | ) | , colour | ); 23 | return output; | } | | /** | * BasicLayout is a simple layout for storing the logs. The logs are stored | * in following format: | * <pre> | * [startTime] [logLevel] categoryName - message\n | * </pre> | * | * @author Stephan Strittmatter | */ 45 | function basicLayout (loggingEvent) { 21 | return timestampLevelAndCategory(loggingEvent) + formatLogData(loggingEvent.data); | } | | /** | * colouredLayout - taken from masylum's fork. | * same as basicLayout, but with colours. | */ 45 | function colouredLayout (loggingEvent) { 2 | return timestampLevelAndCategory( | loggingEvent, | colours[loggingEvent.level.toString()] | ) + formatLogData(loggingEvent.data); | } | 45 | function messagePassThroughLayout (loggingEvent) { 27 | return formatLogData(loggingEvent.data); | } | | /** | * PatternLayout | * Format for specifiers is %[padding].[truncation][field]{[format]} | * e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10 | * Fields can be any of: | * - %r time in toLocaleTimeString format | * - %p log level | * - %c log category | * - %m log data | * - %d date in various formats | * - %% % | * - %n newline | * - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter | * You can use %[ and %] to define a colored block. | * | * Tokens are specified as simple key:value objects. | * The key represents the token name whereas the value can be a string or function | * which is called to extract the value to put in the log message. If token is not | * found, it doesn't replace the field. | * | * A sample token would be: { "pid" : function() { return process.pid; } } | * | * Takes a pattern string, array of tokens and returns a layout function. | * @param {String} Log format pattern String | * @param {object} map object of different tokens | * @return {Function} | * @author Stephan Strittmatter | * @author Jan Schmidle | */ 45 | function patternLayout (pattern, tokens) { 42 | var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n"; 42 | var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdmnprx%])(\{([^\}]+)\})?|([^%]+)/; | 42 | pattern = pattern || TTCC_CONVERSION_PATTERN; | 42 | function categoryName(loggingEvent, specifier) { 9 | var loggerName = loggingEvent.categoryName; 9 | if (specifier) { 7 | var precision = parseInt(specifier, 10); 7 | var loggerNameBits = loggerName.split("."); 7 | if (precision < loggerNameBits.length) { 4 | loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join("."); | } | } 9 | return loggerName; | } | 42 | function formatAsDate(loggingEvent, specifier) { 7 | var format = dateFormat.ISO8601_FORMAT; 7 | if (specifier) { 6 | format = specifier; | // Pick up special cases 6 | if (format == "ISO8601") { 1 | format = dateFormat.ISO8601_FORMAT; 5 | } else if (format == "ABSOLUTE") { 2 | format = dateFormat.ABSOLUTETIME_FORMAT; 3 | } else if (format == "DATE") { 1 | format = dateFormat.DATETIME_FORMAT; | } | } | // Format the date 7 | return dateFormat.asString(format, loggingEvent.startTime); | } | 42 | function formatMessage(loggingEvent) { 8 | return formatLogData(loggingEvent.data); | } | 42 | function endOfLine() { 4 | return eol; | } | 42 | function logLevel(loggingEvent) { 11 | return loggingEvent.level.toString(); | } | 42 | function startTime(loggingEvent) { 3 | return "" + loggingEvent.startTime.toLocaleTimeString(); | } | 42 | function startColour(loggingEvent) { 1 | return colorizeStart(colours[loggingEvent.level.toString()]); | } | 42 | function endColour(loggingEvent) { 1 | return colorizeEnd(colours[loggingEvent.level.toString()]); | } | 42 | function percent() { 1 | return '%'; | } | 42 | function userDefined(loggingEvent, specifier) { 5 | if (typeof(tokens[specifier]) !== 'undefined') { 3 | if (typeof(tokens[specifier]) === 'function') { 2 | return tokens[specifier](loggingEvent); | } else { 1 | return tokens[specifier]; | } | } 2 | return null; | } | 42 | var replacers = { | 'c': categoryName, | 'd': formatAsDate, | 'm': formatMessage, | 'n': endOfLine, | 'p': logLevel, | 'r': startTime, | '[': startColour, | ']': endColour, | '%': percent, | 'x': userDefined | }; | 42 | function replaceToken(conversionCharacter, loggingEvent, specifier) { 50 | return replacers[conversionCharacter](loggingEvent, specifier); | } | 42 | function truncate(truncation, toTruncate) { 50 | var len; 50 | if (truncation) { 5 | len = parseInt(truncation.substr(1), 10); 5 | return toTruncate.substring(0, len); | } | 45 | return toTruncate; | } | 42 | function pad(padding, toPad) { 50 | var len; 50 | if (padding) { 8 | if (padding.charAt(0) == "-") { 4 | len = parseInt(padding.substr(1), 10); | // Right pad with spaces 4 | while (toPad.length < len) { 9 | toPad += " "; | } | } else { 4 | len = parseInt(padding, 10); | // Left pad with spaces 4 | while (toPad.length < len) { 9 | toPad = " " + toPad; | } | } | } 50 | return toPad; | } | 42 | return function(loggingEvent) { 41 | var formattedString = ""; 41 | var result; 41 | var searchString = pattern; | 41 | while ((result = regex.exec(searchString))) { 58 | var matchedString = result[0]; 58 | var padding = result[1]; 58 | var truncation = result[2]; 58 | var conversionCharacter = result[3]; 58 | var specifier = result[5]; 58 | var text = result[6]; | | // Check if the pattern matched was just normal text 58 | if (text) { 8 | formattedString += "" + text; | } else { | // Create a raw replacement string based on the conversion | // character and specifier 50 | var replacement = | replaceToken(conversionCharacter, loggingEvent, specifier) || | matchedString; | | // Format the replacement according to any padding or | // truncation specified 50 | replacement = truncate(truncation, replacement); 50 | replacement = pad(padding, replacement); 50 | formattedString += replacement; | } 58 | searchString = searchString.substr(result.index + result[0].length); | } 41 | return formattedString; | }; | | } | 45 | module.exports = { | basicLayout: basicLayout, | messagePassThroughLayout: messagePassThroughLayout, | patternLayout: patternLayout, | colouredLayout: colouredLayout, | coloredLayout: colouredLayout, | layout: function(name, config) { 11 | return layoutMakers[name] && layoutMakers[name](config); | } | }; date_format.js: [ hits: 38, misses: 0, sloc: 38, coverage: 100.00% ] 45 | "use strict"; 45 | exports.ISO8601_FORMAT = "yyyy-MM-dd hh:mm:ss.SSS"; 45 | exports.ISO8601_WITH_TZ_OFFSET_FORMAT = "yyyy-MM-ddThh:mm:ssO"; 45 | exports.DATETIME_FORMAT = "dd MM yyyy hh:mm:ss.SSS"; 45 | exports.ABSOLUTETIME_FORMAT = "hh:mm:ss.SSS"; | 45 | function padWithZeros(vNumber, width) { 456 | var numAsString = vNumber + ""; 456 | while (numAsString.length < width) { 184 | numAsString = "0" + numAsString; | } 456 | return numAsString; | } | 45 | function addZero(vNumber) { 399 | return padWithZeros(vNumber, 2); | } | | /** | * Formats the TimeOffest | * Thanks to http://www.svendtofte.com/code/date_format/ | * @private | */ 45 | function offset(date) { | // Difference to Greenwich time (GMT) in hours 57 | var os = Math.abs(date.getTimezoneOffset()); 57 | var h = String(Math.floor(os/60)); 57 | var m = String(os%60); 57 | if (h.length == 1) { 2 | h = "0" + h; | } 57 | if (m.length == 1) { 57 | m = "0" + m; | } 57 | return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; | } | 45 | exports.asString = function(/*format,*/ date) { 57 | var format = exports.ISO8601_FORMAT; 57 | if (typeof(date) === "string") { 33 | format = arguments[0]; 33 | date = arguments[1]; | } | 57 | var vDay = addZero(date.getDate()); 57 | var vMonth = addZero(date.getMonth()+1); 57 | var vYearLong = addZero(date.getFullYear()); 57 | var vYearShort = addZero(date.getFullYear().toString().substring(3,4)); 57 | var vYear = (format.indexOf("yyyy") > -1 ? vYearLong : vYearShort); 57 | var vHour = addZero(date.getHours()); 57 | var vMinute = addZero(date.getMinutes()); 57 | var vSecond = addZero(date.getSeconds()); 57 | var vMillisecond = padWithZeros(date.getMilliseconds(), 3); 57 | var vTimeZone = offset(date); 57 | var formatted = format | .replace(/dd/g, vDay) | .replace(/MM/g, vMonth) | .replace(/y{1,4}/g, vYear) | .replace(/hh/g, vHour) | .replace(/mm/g, vMinute) | .replace(/ss/g, vSecond) | .replace(/SSS/g, vMillisecond) | .replace(/O/g, vTimeZone); 57 | return formatted; | | }; log4js.js: [ hits: 127, misses: 0, sloc: 127, coverage: 100.00% ] 24 | "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. | * | * <h3>Example:</h3> | * <pre> | * 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" ); | * </pre> | * | * 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 | */ 24 | var events = require('events') | , fs = require('fs') | , path = require('path') | , util = require('util') | , layouts = require('./layouts') | , levels = require('./levels') | , LoggingEvent = require('./logger').LoggingEvent | , Logger = require('./logger').Logger | , ALL_CATEGORIES = '[all]' | , appenders = {} | , loggers = {} | , appenderMakers = {} | , defaultConfig = { | appenders: [ | { type: "console" } | ], | replaceConsole: false | }; | | /** | * 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 | */ 24 | function getLogger (categoryName) { | | // Use default logger if categoryName is not specified or invalid 445 | if (typeof categoryName !== "string") { 1 | categoryName = Logger.DEFAULT_CATEGORY; | } | 445 | var appenderList; 445 | if (!loggers[categoryName]) { | // Create the logger for this name if it doesn't already exist 39 | loggers[categoryName] = new Logger(categoryName); 39 | if (appenders[categoryName]) { 12 | appenderList = appenders[categoryName]; 12 | appenderList.forEach(function(appender) { 12 | loggers[categoryName].addListener("log", appender); | }); | } 39 | if (appenders[ALL_CATEGORIES]) { 23 | appenderList = appenders[ALL_CATEGORIES]; 23 | appenderList.forEach(function(appender) { 33 | loggers[categoryName].addListener("log", appender); | }); | } | } | 445 | return loggers[categoryName]; | } | | /** | * args are appender, then zero or more categories | */ 24 | function addAppender () { 78 | var args = Array.prototype.slice.call(arguments); 78 | var appender = args.shift(); 78 | if (args.length === 0 || args[0] === undefined) { 46 | args = [ ALL_CATEGORIES ]; | } | //argument may already be an array 78 | if (Array.isArray(args[0])) { 1 | args = args[0]; | } | 78 | args.forEach(function(category) { 80 | addAppenderToCategory(appender, category); | 80 | if (category === ALL_CATEGORIES) { 46 | addAppenderToAllLoggers(appender); 34 | } else if (loggers[category]) { 22 | loggers[category].addListener("log", appender); | } | }); | } | 24 | function addAppenderToAllLoggers(appender) { 46 | for (var logger in loggers) { 58 | if (loggers.hasOwnProperty(logger)) { 58 | loggers[logger].addListener("log", appender); | } | } | } | 24 | function addAppenderToCategory(appender, category) { 80 | if (!appenders[category]) { 66 | appenders[category] = []; | } 80 | appenders[category].push(appender); | } | 24 | function clearAppenders () { 71 | appenders = {}; 71 | for (var logger in loggers) { 202 | if (loggers.hasOwnProperty(logger)) { 202 | loggers[logger].removeAllListeners("log"); | } | } | } | 24 | function configureAppenders(appenderList, options) { 50 | clearAppenders(); 50 | if (appenderList) { 39 | appenderList.forEach(function(appenderConfig) { 40 | loadAppender(appenderConfig.type); 40 | var appender; 40 | appenderConfig.makers = appenderMakers; 40 | try { 40 | appender = appenderMakers[appenderConfig.type](appenderConfig, options); 39 | addAppender(appender, appenderConfig.category); | } catch(e) { 1 | throw new Error("log4js configuration problem for " + util.inspect(appenderConfig), e); | } | }); | } | } | 24 | function configureLevels(levels) { 49 | if (levels) { 11 | for (var category in levels) { 10 | if (levels.hasOwnProperty(category)) { 10 | getLogger(category).setLevel(levels[category]); | } | } | } | } | 24 | function setGlobalLogLevel(level) { 2 | Logger.prototype.level = levels.toLevel(level, levels.TRACE); | } | | /** | * Get the default logger instance. | * @return {Logger} instance of default logger | * @static | */ 24 | function getDefaultLogger () { 1 | return getLogger(Logger.DEFAULT_CATEGORY); | } | 24 | var configState = {}; | 24 | function loadConfigurationFile(filename) { 36 | if (filename) { 11 | return JSON.parse(fs.readFileSync(filename, "utf8")); | } 25 | return undefined; | } | 24 | function configureOnceOff(config, options) { 50 | if (config) { 50 | try { 50 | configureAppenders(config.appenders, options); 49 | configureLevels(config.levels); | 49 | if (config.replaceConsole) { 1 | replaceConsole(); | } else { 48 | restoreConsole(); | } | } catch (e) { 1 | throw new Error( | "Problem reading log4js config " + util.inspect(config) + | ". Error was \"" + e.message + "\" (" + e.stack + ")" | ); | } | } | } | 24 | function reloadConfiguration() { 3 | var mtime = getMTime(configState.filename); 4 | if (!mtime) return; | 2 | if (configState.lastMTime && (mtime.getTime() > configState.lastMTime.getTime())) { 1 | configureOnceOff(loadConfigurationFile(configState.filename)); | } 2 | configState.lastMTime = mtime; | } | 24 | function getMTime(filename) { 8 | var mtime; 8 | try { 8 | mtime = fs.statSync(configState.filename).mtime; | } catch (e) { 1 | getLogger('log4js').warn('Failed to load configuration file ' + filename); | } 8 | return mtime; | } | 24 | function initReloadConfiguration(filename, options) { 5 | if (configState.timerId) { 1 | clearInterval(configState.timerId); 1 | delete configState.timerId; | } 5 | configState.filename = filename; 5 | configState.lastMTime = getMTime(filename); 5 | configState.timerId = setInterval(reloadConfiguration, options.reloadSecs*1000); | } | 24 | function configure(configurationFileOrObject, options) { 49 | var config = configurationFileOrObject; 49 | config = config || process.env.LOG4JS_CONFIG; 49 | options = options || {}; | 49 | if (config === undefined || config === null || typeof(config) === 'string') { 35 | if (options.reloadSecs) { 5 | initReloadConfiguration(config, options); | } 35 | config = loadConfigurationFile(config) || defaultConfig; | } else { 14 | if (options.reloadSecs) { 1 | getLogger('log4js').warn( | 'Ignoring configuration reload parameter for "object" configuration.' | ); | } | } 49 | configureOnceOff(config, options); | } | 24 | var originalConsoleFunctions = { | log: console.log, | debug: console.debug, | info: console.info, | warn: console.warn, | error: console.error | }; | 24 | function replaceConsole(logger) { 3 | function replaceWith(fn) { 15 | return function() { 10 | fn.apply(logger, arguments); | }; | } 3 | logger = logger || getLogger("console"); 3 | ['log','debug','info','warn','error'].forEach(function (item) { 15 | console[item] = replaceWith(item === 'log' ? logger.info : logger[item]); | }); | } | 24 | function restoreConsole() { 50 | ['log', 'debug', 'info', 'warn', 'error'].forEach(function (item) { 250 | console[item] = originalConsoleFunctions[item]; | }); | } | 24 | function loadAppender(appender) { 43 | var appenderModule; 43 | try { 43 | appenderModule = require('./appenders/' + appender); | } catch (e) { 1 | appenderModule = require(appender); | } 43 | module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule); 43 | appenderMakers[appender] = appenderModule.configure.bind(appenderModule); | } | 24 | module.exports = { | getLogger: getLogger, | getDefaultLogger: getDefaultLogger, | | addAppender: addAppender, | loadAppender: loadAppender, | clearAppenders: clearAppenders, | configure: configure, | | replaceConsole: replaceConsole, | restoreConsole: restoreConsole, | | levels: levels, | setGlobalLogLevel: setGlobalLogLevel, | | layouts: layouts, | appenders: {}, | appenderMakers: appenderMakers, | connectLogger: require('./connect-logger').connectLogger | }; | | //set ourselves up 24 | configure(); | logger.js: [ hits: 35, misses: 0, sloc: 35, coverage: 100.00% ] 41 | "use strict"; 41 | var levels = require('./levels') | , util = require('util') | , events = require('events') | , DEFAULT_CATEGORY = '[default]'; | | /** | * Models a logging event. | * @constructor | * @param {String} categoryName name of category | * @param {Log4js.Level} level level of message | * @param {Array} data objects to log | * @param {Log4js.Logger} logger the associated logger | * @author Seth Chisamore | */ 41 | function LoggingEvent (categoryName, level, data, logger) { 71 | this.startTime = new Date(); 71 | this.categoryName = categoryName; 71 | this.data = data; 71 | this.level = level; 71 | this.logger = logger; | } | | /** | * Logger to log messages. | * use {@see Log4js#getLogger(String)} to get an instance. | * @constructor | * @param name name of category to log to | * @author Stephan Strittmatter | */ 41 | function Logger (name, level) { 43 | this.category = name || DEFAULT_CATEGORY; | 43 | if (level) { 2 | this.setLevel(level); | } | } 41 | util.inherits(Logger, events.EventEmitter); 41 | Logger.DEFAULT_CATEGORY = DEFAULT_CATEGORY; 41 | Logger.prototype.level = levels.TRACE; | 41 | Logger.prototype.setLevel = function(level) { 213 | this.level = levels.toLevel(level, this.level || levels.TRACE); | }; | 41 | Logger.prototype.removeLevel = function() { 2 | delete this.level; | }; | 41 | Logger.prototype.log = function() { 71 | var args = Array.prototype.slice.call(arguments) | , logLevel = args.shift() | , loggingEvent = new LoggingEvent(this.category, logLevel, args, this); 71 | this.emit("log", loggingEvent); | }; | 41 | Logger.prototype.isLevelEnabled = function(otherLevel) { 554 | return this.level.isLessThanOrEqualTo(otherLevel); | }; | 41 | ['Trace','Debug','Info','Warn','Error','Fatal'].forEach( | function(levelString) { 246 | var level = levels.toLevel(levelString); 246 | Logger.prototype['is'+levelString+'Enabled'] = function() { 6 | return this.isLevelEnabled(level); | }; | 246 | Logger.prototype[levelString.toLowerCase()] = function () { 80 | if (this.isLevelEnabled(level)) { 71 | var args = Array.prototype.slice.call(arguments); 71 | args.unshift(level); 71 | Logger.prototype.log.apply(this, args); | } | }; | } | ); | | 41 | exports.LoggingEvent = LoggingEvent; 41 | exports.Logger = Logger; connect-logger.js: [ hits: 53, misses: 0, sloc: 53, coverage: 100.00% ] 24 | "use strict"; 24 | var levels = require("./levels"); 24 | var DEFAULT_FORMAT = ':remote-addr - -' + | ' ":method :url HTTP/:http-version"' + | ' :status :content-length ":referrer"' + | ' ":user-agent"'; | /** | * Log requests with the given `options` or a `format` string. | * | * Options: | * | * - `format` Format string, see below for tokens | * - `level` A log4js levels instance. Supports also 'auto' | * | * Tokens: | * | * - `:req[header]` ex: `:req[Accept]` | * - `:res[header]` ex: `:res[Content-Length]` | * - `:http-version` | * - `:response-time` | * - `:remote-addr` | * - `:date` | * - `:method` | * - `:url` | * - `:referrer` | * - `:user-agent` | * - `:status` | * | * @param {String|Function|Object} format or options | * @return {Function} | * @api public | */ | 24 | function getLogger(logger4js, options) { 13 | if ('object' == typeof options) { 6 | options = options || {}; 7 | } else if (options) { 4 | options = { format: options }; | } else { 3 | options = {}; | } | 13 | var thislogger = logger4js | , level = levels.toLevel(options.level, levels.INFO) | , fmt = options.format || DEFAULT_FORMAT | , nolog = options.nolog ? createNoLogCondition(options.nolog) : null; | 13 | return function (req, res, next) { | // mount safety 23 | if (req._logging) return next(); | | // nologs 30 | if (nolog && nolog.test(req.originalUrl)) return next(); 16 | if (thislogger.isLevelEnabled(level) || options.level === 'auto') { | 15 | var start = new Date() | , statusCode | , writeHead = res.writeHead | , end = res.end | , url = req.originalUrl; | | // flag as logging 15 | req._logging = true; | | // proxy for statusCode. 15 | res.writeHead = function(code, headers){ 11 | res.writeHead = writeHead; 11 | res.writeHead(code, headers); 11 | res.__statusCode = statusCode = code; 11 | res.__headers = headers || {}; | | //status code response level handling 11 | if(options.level === 'auto'){ 5 | level = levels.INFO; 8 | if(code >= 300) level = levels.WARN; 7 | if(code >= 400) level = levels.ERROR; | } else { 6 | level = levels.toLevel(options.level, levels.INFO); | } | }; | | // proxy end to output a line to the provided logger. 15 | res.end = function(chunk, encoding) { 15 | res.end = end; 15 | res.end(chunk, encoding); 15 | res.responseTime = new Date() - start; 15 | if (thislogger.isLevelEnabled(level)) { 15 | if (typeof fmt === 'function') { 1 | var line = fmt(req, res, function(str){ return format(str, req, res); }); 2 | if (line) thislogger.log(level, line); | } else { 14 | thislogger.log(level, format(fmt, req, res)); | } | } | }; | } | | //ensure next gets always called 16 | next(); | }; | } | | /** | * Return formatted log line. | * | * @param {String} str | * @param {IncomingMessage} req | * @param {ServerResponse} res | * @return {String} | * @api private | */ | 24 | function format(str, req, res) { 14 | return str | .replace(':url', req.originalUrl) | .replace(':method', req.method) | .replace(':status', res.__statusCode || res.statusCode) | .replace(':response-time', res.responseTime) | .replace(':date', new Date().toUTCString()) | .replace(':referrer', req.headers.referer || req.headers.referrer || '') | .replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor) | .replace( | ':remote-addr', | req.socket && | (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)) | ) | .replace(':user-agent', req.headers['user-agent'] || '') | .replace( | ':content-length', | (res._headers && res._headers['content-length']) || | (res.__headers && res.__headers['Content-Length']) || | '-' | ) 1 | .replace(/:req\[([^\]]+)\]/g, function(_, field){ return req.headers[field.toLowerCase()]; }) | .replace(/:res\[([^\]]+)\]/g, function(_, field){ 1 | return res._headers ? | (res._headers[field.toLowerCase()] || res.__headers[field]) | : (res.__headers && res.__headers[field]); | }); | } | | /** | * Return RegExp Object about nolog | * | * @param {String} nolog | * @return {RegExp} | * @api private | * | * syntax | * 1. String | * 1.1 "\\.gif" | * NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.gif?fuga | * LOGGING http://example.com/hoge.agif | * 1.2 in "\\.gif|\\.jpg$" | * NOT LOGGING http://example.com/hoge.gif and | * http://example.com/hoge.gif?fuga and http://example.com/hoge.jpg?fuga | * LOGGING http://example.com/hoge.agif, | * http://example.com/hoge.ajpg and http://example.com/hoge.jpg?hoge | * 1.3 in "\\.(gif|jpe?g|png)$" | * NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.jpeg | * LOGGING http://example.com/hoge.gif?uid=2 and http://example.com/hoge.jpg?pid=3 | * 2. RegExp | * 2.1 in /\.(gif|jpe?g|png)$/ | * SAME AS 1.3 | * 3. Array | * 3.1 ["\\.jpg$", "\\.png", "\\.gif"] | * SAME AS "\\.jpg|\\.png|\\.gif" | */ 24 | function createNoLogCondition(nolog) { 4 | var regexp = null; | 4 | if (nolog) { 4 | if (nolog instanceof RegExp) { 1 | regexp = nolog; | } | 4 | if (typeof nolog === 'string') { 2 | regexp = new RegExp(nolog); | } | 4 | if (Array.isArray(nolog)) { 1 | var regexpsAsStrings = nolog.map( | function convertToStrings(o) { 2 | return o.source ? o.source : o; | } | ); 1 | regexp = new RegExp(regexpsAsStrings.join('|')); | } | } | 4 | return regexp; | } | 24 | exports.connectLogger = getLogger; appenders/console.js: [ hits: 13, misses: 0, sloc: 13, coverage: 100.00% ] 19 | "use strict"; 19 | var layouts = require('../layouts') | , consoleLog = console.log.bind(console); | 19 | function consoleAppender (layout) { 21 | layout = layout || layouts.colouredLayout; 21 | return function(loggingEvent) { 1 | consoleLog(layout(loggingEvent)); | }; | } | 19 | function configure(config) { 20 | var layout; 20 | if (config.layout) { 1 | layout = layouts.layout(config.layout.type, config.layout); | } 20 | return consoleAppender(layout); | } | 19 | exports.appender = consoleAppender; 19 | exports.configure = configure; streams/BaseRollingFileStream.js: [ hits: 48, misses: 0, sloc: 48, coverage: 100.00% ] 4 | "use strict"; 4 | var fs = require('fs') | , stream | , debug = require('../debug')('BaseRollingFileStream') | , util = require('util') | , semver = require('semver'); | 4 | if (semver.satisfies(process.version, '>=0.10.0')) { 3 | stream = require('stream'); | } else { 1 | stream = require('readable-stream'); | } | 4 | module.exports = BaseRollingFileStream; | 4 | function BaseRollingFileStream(filename, options) { 24 | debug("In BaseRollingFileStream"); 24 | this.filename = filename; 24 | this.options = options || { encoding: 'utf8', mode: parseInt('0644', 8), flags: 'a' }; 24 | this.currentSize = 0; | 24 | function currentFileSize(file) { 23 | var fileSize = 0; 23 | try { 23 | fileSize = fs.statSync(file).size; | } catch (e) { | // file does not exist | } 23 | return fileSize; | } | 24 | function throwErrorIfArgumentsAreNotValid() { 24 | if (!filename) { 1 | throw new Error("You must specify a filename"); | } | } | 24 | throwErrorIfArgumentsAreNotValid(); 23 | debug("Calling BaseRollingFileStream.super"); 23 | BaseRollingFileStream.super_.call(this); 23 | this.openTheStream(); 23 | this.currentSize = currentFileSize(this.filename); | } 4 | util.inherits(BaseRollingFileStream, stream.Writable); | 4 | BaseRollingFileStream.prototype._write = function(chunk, encoding, callback) { 29 | var that = this; 29 | function writeTheChunk() { 29 | debug("writing the chunk to the underlying stream"); 29 | that.currentSize += chunk.length; 29 | that.theStream.write(chunk, encoding, callback); | } | 29 | debug("in _write"); | 29 | if (this.shouldRoll()) { 8 | this.currentSize = 0; 8 | this.roll(this.filename, writeTheChunk); | } else { 21 | writeTheChunk(); | } | }; | 4 | BaseRollingFileStream.prototype.openTheStream = function(cb) { 31 | debug("opening the underlying stream"); 31 | this.theStream = fs.createWriteStream(this.filename, this.options); 31 | if (cb) { 8 | this.theStream.on("open", cb); | } | }; | 4 | BaseRollingFileStream.prototype.closeTheStream = function(cb) { 8 | debug("closing the underlying stream"); 8 | this.theStream.end(cb); | }; | 4 | BaseRollingFileStream.prototype.shouldRoll = function() { 1 | return false; // default behaviour is never to roll | }; | 4 | BaseRollingFileStream.prototype.roll = function(filename, callback) { 1 | callback(); // default behaviour is not to do anything | }; | debug.js: [ hits: 8, misses: 0, sloc: 8, coverage: 100.00% ] 14 | "use strict"; | 14 | module.exports = function(label) { 18 | var debug; | 18 | if (process.env.NODE_DEBUG && /\blog4js\b/.test(process.env.NODE_DEBUG)) { 1 | debug = function(message) { 1 | console.error('LOG4JS: (%s) %s', label, message); | }; | } else { 17 | debug = function() { }; | } | 18 | return debug; | }; streams/index.js: [ hits: 2, misses: 0, sloc: 2, coverage: 100.00% ] 2 | exports.RollingFileStream = require('./RollingFileStream'); 2 | exports.DateRollingFileStream = require('./DateRollingFileStream'); streams/RollingFileStream.js: [ hits: 41, misses: 0, sloc: 41, coverage: 100.00% ] 2 | "use strict"; 2 | var BaseRollingFileStream = require('./BaseRollingFileStream') | , debug = require('../debug')('RollingFileStream') | , util = require('util') | , path = require('path') | , fs = require('fs') | , async = require('async'); | 2 | module.exports = RollingFileStream; | 2 | function RollingFileStream (filename, size, backups, options) { 9 | this.size = size; 9 | this.backups = backups || 1; | 9 | function throwErrorIfArgumentsAreNotValid() { 9 | if (!filename || !size || size <= 0) { 1 | throw new Error("You must specify a filename and file size"); | } | } | 9 | throwErrorIfArgumentsAreNotValid(); | 8 | RollingFileStream.super_.call(this, filename, options); | } 2 | util.inherits(RollingFileStream, BaseRollingFileStream); | 2 | RollingFileStream.prototype.shouldRoll = function() { 22 | debug("should roll with current size %d, and max size %d", this.currentSize, this.size); 22 | return this.currentSize >= this.size; | }; | 2 | RollingFileStream.prototype.roll = function(filename, callback) { 6 | var that = this, | nameMatcher = new RegExp('^' + path.basename(filename)); | 6 | function justTheseFiles (item) { 276 | return nameMatcher.test(item); | } | 6 | function index(filename_) { 109 | return parseInt(filename_.substring((path.basename(filename) + '.').length), 10) || 0; | } | 6 | function byIndex(a, b) { 26 | if (index(a) > index(b)) { 7 | return 1; 19 | } else if (index(a) < index(b) ) { 17 | return -1; | } else { 2 | return 0; | } | } | 6 | function increaseFileIndex (fileToRename, cb) { 19 | var idx = index(fileToRename); 19 | debug('Index of ' + fileToRename + ' is ' + idx); 19 | if (idx < that.backups) { | //on windows, you can get a EEXIST error if you rename a file to an existing file | //so, we'll try to delete the file we're renaming to first 15 | fs.unlink(filename + '.' + (idx+1), function (err) { | //ignore err: if we could not delete, it's most likely that it doesn't exist 15 | debug('Renaming ' + fileToRename + ' -> ' + filename + '.' + (idx+1)); 15 | fs.rename(path.join(path.dirname(filename), fileToRename), filename + '.' + (idx + 1), cb); | }); | } else { 4 | cb(); | } | } | 6 | function renameTheFiles(cb) { | //roll the backups (rename file.n to file.n+1, where n <= numBackups) 6 | debug("Renaming the old files"); 6 | fs.readdir(path.dirname(filename), function (err, files) { 6 | async.forEachSeries( | files.filter(justTheseFiles).sort(byIndex).reverse(), | increaseFileIndex, | cb | ); | }); | } | 6 | debug("Rolling, rolling, rolling"); 6 | async.series([ | this.closeTheStream.bind(this), | renameTheFiles, | this.openTheStream.bind(this) | ], callback); | | }; streams/DateRollingFileStream.js: [ hits: 44, misses: 0, sloc: 44, coverage: 100.00% ] 2 | "use strict"; 2 | var BaseRollingFileStream = require('./BaseRollingFileStream') | , debug = require('../debug')('DateRollingFileStream') | , format = require('../date_format') | , async = require('async') | , fs = require('fs') | , util = require('util'); | 2 | module.exports = DateRollingFileStream; | 2 | function DateRollingFileStream(filename, pattern, options, now) { 14 | debug("Now is " + now); 14 | if (pattern && typeof(pattern) === 'object') { 1 | now = options; 1 | options = pattern; 1 | pattern = null; | } 14 | this.pattern = pattern || '.yyyy-MM-dd'; 14 | this.now = now || Date.now; 14 | this.lastTimeWeWroteSomething = format.asString(this.pattern, new Date(this.now())); 14 | this.baseFilename = filename; 14 | this.alwaysIncludePattern = false; | 14 | if (options) { 11 | if (options.alwaysIncludePattern) { 2 | this.alwaysIncludePattern = true; 2 | filename = this.baseFilename + this.lastTimeWeWroteSomething; | } 11 | delete options.alwaysIncludePattern; 11 | if (Object.keys(options).length === 0) { 9 | options = null; | } | } 14 | debug("this.now is " + this.now + ", now is " + now); | 14 | DateRollingFileStream.super_.call(this, filename, options); | } 2 | util.inherits(DateRollingFileStream, BaseRollingFileStream); | 2 | DateRollingFileStream.prototype.shouldRoll = function() { 7 | var lastTime = this.lastTimeWeWroteSomething, | thisTime = format.asString(this.pattern, new Date(this.now())); | 7 | debug("DateRollingFileStream.shouldRoll with now = " + | this.now() + ", thisTime = " + thisTime + ", lastTime = " + lastTime); | 7 | this.lastTimeWeWroteSomething = thisTime; 7 | this.previousTime = lastTime; | 7 | return thisTime !== lastTime; | }; | 2 | DateRollingFileStream.prototype.roll = function(filename, callback) { 2 | var that = this; | 2 | debug("Starting roll"); | 2 | if (this.alwaysIncludePattern) { 1 | this.filename = this.baseFilename + this.lastTimeWeWroteSomething; 1 | async.series([ | this.closeTheStream.bind(this), | this.openTheStream.bind(this) | ], callback); | } else { 1 | var newFilename = this.baseFilename + this.previousTime; 1 | async.series([ | this.closeTheStream.bind(this), | deleteAnyExistingFile, | renameTheCurrentFile, | this.openTheStream.bind(this) | ], callback); | } | 2 | function deleteAnyExistingFile(cb) { | //on windows, you can get a EEXIST error if you rename a file to an existing file | //so, we'll try to delete the file we're renaming to first 1 | fs.unlink(newFilename, function (err) { | //ignore err: if we could not delete, it's most likely that it doesn't exist 1 | cb(); | }); | } | 2 | function renameTheCurrentFile(cb) { 1 | debug("Renaming the " + filename + " -> " + newFilename); 1 | fs.rename(filename, newFilename, cb); | } | | }; appenders/dateFile.js: [ hits: 22, misses: 0, sloc: 22, coverage: 100.00% ] 3 | "use strict"; 3 | var streams = require('../streams') | , layouts = require('../layouts') | , path = require('path') | , os = require('os') | , eol = os.EOL || '\n' | , openFiles = []; | | //close open files on process exit. 3 | process.on('exit', function() { 1 | openFiles.forEach(function (file) { 5 | file.end(); | }); | }); | | /** | * File appender that rolls files according to a date pattern. | * @filename base filename. | * @pattern the format that will be added to the end of filename when rolling, | * also used to check when to roll files - defaults to '.yyyy-MM-dd' | * @layout layout function for log messages - defaults to basicLayout | */ 3 | function appender(filename, pattern, alwaysIncludePattern, layout) { 14 | layout = layout || layouts.basicLayout; | 14 | var logFile = new streams.DateRollingFileStream( | filename, | pattern, | { alwaysIncludePattern: alwaysIncludePattern } | ); 14 | openFiles.push(logFile); | 14 | return function(logEvent) { 3 | logFile.write(layout(logEvent) + eol, "utf8"); | }; | | } | 3 | function configure(config, options) { 3 | var layout; | 3 | if (config.layout) { 2 | layout = layouts.layout(config.layout.type, config.layout); | } | 3 | if (!config.alwaysIncludePattern) { 2 | config.alwaysIncludePattern = false; | } | 3 | if (options && options.cwd && !config.absolute) { 1 | config.filename = path.join(options.cwd, config.filename); | } | 3 | return appender(config.filename, config.pattern, config.alwaysIncludePattern, layout); | } | 3 | exports.appender = appender; 3 | exports.configure = configure; appenders/file.js: [ hits: 32, misses: 0, sloc: 32, coverage: 100.00% ] 4 | "use strict"; 4 | var layouts = require('../layouts') | , path = require('path') | , fs = require('fs') | , streams = require('../streams') | , os = require('os') | , eol = os.EOL || '\n' | , openFiles = []; | | //close open files on process exit. 4 | process.on('exit', function() { 1 | openFiles.forEach(function (file) { 5 | file.end(); | }); | }); | | /** | * File Appender writing the logs to a text file. Supports rolling of logs by size. | * | * @param file file log messages will be written to | * @param layout a function that takes a logevent and returns a string | * (defaults to basicLayout). | * @param logSize - the maximum size (in bytes) for a log file, | * if not provided then logs won't be rotated. | * @param numBackups - the number of log files to keep after logSize | * has been reached (default 5) | */ 4 | function fileAppender (file, layout, logSize, numBackups) { 18 | var bytesWritten = 0; 18 | file = path.normalize(file); 18 | layout = layout || layouts.basicLayout; 18 | numBackups = numBackups === undefined ? 5 : numBackups; | //there has to be at least one backup if logSize has been specified 18 | numBackups = numBackups === 0 ? 1 : numBackups; | 18 | function openTheStream(file, fileSize, numFiles) { 18 | var stream; 18 | if (fileSize) { 9 | stream = new streams.RollingFileStream( | file, | fileSize, | numFiles | ); | } else { 9 | stream = fs.createWriteStream( | file, | { encoding: "utf8", | mode: parseInt('0644', 8), | flags: 'a' } | ); | } 18 | stream.on("error", function (err) { 1 | console.error("log4js.fileAppender - Writing to file %s, error happened ", file, err); | }); 18 | return stream; | } | 18 | var logFile = openTheStream(file, logSize, numBackups); | | // push file to the stack of open handlers 18 | openFiles.push(logFile); | 18 | return function(loggingEvent) { 15 | logFile.write(layout(loggingEvent) + eol, "utf8"); | }; | } | 4 | function configure(config, options) { 4 | var layout; 4 | if (config.layout) { 3 | layout = layouts.layout(config.layout.type, config.layout); | } | 4 | if (options && options.cwd && !config.absolute) { 1 | config.filename = path.join(options.cwd, config.filename); | } | 4 | return fileAppender(config.filename, layout, config.maxLogSize, config.backups); | } | 4 | exports.appender = fileAppender; 4 | exports.configure = configure; appenders/gelf.js: [ hits: 83, misses: 0, sloc: 83, coverage: 100.00% ] 7 | "use strict"; 7 | var zlib = require('zlib'); 7 | var layouts = require('../layouts'); 7 | var levels = require('../levels'); 7 | var dgram = require('dgram'); 7 | var util = require('util'); 7 | var debug = require('../debug')('GELF Appender'); | 7 | var LOG_EMERG=0; // system is unusable 7 | var LOG_ALERT=1; // action must be taken immediately 7 | var LOG_CRIT=2; // critical conditions 7 | var LOG_ERR=3; // error conditions 7 | var LOG_ERROR=3; // because people WILL typo 7 | var LOG_WARNING=4; // warning conditions 7 | var LOG_NOTICE=5; // normal, but significant, condition 7 | var LOG_INFO=6; // informational message 7 | var LOG_DEBUG=7; // debug-level message | 7 | var levelMapping = {}; 7 | levelMapping[levels.ALL] = LOG_DEBUG; 7 | levelMapping[levels.TRACE] = LOG_DEBUG; 7 | levelMapping[levels.DEBUG] = LOG_DEBUG; 7 | levelMapping[levels.INFO] = LOG_INFO; 7 | levelMapping[levels.WARN] = LOG_WARNING; 7 | levelMapping[levels.ERROR] = LOG_ERR; 7 | levelMapping[levels.FATAL] = LOG_CRIT; | | /** | * GELF appender that supports sending UDP packets to a GELF compatible server such as Graylog | * | * @param layout a function that takes a logevent and returns a string (defaults to none). | * @param host - host to which to send logs (default:localhost) | * @param port - port at which to send logs to (default:12201) | * @param hostname - hostname of the current host (default:os hostname) | * @param facility - facility to log to (default:nodejs-server) | */ 7 | function gelfAppender (layout, host, port, hostname, facility) { 7 | var config, customFields; 7 | if (typeof(host) === 'object') { 7 | config = host; 7 | host = config.host; 7 | port = config.port; 7 | hostname = config.hostname; 7 | facility = config.facility; 7 | customFields = config.customFields; | } | 7 | host = host || 'localhost'; 7 | port = port || 12201; 7 | hostname = hostname || require('os').hostname(); 7 | facility = facility || 'nodejs-server'; 7 | layout = layout || layouts.messagePassThroughLayout; | 7 | var defaultCustomFields = customFields || {}; | 7 | var client = dgram.createSocket("udp4"); | 7 | process.on('exit', function() { 2 | if (client) client.close(); | }); | | /** | * Add custom fields (start with underscore ) | * - if the first object passed to the logger contains 'GELF' field, | * copy the underscore fields to the message | * @param loggingEvent | * @param msg | */ 7 | function addCustomFields(loggingEvent, msg){ | | /* append defaultCustomFields firsts */ 5 | Object.keys(defaultCustomFields).forEach(function(key) { | // skip _id field for graylog2, skip keys not starts with UNDERSCORE 2 | if (key.match(/^_/) && key !== "_id") { 2 | msg[key] = defaultCustomFields[key]; | } | }); | | /* append custom fields per message */ 5 | var data = loggingEvent.data; 5 | if (!Array.isArray(data) || data.length === 0) return; 5 | var firstData = data[0]; | 9 | if (!firstData.GELF) return; // identify with GELF field defined 1 | Object.keys(firstData).forEach(function(key) { | // skip _id field for graylog2, skip keys not starts with UNDERSCORE 3 | if (key.match(/^_/) || key !== "_id") { 3 | msg[key] = firstData[key]; | } | }); | | /* the custom field object should be removed, so it will not be looged by the later appenders */ 1 | loggingEvent.data.shift(); | } | 7 | function preparePacket(loggingEvent) { 5 | var msg = {}; 5 | addCustomFields(loggingEvent, msg); 5 | msg.full_message = layout(loggingEvent); 5 | msg.short_message = msg.full_message; | 5 | msg.version="1.0"; 5 | msg.timestamp = msg.timestamp || new Date().getTime() / 1000 >> 0; 5 | msg.host = hostname; 5 | msg.level = levelMapping[loggingEvent.level || levels.DEBUG]; 5 | msg.facility = facility; 5 | return msg; | } | 7 | function sendPacket(packet) { 3 | try { 3 | client.send(packet, 0, packet.length, port, host); | } catch(e) {} | } | 7 | return function(loggingEvent) { 5 | var message = preparePacket(loggingEvent); 5 | zlib.gzip(new Buffer(JSON.stringify(message)), function(err, packet) { 5 | if (err) { 1 | console.error(err.stack); | } else { 4 | if (packet.length > 8192) { 1 | debug("Message packet length (" + packet.length + ") is larger than 8k. Not sending"); | } else { 3 | sendPacket(packet); | } | } | }); | }; | } | 7 | function configure(config) { 7 | var layout; 7 | if (config.layout) { 1 | layout = layouts.layout(config.layout.type, config.layout); | } 7 | return gelfAppender(layout, config); | } | 7 | exports.appender = gelfAppender; 7 | exports.configure = configure; appenders/hookio.js: [ hits: 46, misses: 0, sloc: 46, coverage: 100.00% ] 4 | "use strict"; 4 | var log4js = require('../log4js') | , layouts = require('../layouts') | , Hook = require('hook.io').Hook | , util = require('util'); | 4 | var Logger = function createLogger(options) { 1 | var self = this; 1 | var actualAppender = options.actualAppender; 1 | Hook.call(self, options); 1 | self.on('hook::ready', function hookReady() { 1 | self.on('*::' + options.name + '::log', function log(loggingEvent) { 2 | deserializeLoggingEvent(loggingEvent); 2 | actualAppender(loggingEvent); | }); | }); | }; 4 | util.inherits(Logger, Hook); | 4 | function deserializeLoggingEvent(loggingEvent) { 2 | loggingEvent.startTime = new Date(loggingEvent.startTime); 2 | loggingEvent.level.toString = function levelToString() { 2 | return loggingEvent.level.levelStr; | }; | } | 4 | function initHook(hookioOptions) { 4 | var loggerHook; 4 | if (hookioOptions.mode === 'master') { | // Start the master hook, handling the actual logging 1 | loggerHook = new Logger(hookioOptions); | } else { | // Start a worker, just emitting events for a master 3 | loggerHook = new Hook(hookioOptions); | } 4 | loggerHook.start(); 4 | return loggerHook; | } | 4 | function getBufferedHook(hook, eventName) { 4 | var hookBuffer = []; 4 | var hookReady = false; 4 | hook.on('hook::ready', function emptyBuffer() { 3 | hookBuffer.forEach(function logBufferItem(loggingEvent) { 1 | hook.emit(eventName, loggingEvent); | }); 3 | hookReady = true; | }); | 4 | return function log(loggingEvent) { 6 | if (hookReady) { 4 | hook.emit(eventName, loggingEvent); | } else { 2 | hookBuffer.push(loggingEvent); | } | }; | } | 4 | function createAppender(hookioOptions) { 4 | var loggerHook = initHook(hookioOptions); 4 | var loggerEvent = hookioOptions.name + '::log'; 4 | return getBufferedHook(loggerHook, loggerEvent); | } | 4 | function configure(config) { 4 | var actualAppender; 4 | if (config.appender && config.mode === 'master') { 1 | log4js.loadAppender(config.appender.type); 1 | actualAppender = log4js.appenderMakers[config.appender.type](config.appender); 1 | config.actualAppender = actualAppender; | } 4 | return createAppender(config); | } | 4 | exports.appender = createAppender; 4 | exports.configure = configure; appenders/logLevelFilter.js: [ hits: 13, misses: 0, sloc: 13, coverage: 100.00% ] 1 | "use strict"; 1 | var levels = require('../levels') | , log4js = require('../log4js'); | 1 | function logLevelFilter (levelString, appender) { 2 | var level = levels.toLevel(levelString); 2 | return function(logEvent) { 8 | if (logEvent.level.isGreaterThanOrEqualTo(level)) { 4 | appender(logEvent); | } | }; | } | 1 | function configure(config) { 1 | log4js.loadAppender(config.appender.type); 1 | var appender = log4js.appenderMakers[config.appender.type](config.appender); 1 | return logLevelFilter(config.level, appender); | } | 1 | exports.appender = logLevelFilter; 1 | exports.configure = configure; appenders/multiprocess.js: [ hits: 65, misses: 0, sloc: 65, coverage: 100.00% ] 6 | "use strict"; 6 | var log4js = require('../log4js') | , net = require('net') | , END_MSG = '__LOG4JS__'; | | /** | * Creates a server, listening on config.loggerPort, config.loggerHost. | * Output goes to config.actualAppender (config.appender is used to | * set up that appender). | */ 6 | function logServer(config) { | | /** | * Takes a utf-8 string, returns an object with | * the correct log properties. | */ 3 | function deserializeLoggingEvent(clientSocket, msg) { 7 | var loggingEvent; 7 | try { 7 | loggingEvent = JSON.parse(msg); 6 | loggingEvent.startTime = new Date(loggingEvent.startTime); 6 | loggingEvent.level = log4js.levels.toLevel(loggingEvent.level.levelStr); | } catch (e) { | // JSON.parse failed, just log the contents probably a naughty. 1 | loggingEvent = { | startTime: new Date(), | categoryName: 'log4js', | level: log4js.levels.ERROR, | data: [ 'Unable to parse log:', msg ] | }; | } | 7 | loggingEvent.remoteAddress = clientSocket.remoteAddress; 7 | loggingEvent.remotePort = clientSocket.remotePort; | 7 | return loggingEvent; | } | 3 | var actualAppender = config.actualAppender, | server = net.createServer(function serverCreated(clientSocket) { 3 | clientSocket.setEncoding('utf8'); 3 | var logMessage = ''; | 3 | function logTheMessage(msg) { 7 | if (logMessage.length > 0) { 7 | actualAppender(deserializeLoggingEvent(clientSocket, msg)); | } | } | 3 | function chunkReceived(chunk) { 13 | var event; 13 | logMessage += chunk || ''; 13 | if (logMessage.indexOf(END_MSG) > -1) { 7 | event = logMessage.substring(0, logMessage.indexOf(END_MSG)); 7 | logTheMessage(event); 7 | logMessage = logMessage.substring(event.length + END_MSG.length) || ''; | //check for more, maybe it was a big chunk 7 | chunkReceived(); | } | } | 3 | clientSocket.on('data', chunkReceived); 3 | clientSocket.on('end', chunkReceived); | }); | 3 | server.listen(config.loggerPort || 5000, config.loggerHost || 'localhost'); | 3 | return actualAppender; | } | 6 | function workerAppender(config) { 3 | var canWrite = false, | buffer = [], | socket; | 3 | createSocket(); | 3 | function createSocket() { 5 | socket = net.createConnection(config.loggerPort || 5000, config.loggerHost || 'localhost'); 5 | socket.on('connect', function() { 4 | emptyBuffer(); 4 | canWrite = true; | }); 5 | socket.on('timeout', socket.end.bind(socket)); | //don't bother listening for 'error', 'close' gets called after that anyway 5 | socket.on('close', createSocket); | } | 3 | function emptyBuffer() { 4 | var evt; 4 | while ((evt = buffer.shift())) { 2 | write(evt); | } | } | 3 | function write(loggingEvent) { 9 | socket.write(JSON.stringify(loggingEvent), 'utf8'); 9 | socket.write(END_MSG, 'utf8'); | } | 3 | return function log(loggingEvent) { 9 | if (canWrite) { 7 | write(loggingEvent); | } else { 2 | buffer.push(loggingEvent); | } | }; | } | 6 | function createAppender(config) { 6 | if (config.mode === 'master') { 3 | return logServer(config); | } else { 3 | return workerAppender(config); | } | } | 6 | function configure(config, options) { 1 | var actualAppender; 1 | if (config.appender && config.mode === 'master') { 1 | log4js.loadAppender(config.appender.type); 1 | actualAppender = log4js.appenderMakers[config.appender.type](config.appender, options); 1 | config.actualAppender = actualAppender; | } 1 | return createAppender(config); | } | 6 | exports.appender = createAppender; 6 | exports.configure = configure; appenders/smtp.js: [ hits: 40, misses: 0, sloc: 40, coverage: 100.00% ] 6 | "use strict"; 6 | var layouts = require("../layouts") | , mailer = require("nodemailer") | , os = require('os'); | | /** | * SMTP Appender. Sends logging events using SMTP protocol. | * It can either send an email on each event or group several | * logging events gathered during specified interval. | * | * @param config appender configuration data | * config.sendInterval time between log emails (in seconds), if 0 | * then every event sends an email | * @param layout a function that takes a logevent and returns a string (defaults to basicLayout). | */ 6 | function smtpAppender(config, layout) { 6 | layout = layout || layouts.basicLayout; 6 | var subjectLayout = layouts.messagePassThroughLayout; 6 | var sendInterval = config.sendInterval*1000 || 0; | 6 | var logEventBuffer = []; 6 | var sendTimer; | 6 | function sendBuffer() { 8 | if (logEventBuffer.length > 0) { | 8 | var transport = mailer.createTransport(config.transport, config[config.transport]); 8 | var firstEvent = logEventBuffer[0]; 8 | var body = ""; 8 | while (logEventBuffer.length > 0) { 9 | body += layout(logEventBuffer.shift()) + "\n"; | } | 8 | var msg = { | to: config.recipients, | subject: config.subject || subjectLayout(firstEvent), | text: body, | headers: { "Hostname": os.hostname() } | }; 8 | if (config.sender) { 1 | msg.from = config.sender; | } 8 | transport.sendMail(msg, function(error, success) { 8 | if (error) { 1 | console.error("log4js.smtpAppender - Error happened", error); | } 8 | transport.close(); | }); | } | } | 6 | function scheduleSend() { 3 | if (!sendTimer) { 2 | sendTimer = setTimeout(function() { 2 | sendTimer = null; 2 | sendBuffer(); | }, sendInterval); | } | } | 6 | return function(loggingEvent) { 9 | logEventBuffer.push(loggingEvent); 9 | if (sendInterval > 0) { 3 | scheduleSend(); | } else { 6 | sendBuffer(); | } | }; | } | 6 | function configure(config) { 6 | var layout; 6 | if (config.layout) { 1 | layout = layouts.layout(config.layout.type, config.layout); | } 6 | return smtpAppender(config, layout); | } | 6 | exports.name = "smtp"; 6 | exports.appender = smtpAppender; 6 | exports.configure = configure; |