From 917737a6a84ba920f0d574e85b77a263edf3add7 Mon Sep 17 00:00:00 2001 From: indexzero Date: Mon, 27 Dec 2010 19:48:53 -0600 Subject: [PATCH] [doc dist api] Initial commit ... no tests since I have no internet --- LICENSE | 19 +++++++ README.md | 17 ++++++ lib/winston.js | 23 ++++++++ lib/winston/logger.js | 94 +++++++++++++++++++++++++++++++ lib/winston/transports.js | 13 +++++ lib/winston/transports/console.js | 28 +++++++++ lib/winston/transports/loggly.js | 48 ++++++++++++++++ lib/winston/transports/riak.js | 26 +++++++++ lib/winston/utils/index.js | 21 +++++++ package.json | 20 +++++++ 10 files changed, 309 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 lib/winston.js create mode 100644 lib/winston/logger.js create mode 100644 lib/winston/transports.js create mode 100644 lib/winston/transports/console.js create mode 100644 lib/winston/transports/loggly.js create mode 100644 lib/winston/transports/riak.js create mode 100644 lib/winston/utils/index.js create mode 100644 package.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f257e8d0d --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009 Charlie Robbins + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..5b7c64cfd --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# winston + +A multi-transport async logging library for node.js. + +"CHILL WINSTON!" ... I put it in the logs. + +## Installation + +### Installing npm (node package manager) +
+  curl http://npmjs.org/install.sh | sh
+
+ +### Installing winston +
+  [sudo] npm install winston
+
\ No newline at end of file diff --git a/lib/winston.js b/lib/winston.js new file mode 100644 index 000000000..ce9ec4277 --- /dev/null +++ b/lib/winston.js @@ -0,0 +1,23 @@ +/* + * winston.js: Top-level include defining Winston. + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var winston = exports; + +winston.default = {}; +winston.Logger = require('./winston/logger').Logger; +winston.Transports = require('./winston/transports'); + +var defaultLogger = new (winston.Logger)() + +winston.add = function (transport, options) { + +}; + +winston.remove = function (transport) { + +}; \ No newline at end of file diff --git a/lib/winston/logger.js b/lib/winston/logger.js new file mode 100644 index 000000000..642fc0d7e --- /dev/null +++ b/lib/winston/logger.js @@ -0,0 +1,94 @@ + +var util = require('util'), + events = require('events') + transport = require('./transports'); + +var Logger = exports.Logger = function (options) { + events.EventEmitter.call(this); + + // Default to 'info' Level + this.level = 2; + this.emitErrs = options.emitErrs || true; + + var self = this; + Object.keys(options.transports).forEach(function (tname) { + var config = options.transports[tname]; + config = config instanceof Object ? config : { level: config }; + + self.add(tname, config); + }); +}; + +util.inherits(Logger, events.EventEmitter); + +var levels = Logger.prototype.levels = { + silly: 0, + verbose: 1, + info: 2, + warn: 3, + error: 4 +}; + +// +// Define prototype methods for each log level +// i.e. logger.log('info', msg) <=> logger.info(msg) +// +Object.keys(levels).forEach(function (level) { + Logger.prototype[level] = function (msg, meta, callback) { + if (arguments.length === 2) { + callback = meta; + this.log(level, msg, callback); + } + else if (arguments.length === 3) { + this.log(level, msg, meta, callback); + } + }; +}); + +// +// function log (level, msg, [meta], callback) +// Core logging method for Winston. Metadata is optional. +// +Logger.prototype.log = function (level, msg) { + var self = this, logged = 0, errs = [], + meta, callback, len = this.transports.length; + + if (arguments.length === 3) { + meta = {}; + callback = arguments[2] + } + else if (arguments.length === 4) { + meta = arguments[2]; + callback = arguments[3]; + } + + if (this.transport.length === 0) return callback(new Error('Cannot log with no transports.')); + else if (!levels[leve]) return callback(new Error('Unknown log level: ' + level)); + + this.transports.forEach(function (transport) { + if ((transport.level && transport.level >= levels[level]) + || (!transport.level && self.level >= levels[level])) { + transport.log(level, msg, meta, function (err, res) { + if (err) errs.push({ error: err, transport: transport }); + if (err && self.emitErrs) return self.emit('error', err, transport); + + self.emit('log', levels[level], msg); + if (++logged == len && callback) callback(errs ? errs : null); + }); + } + }); +}; + +Logger.prototype.add = function (transport, options) { + var proto = Object.keys(transports).filter(function (k) { return k === transport }); + if (proto.length === 0) throw new Error('Cannot find Transport: ' + transport); + + this.transports.push(new (transports[transport])(options)); +}; + +Logger.prototype.remove = function (transport) { + if (!this.transports[transport]) throw new Error('Transport ' + transport + ' not attached to this instance'); + + var transport = this.transports[transport]; + if (transport.close) transport.close(); +}; diff --git a/lib/winston/transports.js b/lib/winston/transports.js new file mode 100644 index 000000000..26bb33d38 --- /dev/null +++ b/lib/winston/transports.js @@ -0,0 +1,13 @@ +/* + * transports.js: Set of all transports Winston knows about + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var transports = exports; + +transports.Console = require('./transports/console').Console; +transport.Loggly = require('./transports/loggly').Loggly; +transports.Riak = require('./transports/riak').Riak; diff --git a/lib/winston/transports/console.js b/lib/winston/transports/console.js new file mode 100644 index 000000000..b95f7d00a --- /dev/null +++ b/lib/winston/transports/console.js @@ -0,0 +1,28 @@ +/* + * console.js: Transport for outputting to the console + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var util = require('util'), + eyes = require('eyes'), + colors = require('colors'); + +var Console = exports.Console = function (options) { + // TODO: Consume the colorize option + this.colorize = options.colorize; +}; + +Console.prototype.log = function (level, msg, meta, callback) { + if (level !== 'error') { + util.debug(level + ': ' msg); + } + else { + util.error(msg); + } + + if (meta && Object.keys(meta).length > 0) eyes.inspect(meta); + callback(null, true); +}; \ No newline at end of file diff --git a/lib/winston/transports/loggly.js b/lib/winston/transports/loggly.js new file mode 100644 index 000000000..45c52b36d --- /dev/null +++ b/lib/winston/transports/loggly.js @@ -0,0 +1,48 @@ +/* + * loggly.js: Transport for logginh to remote Loggly API + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var loggly = require('loggly') + utils = require('./utils'); + +var Loggly = exports.Loggly = function (options) { + if (!options.auth) throw new Error('Loggly authentication is required'); + if (!options.subdomain) throw new Error('Loggly Subdomain is required'); + if (!options.inputToken && !options.inputName) throw new Error('Target input token or name is required.'); + + this.client = new (loggly.Loggly)({ + subdomain: options.subdomain + auth: options.auth + }); + + + if (options.inputToken) { + this.inputToken = options.inputToken; + this.ready = true; + } + else if (options.inputName) { + this.ready = false; + this.inputName = options.inputName; + + var self = this; + this.client.getInput(this.inputName, function (err, input) { + if (err) throw err; + + self.inputToken = input.input_token; + self.ready = true; + }); + } +}; + +Loggly.prototype.log = function (level, msg, meta, callback) { + var message = utils.clone(meta); + message.level = level; + message.message = msg; + + // TODO: Fix Race condition here if 'inputName' is provided to the constructor + this.client.log(this.inputToken, message, callback); +}; \ No newline at end of file diff --git a/lib/winston/transports/riak.js b/lib/winston/transports/riak.js new file mode 100644 index 000000000..099f90428 --- /dev/null +++ b/lib/winston/transports/riak.js @@ -0,0 +1,26 @@ +/* + * riak.js: Transport for logging to Riak server + * (Special thanks to node-rlog) + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var riakjs = require('riak-js'), + utils = require('./../utils'); + +var Riak = exports.Riak = function (options) { + this.client = riakjs.getClient(options); + + // TODO: Better support for dynamic bucket names + this.bucketName = options.bucketName; +}; + +Riak.prototype.log = function (level, msg, meta, callback) { + var metac = utils.clone(meta); + metac.level = level; + metac.contentType = msg instanceof Object ? 'application/json' : 'text/plain'; + + this.client.save(this.bucketName, Date.now, msg, metac, callback); +}; \ No newline at end of file diff --git a/lib/winston/utils/index.js b/lib/winston/utils/index.js new file mode 100644 index 000000000..6eea1097a --- /dev/null +++ b/lib/winston/utils/index.js @@ -0,0 +1,21 @@ +/* + * index.js: Top-level include for Winston + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +// +// function clone (obj) +// Helper method for deep cloning pure JSON objects +// i.e. JSON objects that are either literals or objects (no Arrays, etc) +// +var clone = exports.clone = function (obj) { + var clone = {}; + for (var i in obj) { + clone[i] = obj[i] instanceof Object ? interns.clone(obj[i]) : obj[i]; + } + + return clone; +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..a40ed4d7f --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "winston", + "description": "A multi-transport async logging library for Node.js", + "version": "0.1.0", + "author": "Charlie Robbins ", + "repository": { + "type": "git", + "url": "http://github.com/indexzero/winston.git" + }, + "keywords": ["logging", "sysadmin", "tools"], + "dependencies": { + "colors": ">= 0.3.0", + "eyes": ">= 0.1.6", + "riak-js": ">= 0.3.0beta4", + "vows": ">= 0.5.2" + }, + "main": "./lib/winston", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.3.0" } +}