From f89d54b66e64b42d08f57e3c99dc37885157d895 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Tue, 12 Jul 2011 13:03:17 +1000 Subject: [PATCH] removed main function, now using felixge's sandboxed-module, split code into multiple files --- lib/layouts.js | 222 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/levels.js | 57 +++++++++++++ test/layouts.js | 90 ++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 lib/layouts.js create mode 100644 lib/levels.js create mode 100644 test/layouts.js diff --git a/lib/layouts.js b/lib/layouts.js new file mode 100644 index 0000000..c535556 --- /dev/null +++ b/lib/layouts.js @@ -0,0 +1,222 @@ +var dateFormat = require('./date_format') +, layoutMakers = { + "messagePassThrough": function() { return messagePassThroughLayout; }, + "basic": function() { return basicLayout; }, + "colored": function() { return colouredLayout; }, + "coloured": function() { return colouredLayout; }, + "pattern": function (config) { + var pattern = config.pattern || undefined; + return patternLayout(pattern); + } +}; + +/** + * 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) { + var timestampLevelAndCategory = '[' + dateFormat.asString(loggingEvent.startTime) + '] '; + timestampLevelAndCategory += '[' + loggingEvent.level.toString() + '] '; + timestampLevelAndCategory += loggingEvent.categoryName + ' - '; + + var output = timestampLevelAndCategory + loggingEvent.message; + + if (loggingEvent.exception) { + output += '\n' + output += timestampLevelAndCategory; + if (loggingEvent.exception.stack) { + output += loggingEvent.exception.stack; + } else { + output += loggingEvent.exception.name + ': '+loggingEvent.exception.message; + } + } + return output; +} + +/** + * Taken from masylum's fork (https://github.com/masylum/log4js-node) + */ +function colorize (str, style) { + 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] + }; + return '\033[' + styles[style][0] + 'm' + str + + '\033[' + styles[style][1] + 'm'; +} + +/** + * colouredLayout - taken from masylum's fork. + * same as basicLayout, but with colours. + */ +function colouredLayout (loggingEvent) { + var timestampLevelAndCategory = colorize('[' + dateFormat.asString(loggingEvent.startTime) + '] ', 'grey'); + timestampLevelAndCategory += colorize( + '[' + loggingEvent.level.toString() + '] ', loggingEvent.level.colour + ); + timestampLevelAndCategory += colorize(loggingEvent.categoryName + ' - ', 'grey'); + + var output = timestampLevelAndCategory + loggingEvent.message; + + if (loggingEvent.exception) { + output += '\n' + output += timestampLevelAndCategory; + if (loggingEvent.exception.stack) { + output += loggingEvent.exception.stack; + } else { + output += loggingEvent.exception.name + ': '+loggingEvent.exception.message; + } + } + return output; +} + +function messagePassThroughLayout (loggingEvent) { + return loggingEvent.message; +} + +/** + * PatternLayout + * Takes a pattern string and returns a layout function. + * @author Stephan Strittmatter + */ +function patternLayout (pattern) { + var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n"; + var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([cdmnpr%])(\{([^\}]+)\})?|([^%]+)/; + + pattern = pattern || patternLayout.TTCC_CONVERSION_PATTERN; + + return function(loggingEvent) { + var formattedString = ""; + var result; + var searchString = this.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 = ""; + switch(conversionCharacter) { + case "c": + var loggerName = loggingEvent.categoryName; + if (specifier) { + var precision = parseInt(specifier, 10); + var loggerNameBits = loggingEvent.categoryName.split("."); + if (precision >= loggerNameBits.length) { + replacement = loggerName; + } else { + replacement = loggerNameBits.slice(loggerNameBits.length - precision).join("."); + } + } else { + replacement = loggerName; + } + break; + case "d": + var format = dateFormat.ISO8601_FORMAT; + if (specifier) { + format = specifier; + // Pick up special cases + if (format == "ISO8601") { + format = dateFormat.ISO8601_FORMAT; + } else if (format == "ABSOLUTE") { + format = dateFormat.ABSOLUTETIME_FORMAT; + } else if (format == "DATE") { + format = dateFormat.DATETIME_FORMAT; + } + } + // Format the date + replacement = dateFormat.asString(format, loggingEvent.startTime); + break; + case "m": + replacement = loggingEvent.message; + break; + case "n": + replacement = "\n"; + break; + case "p": + replacement = loggingEvent.level.toString(); + break; + case "r": + replacement = "" + loggingEvent.startTime.toLocaleTimeString(); + break; + case "%": + replacement = "%"; + break; + default: + replacement = matchedString; + break; + } + // Format the replacement according to any padding or + // truncation specified + + var len; + + // First, truncation + if (truncation) { + len = parseInt(truncation.substr(1), 10); + replacement = replacement.substring(0, len); + } + // Next, padding + if (padding) { + if (padding.charAt(0) == "-") { + len = parseInt(padding.substr(1), 10); + // Right pad with spaces + while (replacement.length < len) { + replacement += " "; + } + } else { + len = parseInt(padding, 10); + // Left pad with spaces + while (replacement.length < len) { + replacement = " " + 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); + } +}; \ No newline at end of file diff --git a/lib/levels.js b/lib/levels.js new file mode 100644 index 0000000..ea21de1 --- /dev/null +++ b/lib/levels.js @@ -0,0 +1,57 @@ +function Level(level, levelStr, colour) { + this.level = level; + this.levelStr = levelStr; + this.colour = colour; +} + +/** + * converts given String to corresponding Level + * @param {String} sArg String value of Level + * @param {Log4js.Level} defaultLevel default Level, if no String representation + * @return Level object + * @type Log4js.Level + */ +function toLevel(sArg, defaultLevel) { + + if (sArg === null) { + return defaultLevel; + } + + if (typeof sArg == "string") { + var s = sArg.toUpperCase(); + if (module.exports[s]) { + return module.exports[s]; + } + } + return defaultLevel; +}; + +Level.prototype.toString = function() { + return this.levelStr; +}; + +Level.prototype.isLessThanOrEqualTo = function(otherLevel) { + if (typeof otherLevel === "string") { + otherLevel = Level.toLevel(otherLevel); + } + return this.level <= otherLevel.level; +}; + +Level.prototype.isGreaterThanOrEqualTo = function(otherLevel) { + if (typeof otherLevel === "string") { + otherLevel = Level.toLevel(otherLevel); + } + return this.level >= otherLevel.level; +}; + +module.exports = { + ALL: new Level(Number.MIN_VALUE, "ALL", "grey") + , TRACE: new Level(5000, "TRACE", "blue") + , DEBUG: new Level(10000, "DEBUG", "cyan") + , INFO: new Level(20000, "INFO", "green") + , WARN: new Level(30000, "WARN", "yellow") + , ERROR: new Level(40000, "ERROR", "red") + , FATAL: new Level(50000, "FATAL", "magenta") + , OFF: new Level(Number.MAX_VALUE, "OFF", "grey") + , toLevel: toLevel +}; diff --git a/test/layouts.js b/test/layouts.js new file mode 100644 index 0000000..719c611 --- /dev/null +++ b/test/layouts.js @@ -0,0 +1,90 @@ +var vows = require('vows'), +assert = require('assert'); + +vows.describe('log4js layouts').addBatch({ + 'colouredLayout': { + topic: function() { + return require('../lib/layouts').colouredLayout; + }, + + 'should apply level colour codes to output': function(layout) { + var output = layout({ + message: "nonsense", + startTime: new Date(2010, 11, 5, 14, 18, 30, 45), + categoryName: "cheese", + level: { + colour: "green", + toString: function() { return "ERROR"; } + } + }); + assert.equal(output, '\033[90m[2010-12-05 14:18:30.045] \033[39m\033[32m[ERROR] \033[39m\033[90mcheese - \033[39mnonsense'); + } + }, + + 'messagePassThroughLayout': { + topic: function() { + return require('../lib/layouts').messagePassThroughLayout; + }, + 'should take a logevent and output only the message' : function(layout) { + assert.equal(layout({ + message: "nonsense", + startTime: new Date(2010, 11, 5, 14, 18, 30, 45), + categoryName: "cheese", + level: { + colour: "green", + toString: function() { return "ERROR"; } + } + }), "nonsense"); + } + }, + + 'basicLayout': { + topic: function() { + var layout = require('../lib/layouts').basicLayout, + event = { + message: 'this is a test', + startTime: new Date(2010, 11, 5, 14, 18, 30, 45), + categoryName: "tests", + level: { + colour: "green", + toString: function() { return "DEBUG"; } + } + }; + return [layout, event]; + }, + 'should take a logevent and output a formatted string': function(args) { + var layout = args[0], event = args[1]; + assert.equal(layout(event), "[2010-12-05 14:18:30.045] [DEBUG] tests - this is a test"); + }, + 'should output a stacktrace, message if the event has an error attached': function(args) { + var layout = args[0], event = args[1], output, lines, + error = new Error("Some made-up error"), + stack = error.stack.split(/\n/); + + event.exception = error; + output = layout(event); + lines = output.split(/\n/); + + assert.length(lines, stack.length+1); + assert.equal(lines[0], "[2010-12-05 14:18:30.045] [DEBUG] tests - this is a test"); + assert.equal(lines[1], "[2010-12-05 14:18:30.045] [DEBUG] tests - Error: Some made-up error"); + for (var i = 1; i < stack.length; i++) { + assert.equal(lines[i+1], stack[i]); + } + }, + 'should output a name and message if the event has something that pretends to be an error': function(args) { + var layout = args[0], event = args[1], output, lines; + event.exception = { + name: 'Cheese', + message: 'Gorgonzola smells.' + }; + output = layout(event); + lines = output.split(/\n/); + + assert.length(lines, 2); + assert.equal(lines[0], "[2010-12-05 14:18:30.045] [DEBUG] tests - this is a test"); + assert.equal(lines[1], "[2010-12-05 14:18:30.045] [DEBUG] tests - Cheese: Gorgonzola smells."); + } + } +}).export(module); +