--- /dev/null
+"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, timezoneOffest) {
+ var output = colorize(
+ formatLogData(
+ '[%s] [%s] %s - '
+ , dateFormat.asString(loggingEvent.startTime, timezoneOffest)
+ , loggingEvent.level
+ , loggingEvent.categoryName
+ )
+ , colour
+ );
+ 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
+ */
+function basicLayout (loggingEvent, timezoneOffset) {
+ return timestampLevelAndCategory(loggingEvent, undefined, timezoneOffset) + formatLogData(loggingEvent.data);
+}
+
+/**
+ * colouredLayout - taken from masylum's fork.
+ * same as basicLayout, but with colours.
+ */
+function colouredLayout (loggingEvent, timezoneOffset) {
+ return timestampLevelAndCategory(
+ loggingEvent,
+ colours[loggingEvent.level.toString()],
+ timezoneOffset
+ ) + 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{<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
+ * @param {number} timezone offset in minutes
+ * @return {Function}
+ * @author Stephan Strittmatter
+ * @author Jan Schmidle
+ */
+function patternLayout (pattern, tokens, timezoneOffset) {
+ 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, timezoneOffset);
+ }
+
+ 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 dateFormat.asString('hh:mm:ss', loggingEvent.startTime, timezoneOffset);
+ }
+
+ 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,
+ addLayout: function(name, serializerGenerator) {
+ layoutMakers[name] = serializerGenerator;
+ },
+ layout: function(name, config) {
+ return layoutMakers[name] && layoutMakers[name](config);
+ }
+};