diff --git a/Readme.md b/Readme.md index 14db231..fd778ad 100644 --- a/Readme.md +++ b/Readme.md @@ -34,6 +34,51 @@ Instead of the log level constants, you may also supply a string: log.error('oh no, failed to send mail to %s.', user.email); +Turn colors on: + + log.colorful(); + +Turn colors off: + + log.colorful(false); + +Setting up our own colors : + + log.colorful({ + 'EMERGENCY' : "\033[0;31m", + 'ALERT' : "\033[0;33m", + 'CRITICAL' : "\033[0;31m", + 'ERROR' : "\033[1;31m", + 'WARNING' : "\033[0;33m", + 'NOTICE' : "\033[0;36m", + 'INFO' : "\033[0;35m", + 'DEBUG' : "\033[0m" + }); + + You don't have to specify all levels, when defining colors, if you only want to specify certain levels, you just write those: + + log.colorful({ + 'INFO' : "\033[0;35m", + 'DEBUG' : "\033[0m" + }) + +Setting up your own log levels: + + log.customLevels([ 'COMMAND', 'DATA' ] ); + +Which will simple add methods `command` and `data` to the `Log` object, and you can use them as the way that default log methods be used, like `log.command( "a command message")`. + +Also, you can setting up your own log levels and their colors at the same time: + + log.customLevels({ + COMMAND: '\033[0;31m', + DATA: '\033[0;31m' + }); + + + + + ## Reader To stream a log, simply pass a readable stream instead of a writable: diff --git a/examples/stdout-color.js b/examples/stdout-color.js new file mode 100644 index 0000000..c5ce7f0 --- /dev/null +++ b/examples/stdout-color.js @@ -0,0 +1,66 @@ + +/** + * Module dependencies. + */ + +var Log = require('../lib/log') + , log = new Log('trace').colorful(); + +/** + * Default log. + */ +log.trace( 'Below is default logs.'); +log.debug('a debug message'); +log.info('a info message'); +log.notice('a notice message'); +log.warning('a warning message'); +log.error('a error message'); +log.critical('a critical message'); +log.alert('a alert message'); +log.emergency('a emergency %s', 'message'); + +/** + * Setup our own colors + */ +log.colorful({ + ERROR : "\033[0;37m", + CRITICAL : "\033[1;36m" +}); + +log.trace( 'Below is color-customed logs.'); +log.debug('a debug message'); +log.info('a info message'); +log.notice('a notice message'); +log.warning('a warning message'); +log.error('a error message'); +log.critical('a critical message'); +log.alert('a alert message'); +log.emergency('a emergency %s', 'message'); + +/** + * Turn off color. + */ +log.colorful( false ); +log.trace( 'Below is color-offed logs.'); +log.debug('a debug message'); +log.info('a info message'); +log.notice('a notice message'); +log.warning('a warning message'); +log.error('a error message'); +log.critical('a critical message'); +log.alert('a alert message'); +log.emergency('a emergency %s', 'message'); + +/** + * Customize levels and colors + */ +log.customLevels({ + COMMAND: "\x1b[0;35m", + DATA: "\x1b[0;33m", + RESULT: "\x1b[0;36m" +}).colorful( true).setLevel( 'result' ); + +log.command( 'a command message' ); +log.data( 'a data message' ); +log.result( 'a result message' ); + diff --git a/lib/log.js b/lib/log.js index 41daac2..2e3a2f4 100644 --- a/lib/log.js +++ b/lib/log.js @@ -2,6 +2,23 @@ * Log.js * Copyright(c) 2010 TJ Holowaychuk * MIT Licensed + * + * Colorful stdout messages by Couto + * + * CustomLevel by Neekey + */ + +/** + * Colors Cheat-sheet + * white : "\033[1;37m", black : "\033[0;30m", + * red : "\033[0;31m", light_red : "\033[1;31m", + * green : "\033[0;32m", light_green : "\033[1;32m", + * yellow : "\033[0;33m", light_yellow : "\033[1;33m", + * blue : "\033[0;34m", light_blue : "\033[1;34m", + * purple : "\033[0;35m", light_purple : "\033[1;35m", + * cyan : "\033[0;36m", light_cyan : "\033[1;36m", + * gray : "\033[1;30m", light_gray : "\033[0;37m", + * reset : "\033[0m" */ /** @@ -13,239 +30,253 @@ var EventEmitter = require('events').EventEmitter; /** * Initialize a `Loggeer` with the given log `level` defaulting * to __DEBUG__ and `stream` defaulting to _stdout_. - * - * @param {Number} level - * @param {Object} stream + * + * @param {Number} level + * @param {Object} stream * @api public */ -var Log = exports = module.exports = function Log(level, stream){ - if ('string' == typeof level) level = exports[level.toUpperCase()]; - this.level = level || exports.DEBUG; - this.stream = stream || process.stdout; - if (this.stream.readable) this.read(); +var Log = exports = module.exports = function Log(level, stream, custom) { + + // Add custom levels. + if( custom ) this.customLevels( custom ); + this.setLevel( level ); + this.stream = stream || process.stdout; + if (this.stream.readable) this.read(); }; /** - * System is unusable. - * - * @type Number + * Define Levels for levels. */ - -exports.EMERGENCY = 0; +exports.colors = { + /** + * System is unusable. + */ + 'EMERGENCY':"\033[0;31m", + /** + * Action must be taken immediately. + */ + 'ALERT':"\033[0;33m", + /** + * Critical condition. + */ + 'CRITICAL':"\033[0;31m", + /** + * Error condition. + */ + 'ERROR':"\033[1;31m", + /** + * Warning condition. + */ + 'WARNING':"\033[0;33m", + /** + * Normal but significant condition. + */ + 'NOTICE':"\033[0;36m", + /** + * Purely informational message. + */ + 'INFO':"\033[0;35m", + /** + * Application debug messages. + */ + 'DEBUG':"\033[0m", + /** + * Application debug messages. + */ + 'TRACE':"\033[1;30m", + /** + * Reset log color. + */ + 'reset':"\033[0m" +} /** - * Action must be taken immediately. - * - * @type Number + * The latest enum value for level. + * + * @type {Number} */ - -exports.ALERT = 1; +var enumCount = 0; /** - * Critical condition. + * Add an new log level to exports, and assign an new enum value to it. * - * @type Number + * @param levelName */ +var addLevelEnumerable = function (levelName) { -exports.CRITICAL = 2; - -/** - * Error condition. - * - * @type Number - */ + if (!( levelName in exports )) { + exports[ levelName ] = ++enumCount; + } -exports.ERROR = 3; + var lowCaseLevelName = levelName.toLowerCase(); -/** - * Warning condition. - * - * @type Number - */ + if(!( lowCaseLevelName in Log.prototype )) { -exports.WARNING = 4; + Log.prototype[ lowCaseLevelName ] = function(msg){ + this.log(levelName, arguments); + } + } +}; /** - * Normal but significant condition. - * - * @type Number + * prototype. */ -exports.NOTICE = 5; +Log.prototype = { -/** - * Purely informational message. - * - * @type Number - */ + /** + * Start emitting "line" events. + * + * @api public + */ + + read:function () { + var buf = '' + , self = this + , stream = this.stream; + + stream.setEncoding('ascii'); + stream.on('data', function (chunk) { + buf += chunk; + if ('\n' != buf[buf.length - 1]) return; + buf.split('\n').map(function (line) { + if (!line.length) return; + try { + var captures = line.match(/^\[([^\]]+)\] (\w+) (.*)/); + var obj = { + date:new Date(captures[1]), level:exports[captures[2]], levelString:captures[2], msg:captures[3] + }; + self.emit('line', obj); + } catch (err) { + // Ignore + } + }); + buf = ''; + }); + + stream.on('end', function () { + self.emit('end'); + }); + }, + + /** + * Log output message. + * + * @param {String} levelStr + * @param {Array} args + * @api private + */ + + log:function (levelStr, args) { + if (exports[levelStr] <= this.level) { + var i = 1; + var msg = args[0].replace(/%s/g, function () { + return args[i++]; + }); + if (this.stream === process.stdout && + this.useColors && + exports.colors[levelStr]) { + + this.stream.write( + exports.colors[levelStr] + + '[' + new Date + ']' + + ' ' + levelStr + + ' ' + msg + + exports.colors.reset + + '\n' + ); + } else { + this.stream.write( + '[' + new Date + ']' + + ' ' + levelStr + + ' ' + msg + + '\n' + ); + } + } + }, + + /** + * Customize colors for stout. + * + * @param {Object|Boolean} colors + * @return {*} + * @example + * `colorful({ DEBUG: "\033[0m"});` set level `DEBUG` color to `\033[0m`. + * `colorful(false)` turn off color. + * `colorful()` or `colorful(true)` turn on color. + * + */ + colorful:function (colors) { + var k; + if (Object.prototype.toString.call(colors) === '[object Object]') { + for (k in colors) { + if (colors.hasOwnProperty(k)) { + exports.colors[k] = colors[k]; + } + } + this.useColors = true; + } else if (colors === false) { + this.useColors = false; + } + else { + this.useColors = true; + } + return this; + }, -exports.INFO = 6; + /** + * Customize Levels and Colors. + * + * @param {Object|Array} levels + * @example + * `customLevels([ 'DEBUG', 'ERROR', 'WARNING' ]);` + * `customLevels({ DEBUG: "\033[0m"});` + */ + customLevels: function( levels ){ -/** - * Application debug messages. - * - * @type Number - */ + var k; -exports.DEBUG = 7; + if( Object.prototype.toString.call( levels ) === '[object Array]' ){ -/** - * prototype. - */ + for( k = 0; k < levels.length; k++ ){ -Log.prototype = { - - /** - * Start emitting "line" events. - * - * @api public - */ - - read: function(){ - var buf = '' - , self = this - , stream = this.stream; - - stream.setEncoding('ascii'); - stream.on('data', function(chunk){ - buf += chunk; - if ('\n' != buf[buf.length - 1]) return; - buf.split('\n').map(function(line){ - if (!line.length) return; - try { - var captures = line.match(/^\[([^\]]+)\] (\w+) (.*)/); - var obj = { - date: new Date(captures[1]) - , level: exports[captures[2]] - , levelString: captures[2] - , msg: captures[3] - }; - self.emit('line', obj); - } catch (err) { - // Ignore + addLevelEnumerable( levels[ k ] ); + } } - }); - buf = ''; - }); - - stream.on('end', function(){ - self.emit('end'); - }); - }, - - /** - * Log output message. - * - * @param {String} levelStr - * @param {Array} args - * @api private - */ - - log: function(levelStr, args) { - if (exports[levelStr] <= this.level) { - var i = 1; - var msg = args[0].replace(/%s/g, function(){ - return args[i++]; - }); - this.stream.write( - '[' + new Date + ']' - + ' ' + levelStr - + ' ' + msg - + '\n' - ); + else if( Object.prototype.toString.call( levels ) === '[object Object]' ){ + this.colorful( levels ); + + for( k in levels ){ + addLevelEnumerable( k ); + } + } + + return this; + }, + + /** + * Set log level + * + * @param level + * @return {*} + */ + setLevel: function( level ){ + + if ('string' == typeof level) level = exports[level.toUpperCase()]; + this.level = level || this.level || exports.DEBUG; + + return this; } - }, - - /** - * Log emergency `msg`. - * - * @param {String} msg - * @api public - */ - - emergency: function(msg){ - this.log('EMERGENCY', arguments); - }, - - /** - * Log alert `msg`. - * - * @param {String} msg - * @api public - */ - - alert: function(msg){ - this.log('ALERT', arguments); - }, - - /** - * Log critical `msg`. - * - * @param {String} msg - * @api public - */ - - critical: function(msg){ - this.log('CRITICAL', arguments); - }, - - /** - * Log error `msg`. - * - * @param {String} msg - * @api public - */ - - error: function(msg){ - this.log('ERROR', arguments); - }, - - /** - * Log warning `msg`. - * - * @param {String} msg - * @api public - */ - - warning: function(msg){ - this.log('WARNING', arguments); - }, - - /** - * Log notice `msg`. - * - * @param {String} msg - * @api public - */ - - notice: function(msg){ - this.log('NOTICE', arguments); - }, - - /** - * Log info `msg`. - * - * @param {String} msg - * @api public - */ - - info: function(msg){ - this.log('INFO', arguments); - }, - - /** - * Log debug `msg`. - * - * @param {String} msg - * @api public - */ - - debug: function(msg){ - this.log('DEBUG', arguments); - } + }; +/** + * Add Default levels to exports and add methods to Log.prototype. + */ +Log.prototype.customLevels( exports.colors ); + /** * Inherit from `EventEmitter`. */