Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[merge] Merge gh-6

  • Loading branch information...
commit fceba7247f40cf65b79de86dbb2ebff7fb1fc81c 2 parents 70a775a + b09c893
@indexzero indexzero authored
Showing with 269 additions and 177 deletions.
  1. +5 −3 README.md
  2. +262 −172 lib/winston-mongodb.js
  3. +2 −2 package.json
View
8 README.md
@@ -37,17 +37,19 @@ The [winston][0] codebase has been growing significantly with contributions and
The MongoDB transport takes the following options. 'db' is required:
-* __level:__ Level of messages that this transport should log.
-* __silent:__ Boolean flag indicating whether to suppress output.
+* __level:__ Level of messages that this transport should log, defaults to 'info'.
+* __silent:__ Boolean flag indicating whether to suppress output, defaults to false.
* __db:__ The name of the database you want to log to. *[required]*
* __collection__: The name of the collection you want to store log messages in, defaults to 'log'.
* __safe:__ Boolean indicating if you want eventual consistency on your log messages, if set to true it requires an extra round trip to the server to ensure the write was committed, defaults to true.
* __host:__ The host running MongoDB, defaults to localhost.
* __port:__ The port on the host that MongoDB is running on, defaults to MongoDB's default port.
+* __errortimeout:__ Reconnect timeout upon connection error from Mongo, defaults to 10 seconds (10000).
+* __timeoout:__ Timeout for keeping idle connection to Mongo alive, defaults to 10 seconds (10000).
*Metadata:* Logged as a native JSON object.
#### Author: [Charlie Robbins](http://blog.nodejitsu.com)
-#### Contributors: [Kendrick Taylor](https://github.com/sktaylor)
+#### Contributors: [Kendrick Taylor](https://github.com/sktaylor), [Yosef Dinerstein](https://github.com/yosefd)
[0]: https://github.com/indexzero/winston
View
434 lib/winston-mongodb.js
@@ -1,173 +1,263 @@
-/*
- * mongodb.js: Transport for outputting to a MongoDB database
- *
- * (C) 2010 Charlie Robbins, Kendrick Taylor
- * MIT LICENCE
- *
- */
-
-var util = require('util'),
- mongodb = require('mongodb'),
- winston = require('winston');
-
-//
-// ### function MongoDB (options)
-// Constructor for the MongoDB transport object.
-//
-var MongoDB = exports.MongoDB = function (options) {
- options = options || {};
-
- if (!options.db) {
- throw new Error("Cannot log to MongoDB without database name.");
- }
-
- this.name = 'mongodb';
- this.db = options.db;
- this.host = options.host || 'localhost';
- this.port = options.port || 27017;
- this.collection = options.collection || "log";
- this.safe = options.safe || true;
- this.level = options.level || 'info';
- this.silent = options.silent || false;
- this.username = options.username || null;
- this.password = options.password || null;
- this.keepAlive = options.keepAlive || 10000;
- this.state = 'unopened';
- this.pending = [];
-
- this.client = new mongodb.Db(this.db, new mongodb.Server(this.host, this.port, {}), {
- native_parser : false
- });
-};
-
-//
-// Inherit from `winston.Transport`.
-//
-util.inherits(MongoDB, winston.Transport);
-
-//
-// Define a getter so that `winston.transports.MongoDB`
-// is available and thus backwards compatible.
-//
-winston.transports.MongoDB = MongoDB;
-
-//
-// ### function log (level, msg, [meta], callback)
-// #### @level {string} Level at which to log the message.
-// #### @msg {string} Message to log
-// #### @meta {Object} **Optional** Additional metadata to attach
-// #### @callback {function} Continuation to respond to when complete.
-// Core logging method exposed to Winston. Metadata is optional.
-//
-MongoDB.prototype.log = function (level, msg, meta, callback) {
- var self = this;
-
- if (this.silent) {
- return callback(null, true);
- }
-
- this.open(function (err) {
- if (err) {
- self.emit('error', err);
- }
-
- self._db.collection(self.collection, function (err, col) {
- if (err) {
- self.emit('error', err);
- }
-
- var entry = {
- timestamp: new Date(), // RFC3339/ISO8601 format instead of common.timestamp()
- level: level,
- message: msg,
- meta: meta
- };
-
- col.save(entry, { safe: self.safe }, function (err, doc) {
- if (err) {
- self.emit('error', err);
- }
-
- self.emit('logged');
- });
- });
- });
-
- callback(null, true);
-};
-
-//
-// ### function open (callback)
-// #### @callback {function} Continuation to respond to when complete
-// Attempts to open a new connection to MongoDB. If one has not opened yet
-// then the callback is enqueued for later flushing.
-//
-MongoDB.prototype.open = function (callback) {
- var self = this;
-
- if (this.state === 'opening' || this.state === 'unopened') {
- //
- // While opening our MongoDB connection, append any callback
- // to a list that is managed by this instance.
- //
- this.pending.push(callback);
-
- if (this.state === 'opening') {
- return;
- }
- }
- else if (this.state === 'opened') {
- return callback();
- }
- else if (this.state === 'error') {
- return callback(err);
- }
-
- function flushPending (err, db) {
- self._db = db;
- self.state = 'opened';
-
- //
- // Iterate over all callbacks that have accumulated during
- // the creation of the TCP socket.
- //
- for (var i = 0; i < self.pending.length; i++) {
- self.pending[i]();
- }
-
- // Quickly truncate the Array (this is more performant).
- self.pending.length = 0;
- }
-
- function onError (err) {
- self.state = 'error';
- self.error = err;
- flushPending(err, false);
- }
-
- this.state = 'opening';
- this.client.open(function (err, db) {
- if (err) {
- return onError(err);
- }
- else if (self.username && self.password) {
- return self.client.authenticate(self.username, self.password, function (err) {
- return err ? onError(err) : flushPending(null, db);
- });
- }
-
- flushPending(null, db)
- });
-
- //
- // Set a timeout to close the client connection unless `this.keepAlive`
- // has been set to true in which case it is the responsibility of the
- // programmer to close the underlying connection.
- //
- if (!(this.keepAlive === true)) {
- setTimeout(function () {
- self.state = 'unopened';
- return self._db ? self._db.close() : null
- }, this.keepAlive);
- }
+/*
+ * mongodb.js: Transport for outputting to a MongoDB database
+ *
+ * (C) 2010 Charlie Robbins, Kendrick Taylor
+ * MIT LICENCE
+ *
+ */
+
+var util = require('util');
+var mongodb = require('mongodb');
+var winston = require('winston');
+
+//
+// ### function MongoDB (options)
+// Constructor for the MongoDB transport object.
+//
+var MongoDB = exports.MongoDB = function (options) {
+ options = options || {};
+
+ if (!options.db) {
+ throw new Error("Cannot log to MongoDB without database name.");
+ }
+
+ var self = this;
+
+ this.name = 'mongodb';
+ this.db = options.db;
+ this.host = options.host || 'localhost';
+ this.port = options.port || mongodb.Connection.DEFAULT_PORT;
+ this.collection = options.collection || "log";
+ this.safe = options.safe || true;
+ this.level = options.level || 'info';
+ this.silent = options.silent || false;
+ this.username = options.username || null;
+ this.password = options.password || null;
+ this.errorTimeout = options.errorTimeout || 10000;
+
+ if (options.keepAlive !== true) {
+ //
+ // Backward compatibility for timeout delivered in keepAlive parameter.
+ //
+ this.timeout = options.timeout || options.keepAlive || 10000;
+ }
+
+ this.state = 'unopened';
+ this.timeoutId = null;
+ this.pending = [];
+
+ this.server = new mongodb.Server(this.host, this.port, {});
+ this.client = new mongodb.Db(this.db, this.server, {
+ native_parser: false
+ });
+
+ this.server.on('error', function (err) {
+ // Close session. Next log will reopen.
+ self.close();
+ });
+
+ this.client.on('error', function (err) {
+ // Close session. Next log will reopen.
+ self.close();
+ });
+};
+
+//
+// Inherit from `winston.Transport`.
+//
+util.inherits(MongoDB, winston.Transport);
+
+//
+// Define a getter so that `winston.transports.MongoDB`
+// is available and thus backwards compatible.
+//
+winston.transports.MongoDB = MongoDB;
+
+//
+// ### function log (level, msg, [meta], callback)
+// #### @level {string} Level at which to log the message.
+// #### @msg {string} Message to log
+// #### @meta {Object} **Optional** Additional metadata to attach
+// #### @callback {function} Continuation to respond to when complete.
+// Core logging method exposed to Winston. Metadata is optional.
+//
+MongoDB.prototype.log = function (level, msg, meta, callback) {
+ var self = this;
+
+ //
+ // Avoid reentrancy that can be not assumed by database code.
+ // If database logs, better not to call database itself in the same call.
+ //
+ process.nextTick(function () {
+ if (self.silent) {
+ return callback(null, true);
+ }
+
+ self.open(function (err) {
+ if (err) {
+ self.emit('error', err);
+ return callback(err, null);
+ }
+
+ // Set a timeout to close the client connection unless `self.keepAlive`
+ // has been set to true in which case it is the responsibility of the
+ // programmer to close the underlying connection.
+ if (self.timeout) {
+ if (self.timeoutId) {
+ clearTimeout(self.timeoutId);
+ }
+
+ self.timeoutId = setTimeout(function () {
+ // The session is idle. Closing it.
+ self.close();
+ }, self.timeout);
+ }
+
+ function onError(err) {
+ self.close();
+ self.emit('error', err);
+ callback(err, null);
+ }
+
+ self._db.collection(self.collection, function (err, col) {
+ if (err) {
+ return onError(err);
+ }
+
+ var entry = {
+ timestamp: new Date(), // RFC3339/ISO8601 format instead of common.timestamp()
+ level: level,
+ message: msg,
+ meta: meta
+ };
+
+ col.save(entry, { safe: self.safe }, function (err) {
+ if (err) {
+ return onError(err);
+ }
+
+ self.emit('logged');
+ callback(null, true);
+ });
+ });
+ });
+ });
+};
+
+//
+// ### function open (callback)
+// #### @callback {function} Continuation to respond to when complete
+// Attempts to open a new connection to MongoDB. If one has not opened yet
+// then the callback is enqueued for later flushing.
+//
+MongoDB.prototype.open = function (callback) {
+ var self = this;
+
+ if (this.state === 'opening' || this.state === 'unopened') {
+ //
+ // While opening our MongoDB connection, append any callback
+ // to a list that is managed by this instance.
+ //
+ this.pending.push(callback);
+
+ if (this.state === 'opening') {
+ return;
+ }
+ }
+ else if (this.state === 'opened') {
+ return callback();
+ }
+ else if (this.state === 'error') {
+ return callback(this.error);
+ }
+
+ //
+ // Flushes any pending log messages to MongoDB.
+ //
+ function flushPending(err) {
+ //
+ // Iterate over all callbacks that have accumulated during
+ // the creation of the TCP socket.
+ //
+ for (var i = 0; i < self.pending.length; i++) {
+ self.pending[i](err);
+ }
+
+ //
+ // Quickly truncate the Array (this is more performant).
+ //
+ self.pending.length = 0;
+ }
+
+ //
+ // Helper function which executes if there is an error
+ // establishing the connection.
+ //
+ function onError(err) {
+ self.state = 'error';
+ self.error = err;
+ flushPending(err);
+
+ //
+ // Close to be able to attempt opening later.
+ //
+ self.client.close();
+
+ //
+ // Retry new connection upon following request after error timeout expired.
+ //
+ setTimeout(function () {
+ //
+ // This is the only exit from error state.
+ //
+ self.state = 'unopened';
+ }, self.errorTimeout);
+ }
+
+ //
+ // Helper function which executes if the connection
+ // is established.
+ //
+ function onSuccess(db) {
+ self.state = 'opened';
+ self._db = db;
+ flushPending();
+ }
+
+ this.state = 'opening';
+ this.client.open(function (err, db) {
+ if (err) {
+ return onError(err);
+ }
+
+ if (self.username && self.password) {
+ return self.client.authenticate(self.username, self.password, function (err) {
+ return err ? onError(err) : onSuccess(db);
+ });
+ }
+
+ onSuccess(db);
+ });
+};
+
+//
+// ### function close ()
+// Cleans up resources (streams, event listeners) for
+// this instance (if necessary).
+//
+MongoDB.prototype.close = function () {
+ //
+ // Reset session if it is opened.
+ //
+ if (this.state === 'opened') {
+ if (this.timeoutId) {
+ clearTimeout(this.timeoutId);
+ }
+
+ //
+ // Next time try to open new session.
+ //
+ this.client.close();
+ this.state = 'unopened';
+ }
};
View
4 package.json
@@ -1,10 +1,10 @@
{
"name": "winston-mongodb",
"description": "A MongoDB transport for winston",
- "version": "0.3.3",
+ "version": "0.3.4",
"author": "Charlie Robbins <charlie.robbins@gmail.com>",
"contributors": [
- { "name": "Kendrick Taylor", "email": "sktayloriii@gmail.com" }
+ { "name": "Kendrick Taylor", "email": "sktayloriii@gmail.com", "email": "yosefd@microsoft.com" }
],
"repository": {
"type": "git",
Please sign in to comment.
Something went wrong with that request. Please try again.