"use strict"; var dateFormat = require('./date_format') , os = require('os') , eol = os.EOL || '\n' , util = require('util') , replacementRegExp = /%[sdj]/g , layoutMakers = { "messagePassThrough": function() { return messagePassThroughLayout; }, "basic": function() { return basicLayout; }, "colored": function() { return colouredLayout; }, "coloured": function() { return colouredLayout; }, "pattern": function (config) { 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" }; function wrapErrorsWithInspect(items) { return items.map(function(item) { if ((item instanceof Error) && item.stack) { return { inspect: function() { return util.format(item) + '\n' + item.stack; } }; } else { return item; } }); } function formatLogData(logData) { var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments); return util.format.apply(util, wrapErrorsWithInspect(data)); } 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] }; function colorizeStart(style) { return style ? '\x1B[' + styles[style][0] + 'm' : ''; } function colorizeEnd(style) { return style ? '\x1B[' + styles[style][1] + 'm' : ''; } /** * Taken from masylum's fork (https://github.com/masylum/log4js-node) */ function colorize (str, style) { return colorizeStart(style) + str + colorizeEnd(style); } function timestampLevelAndCategory(loggingEvent, colour) { var output = colorize( formatLogData( '[%s] [%s] %s - ' , dateFormat.asString(loggingEvent.startTime) , loggingEvent.level , loggingEvent.categoryName ) , colour ); return output; } /** * BasicLayout is a simple layout for storing the logs. The logs are stored * in following format: *
 * [startTime] [logLevel] categoryName - message\n
 * 
* * @author Stephan Strittmatter */ function basicLayout (loggingEvent) { return timestampLevelAndCategory(loggingEvent) + formatLogData(loggingEvent.data); } /** * colouredLayout - taken from masylum's fork. * same as basicLayout, but with colours. */ function colouredLayout (loggingEvent) { return timestampLevelAndCategory( loggingEvent, colours[loggingEvent.level.toString()] ) + formatLogData(loggingEvent.data); } function messagePassThroughLayout (loggingEvent) { 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 * - %h hostname * - %m log data * - %d date in various formats * - %% % * - %n newline * - %z pid * - %x{} 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 */ function patternLayout (pattern, tokens) { var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n"; var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdhmnprzxy%])(\{([^\}]+)\})?|([^%]+)/; pattern = pattern || TTCC_CONVERSION_PATTERN; function categoryName(loggingEvent, specifier) { var loggerName = loggingEvent.categoryName; if (specifier) { var precision = parseInt(specifier, 10); var loggerNameBits = loggerName.split("."); if (precision < loggerNameBits.length) { loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join("."); } } return loggerName; } function formatAsDate(loggingEvent, specifier) { var format = dateFormat.ISO8601_FORMAT; if (specifier) { format = specifier; // Pick up special cases if (format == "ISO8601") { format = dateFormat.ISO8601_FORMAT; } else if (format == "ISO8601_WITH_TZ_OFFSET") { format = dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT; } else if (format == "ABSOLUTE") { format = dateFormat.ABSOLUTETIME_FORMAT; } else if (format == "DATE") { format = dateFormat.DATETIME_FORMAT; } } // Format the date return dateFormat.asString(format, loggingEvent.startTime); } function hostname() { return os.hostname().toString(); } function formatMessage(loggingEvent) { return formatLogData(loggingEvent.data); } function endOfLine() { return eol; } function logLevel(loggingEvent) { return loggingEvent.level.toString(); } function startTime(loggingEvent) { return "" + loggingEvent.startTime.toLocaleTimeString(); } function startColour(loggingEvent) { return colorizeStart(colours[loggingEvent.level.toString()]); } function endColour(loggingEvent) { return colorizeEnd(colours[loggingEvent.level.toString()]); } function percent() { return '%'; } function pid(loggingEvent) { if (loggingEvent && loggingEvent.pid) { return loggingEvent.pid; } else { return process.pid; } } function clusterInfo(loggingEvent, specifier) { if (loggingEvent.cluster && specifier) { return specifier .replace('%m', loggingEvent.cluster.master) .replace('%w', loggingEvent.cluster.worker) .replace('%i', loggingEvent.cluster.workerId); } else if (loggingEvent.cluster) { return loggingEvent.cluster.worker+'@'+loggingEvent.cluster.master; } else { return pid(); } } function userDefined(loggingEvent, specifier) { if (typeof(tokens[specifier]) !== 'undefined') { if (typeof(tokens[specifier]) === 'function') { return tokens[specifier](loggingEvent); } else { return tokens[specifier]; } } return null; } var replacers = { 'c': categoryName, 'd': formatAsDate, 'h': hostname, 'm': formatMessage, 'n': endOfLine, 'p': logLevel, 'r': startTime, '[': startColour, ']': endColour, 'y': clusterInfo, 'z': pid, '%': percent, 'x': userDefined }; function replaceToken(conversionCharacter, loggingEvent, specifier) { return replacers[conversionCharacter](loggingEvent, specifier); } function truncate(truncation, toTruncate) { var len; if (truncation) { len = parseInt(truncation.substr(1), 10); return toTruncate.substring(0, len); } return toTruncate; } function pad(padding, toPad) { var len; if (padding) { if (padding.charAt(0) == "-") { len = parseInt(padding.substr(1), 10); // Right pad with spaces while (toPad.length < len) { toPad += " "; } } else { len = parseInt(padding, 10); // Left pad with spaces while (toPad.length < len) { toPad = " " + toPad; } } } return toPad; } return function(loggingEvent) { var formattedString = ""; var result; var searchString = pattern; while ((result = regex.exec(searchString))) { var matchedString = result[0]; var padding = result[1]; var truncation = result[2]; var conversionCharacter = result[3]; var specifier = result[5]; var text = result[6]; // Check if the pattern matched was just normal text if (text) { formattedString += "" + text; } else { // Create a raw replacement string based on the conversion // character and specifier var replacement = replaceToken(conversionCharacter, loggingEvent, specifier); // Format the replacement according to any padding or // truncation specified replacement = truncate(truncation, replacement); replacement = pad(padding, replacement); formattedString += replacement; } searchString = searchString.substr(result.index + result[0].length); } return formattedString; }; } module.exports = { basicLayout: basicLayout, messagePassThroughLayout: messagePassThroughLayout, patternLayout: patternLayout, colouredLayout: colouredLayout, coloredLayout: colouredLayout, layout: function(name, config) { return layoutMakers[name] && layoutMakers[name](config); } };