Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

A few more fixes for better compatibility with the latest mongodb.

Keep connection as long as there is a stream of logs. Close on inactivity.
  • Loading branch information...
commit caa495765753d9036e8deea05fe1474e80bfe64e 1 parent 74caf00
Yosef Dinerstein yosefd authored

Showing 1 changed file with 68 additions and 51 deletions. Show diff stats Hide diff stats

  1. +68 51 lib/winston-mongodb.js
119 lib/winston-mongodb.js
@@ -31,18 +31,17 @@ var MongoDB = exports.MongoDB = function (options) {
31 31 this.silent = options.silent || false;
32 32 this.username = options.username || null;
33 33 this.password = options.password || null;
34   - this.timeout = options.timeout || 10000;
35   - if (!(options.keepAlive === true)) {
36   - this.keepAlive = false;
37   - if (!options.timeout && options.keepAlive) {
38   - // Backward compatibility for setting timeout via keepAlive.
39   - this.timeout = options.keepAlive;
40   - }
  34 + this.errortimeout = options.errortimeout || 10000;
  35 + if (options.keepAlive !== true) {
  36 + // Backward compatibility for timeout delivered in keepAlive parameter.
  37 + this.timeout = options.timeout || options.keepAlive || 10000;
41 38 }
42 39 this.state = 'unopened';
  40 + this.timeoutId = null;
43 41 this.pending = [];
44 42
45   - this.client = new mongodb.Db(this.db, new mongodb.Server(this.host, this.port, {}), {
  43 + this.server = new mongodb.Server(this.host, this.port, {});
  44 + this.client = new mongodb.Db(this.db, this.server, {
46 45 native_parser: false
47 46 });
48 47 };
@@ -69,42 +68,68 @@ winston.transports.MongoDB = MongoDB;
69 68 MongoDB.prototype.log = function (level, msg, meta, callback) {
70 69 var self = this;
71 70
72   - if (this.silent) {
73   - return callback(null, true);
74   - }
  71 + // Avoid reentrancy that can be not assumed by database code.
  72 + // If database logs, better not to call database itself in the same call.
  73 + process.nextTick(function () {
75 74
76   - this.open(function (err) {
77   - if (err) {
78   - self.emit('error', err);
79   - return callback(err, null);
  75 + if (self.silent) {
  76 + return callback(null, true);
80 77 }
81 78
82   - self._db.collection(self.collection, function (err, col) {
  79 + self.open(function (err) {
83 80 if (err) {
84 81 self.emit('error', err);
85 82 return callback(err, null);
86 83 }
87 84
88   - var entry = {
89   - timestamp: new Date(), // RFC3339/ISO8601 format instead of common.timestamp()
90   - level: level,
91   - message: msg,
92   - meta: meta
93   - };
94   -
95   - col.save(entry, { safe: self.safe }, function (err, doc) {
  85 + self._db.collection(self.collection, function (err, col) {
96 86 if (err) {
97 87 self.emit('error', err);
98 88 return callback(err, null);
99 89 }
100 90
101   - self.emit('logged');
102   - return callback(null, true);
  91 + var entry = {
  92 + timestamp: new Date(), // RFC3339/ISO8601 format instead of common.timestamp()
  93 + level: level,
  94 + message: msg,
  95 + meta: meta
  96 + };
  97 +
  98 + col.save(entry, { safe: self.safe }, function (err, doc) {
  99 + if (err) {
  100 + self.emit('error', err);
  101 + return callback(err, null);
  102 + }
  103 +
  104 + self.emit('logged');
  105 + // Delay timeout to start from the last successful log.
  106 + self.setTimeout();
  107 + return callback(null, true);
  108 + });
103 109 });
104 110 });
105 111 });
106 112 };
107 113
  114 +MongoDB.prototype.setTimeout = function () {
  115 + var self = this;
  116 + if (!self.timeout) {
  117 + return;
  118 + }
  119 + if (self.timeoutId) {
  120 + clearTimeout(self.timeoutId);
  121 + }
  122 + //
  123 + // Set a timeout to close the client connection unless `self.keepAlive`
  124 + // has been set to true in which case it is the responsibility of the
  125 + // programmer to close the underlying connection.
  126 + //
  127 + self.timeoutId = setTimeout(function () {
  128 + self.client.close();
  129 + self.state = 'unopened';
  130 + }, self.timeout);
  131 +}
  132 +
108 133 //
109 134 // ### function open (callback)
110 135 // #### @callback {function} Continuation to respond to when complete
@@ -114,25 +139,25 @@ MongoDB.prototype.log = function (level, msg, meta, callback) {
114 139 MongoDB.prototype.open = function (callback) {
115 140 var self = this;
116 141
117   - if (this.state === 'opening' || this.state === 'unopened') {
  142 + if (self.state === 'opening' || self.state === 'unopened') {
118 143 //
119 144 // While opening our MongoDB connection, append any callback
120 145 // to a list that is managed by this instance.
121 146 //
122   - this.pending.push(callback);
  147 + self.pending.push(callback);
123 148
124   - if (this.state === 'opening') {
  149 + if (self.state === 'opening') {
125 150 return;
126 151 }
127 152 }
128   - else if (this.state === 'opened') {
  153 + else if (self.state === 'opened') {
129 154 return callback();
130 155 }
131   - else if (this.state === 'error') {
  156 + else if (self.state === 'error') {
132 157 return callback(self.error);
133 158 }
134 159
135   - function completion(err) {
  160 + function flushCallbacks(err) {
136 161 //
137 162 // Iterate over all callbacks that have accumulated during
138 163 // the creation of the TCP socket.
@@ -143,37 +168,29 @@ MongoDB.prototype.open = function (callback) {
143 168
144 169 // Quickly truncate the Array (this is more performant).
145 170 self.pending.length = 0;
146   -
147   - //
148   - // Set a timeout to close the client connection unless `self.keepAlive`
149   - // has been set to true in which case it is the responsibility of the
150   - // programmer to close the underlying connection.
151   - //
152   - if (!self.keepAlive || err) {
153   - setTimeout(function () {
154   - self.state = 'unopened';
155   - if (self._db) {
156   - // Connection was opened.
157   - self._db.close();
158   - }
159   - }, self.timeout);
160   - }
161 171 }
162 172
163 173 function onError(err) {
164 174 self.state = 'error';
165 175 self.error = err;
166   - completion(err);
  176 + flushCallbacks(err);
  177 + // Close to be able to attempt opening later.
  178 + self.client.close();
  179 + // Retry new connection upon following request after error timeout expired.
  180 + setTimeout(function () {
  181 + self.state = 'unopened';
  182 + }, self.errortimeout);
167 183 }
168 184
169 185 function onSuccess(db) {
170 186 self.state = 'opened';
171 187 self._db = db;
172   - completion();
  188 + flushCallbacks();
  189 + self.setTimeout();
173 190 }
174 191
175   - this.state = 'opening';
176   - this.client.open(function (err, db) {
  192 + self.state = 'opening';
  193 + self.client.open(function (err, db) {
177 194 if (err) {
178 195 return onError(err);
179 196 }

0 comments on commit caa4957

Please sign in to comment.
Something went wrong with that request. Please try again.