* added support of passing promises objects which will be resolved with
pre-connected db object in options.db;
* fixed issue when events logged sometime between authorizeDb and
processOpQuery calls may be lost;
* renamed default log collection to 'log' because it makes more sense than
'logs' ('log' is a list of messages already, plural form would imply
multiple of such lists);
* this.mainDb renamed to this.logDb;
* this._opQuery renamed to this._opQueue;
* added test to promises support (bluebird added to devDependencies as A+ promises implementation).
This commit is contained in:
Yurij Mikhalevich 2015-03-23 22:13:24 +03:00
Родитель 7a7b98ed25
Коммит ccf69f6cc7
4 изменённых файлов: 81 добавлений и 44 удалений

Просмотреть файл

@ -34,11 +34,12 @@ The MongoDB transport takes the following options. 'db' is required:
'info'. 'info'.
* __silent:__ Boolean flag indicating whether to suppress output, defaults to * __silent:__ Boolean flag indicating whether to suppress output, defaults to
false. false.
* __db:__ MongoDB connection uri or preconnected db object. * __db:__ MongoDB connection uri, pre-connected db object or promise object
which will be resolved with pre-connected db object.
* __options:__ MongoDB connection parameters (optional, defaults to * __options:__ MongoDB connection parameters (optional, defaults to
`{db: {native_parser: true}, server: {poolSize: 2, socketOptions: {autoReconnect: true}}}`). `{db: {native_parser: true}, server: {poolSize: 2, socketOptions: {autoReconnect: true}}}`).
* __collection__: The name of the collection you want to store log messages in, * __collection__: The name of the collection you want to store log messages in,
defaults to 'logs'. defaults to 'log'.
* __storeHost:__ Boolean indicating if you want to store machine hostname in * __storeHost:__ Boolean indicating if you want to store machine hostname in
logs entry, if set to true it populates MongoDB entry with 'hostname' field, logs entry, if set to true it populates MongoDB entry with 'hostname' field,
which stores os.hostname() value. which stores os.hostname() value.
@ -74,11 +75,21 @@ settled by mongodb, defaults to `false`.
## Changelog ## Changelog
### Brief 1.1.0 changelog
* added support of passing promises objects which will be resolved with
pre-connected db object in options.db;
* fixed issue when events logged sometime between authorizeDb and
processOpQuery calls may be lost;
* renamed default log collection to 'log' because it makes more sense than
'logs' ('log' is a list of messages already, plural form would imply
multiple of such lists).
### Brief 1.0.0 changelog ### Brief 1.0.0 changelog
* migrated to mongodb 2.x driver; * migrated to mongodb 2.x driver;
* changed configuration format to MongoDB uri string; * changed configuration format to MongoDB uri string;
* added support of passing preconnected db object instead of MongoDB uri string; * added support of passing pre-connected db object instead of MongoDB uri string;
* added support of passing MongoDB connection parameters in options property; * added support of passing MongoDB connection parameters in options property;
* added support of replica sets through new options and db properties; * added support of replica sets through new options and db properties;
* migrated to [Semantic Versioning](http://semver.org/) in package versions names; * migrated to [Semantic Versioning](http://semver.org/) in package versions names;

Просмотреть файл

@ -63,7 +63,7 @@ var MongoDB = exports.MongoDB = function(options) {
} }
}; };
} }
this.collection = (options.collection || 'logs'); this.collection = (options.collection || 'log');
this.level = (options.level || 'info'); this.level = (options.level || 'info');
this.silent = options.silent; this.silent = options.silent;
this.username = options.username; this.username = options.username;
@ -77,47 +77,55 @@ var MongoDB = exports.MongoDB = function(options) {
this.hostname = os.hostname(); this.hostname = os.hostname();
} }
this._opQueue = [];
this._opQuery = [];
var self = this; var self = this;
function processOpQuery() { function setupDatabaseAndEmptyQueue(db) {
self._opQuery.forEach(function(operation) { authorizeDb(db, function(err, db) {
self[operation.method].apply(self, operation.args); createCollection(db, function(err, db) {
self.logDb = db;
processOpQueue();
});
}); });
delete self._opQuery;
} }
function createCollection(cb) { function processOpQueue() {
self._opQueue.forEach(function(operation) {
self[operation.method].apply(self, operation.args);
});
delete self._opQueue;
}
function createCollection(db, cb) {
var opts = {}; var opts = {};
if (self.capped) { if (self.capped) {
opts = {capped: true, size: self.cappedSize}; opts = {capped: true, size: self.cappedSize};
} }
self.mainDb.createCollection(self.collection, opts, function() { db.createCollection(self.collection, opts, function() {
cb(null); cb(null, db);
}); });
} }
function authorizeDb(cb) { function authorizeDb(db, cb) {
if (self.username && self.password) { if (self.username && self.password) {
self.mainDb.authenticate(self.username, self.password, db.authenticate(self.username, self.password,
function(err, result) { function(err, result) {
if (err) { if (err) {
console.error('winston-mongodb: error initialising logger', err); console.error('winston-mongodb: error initialising logger', err);
self.mainDb.close(); db.close();
return; return;
} }
if (!result) { if (!result) {
console.error('winston-mongodb: invalid username or password'); console.error('winston-mongodb: invalid username or password');
self.mainDb.close(); db.close();
return; return;
} }
cb(null); cb(null, db);
} }
); );
} }
cb(null); cb(null, db);
} }
if ('string' === typeof this.db) { if ('string' === typeof this.db) {
@ -126,16 +134,17 @@ var MongoDB = exports.MongoDB = function(options) {
console.error('winston-mongodb: error initialising logger', err); console.error('winston-mongodb: error initialising logger', err);
return; return;
} }
self.mainDb = db; setupDatabaseAndEmptyQueue(db);
authorizeDb(function() {
createCollection(processOpQuery);
}); });
} else if ('function' === typeof this.db.then) {
this.db.then(function(db) {
setupDatabaseAndEmptyQueue(db);
}, function(err) {
console.error(
'winston-mongodb: error initialising logger from promise', err);
}); });
} else { } else {
this.mainDb = this.db; setupDatabaseAndEmptyQueue(this.db);
authorizeDb(function() {
createCollection(processOpQuery);
});
} }
}; };
@ -161,8 +170,8 @@ winston.transports.MongoDB = MongoDB;
* @param {Function} callback Continuation to respond to when complete. * @param {Function} callback Continuation to respond to when complete.
*/ */
MongoDB.prototype.log = function(level, msg, opt_meta, callback) { MongoDB.prototype.log = function(level, msg, opt_meta, callback) {
if (!this.mainDb) { if (!this.logDb) {
this._opQuery.push({ this._opQueue.push({
method: 'log', method: 'log',
args: arguments args: arguments
}); });
@ -186,7 +195,7 @@ MongoDB.prototype.log = function(level, msg, opt_meta, callback) {
callback(err, null); callback(err, null);
} }
self.mainDb.collection(self.collection, function(err, col) { self.logDb.collection(self.collection, function(err, col) {
if (err) { if (err) {
onError(err); onError(err);
return; return;
@ -225,8 +234,8 @@ MongoDB.prototype.log = function(level, msg, opt_meta, callback) {
* @return {*} * @return {*}
*/ */
MongoDB.prototype.query = function(opt_options, callback) { MongoDB.prototype.query = function(opt_options, callback) {
if (!this.mainDb) { if (!this.logDb) {
this._opQuery.push({ this._opQueue.push({
method: 'query', method: 'query',
args: arguments args: arguments
}); });
@ -257,7 +266,7 @@ MongoDB.prototype.query = function(opt_options, callback) {
opt.fields = options.fields; opt.fields = options.fields;
} }
this.mainDb.collection(this.collection, function(err, col) { this.logDb.collection(this.collection, function(err, col) {
if (err) { if (err) {
callback(err); callback(err);
return; return;
@ -293,8 +302,8 @@ MongoDB.prototype.stream = function(options, stream) {
var self = this; var self = this;
var start = options.start; var start = options.start;
if (!this.mainDb) { if (!this.logDb) {
this._opQuery.push({ this._opQueue.push({
method: 'stream', method: 'stream',
args: [options, stream] args: [options, stream]
}); });
@ -310,7 +319,7 @@ MongoDB.prototype.stream = function(options, stream) {
} }
if (start != null) { if (start != null) {
this.mainDb.collection(this.collection, function(err, col) { this.logDb.collection(this.collection, function(err, col) {
if (err) { if (err) {
stream.emit('error', err); stream.emit('error', err);
return; return;
@ -334,7 +343,7 @@ MongoDB.prototype.stream = function(options, stream) {
return stream; return stream;
} }
this.mainDb.collection(this.collection, function(err, col) { this.logDb.collection(this.collection, function(err, col) {
if (err) { if (err) {
stream.emit('error', err); stream.emit('error', err);
return; return;
@ -387,8 +396,8 @@ MongoDB.prototype.streamPoll = function(options, stream) {
var start = options.start; var start = options.start;
var last; var last;
if (!this.mainDb) { if (!this.logDb) {
this._opQuery.push({ this._opQueue.push({
method: 'streamPoll', method: 'streamPoll',
args: [options, stream] args: [options, stream]
}); });
@ -408,7 +417,7 @@ MongoDB.prototype.streamPoll = function(options, stream) {
}; };
(function check() { (function check() {
self.mainDb.collection(self.collection, function(err, col) { self.logDb.collection(self.collection, function(err, col) {
if (err) { if (err) {
stream.emit('error', err); stream.emit('error', err);
return; return;

Просмотреть файл

@ -1,7 +1,7 @@
{ {
"name": "winston-mongodb", "name": "winston-mongodb",
"license": "MIT", "license": "MIT",
"version": "1.0.1", "version": "1.1.0",
"description": "A MongoDB transport for winston", "description": "A MongoDB transport for winston",
"author": "Charlie Robbins <charlie.robbins@gmail.com>", "author": "Charlie Robbins <charlie.robbins@gmail.com>",
"contributors": [ "contributors": [
@ -19,7 +19,8 @@
}, },
"devDependencies": { "devDependencies": {
"winston": "0.9.x", "winston": "0.9.x",
"vows": "0.8.x" "vows": "0.8.x",
"bluebird": "2.9.x"
}, },
"main": "./lib/winston-mongodb", "main": "./lib/winston-mongodb",
"scripts": { "test": "vows test/*-test.js --spec" } "scripts": { "test": "vows test/*-test.js --spec" }

Просмотреть файл

@ -7,6 +7,8 @@
*/ */
var vows = require('vows'); var vows = require('vows');
var mongodb = require('mongodb');
var Promise = require('bluebird');
var transport = require('winston/test/transports/transport'); var transport = require('winston/test/transports/transport');
var MongoDB = require('../lib/winston-mongodb').MongoDB; var MongoDB = require('../lib/winston-mongodb').MongoDB;
@ -14,10 +16,24 @@ vows.describe('winston-mongodb').addBatch({
'An instance of the MongoDB Transport': transport(MongoDB, { 'An instance of the MongoDB Transport': transport(MongoDB, {
db: 'mongodb://localhost/winston' db: 'mongodb://localhost/winston'
}), }),
'And instance of the MongoDB Transport on capped collection': 'An instance of the MongoDB Transport on capped collection':
transport(MongoDB, { transport(MongoDB, {
db: 'mongodb://localhost/winston', db: 'mongodb://localhost/winston',
capped: true, capped: true,
collection: 'cappedLogs' collection: 'cappedLog'
})
}).addBatch({
'An instance of the MongoDB Transport with promise':
transport(MongoDB, {
db: new Promise(function(resolve, reject) {
mongodb.MongoClient.connect('mongodb://localhost/winston',
function(err, db) {
if (err) {
reject(err);
} else {
resolve(db);
}
});
})
}) })
}).export(module); }).export(module);