diff --git a/lib/configs.js b/lib/configs.js index a89c087..98edd5a 100644 --- a/lib/configs.js +++ b/lib/configs.js @@ -21,4 +21,5 @@ module.exports = { , REQUEST_WITH_CREDENTIALS: false , BACKOFF_PERIOD: 3000 , FAILED_BUF_RETENTION_LIMIT: 10000000 + , RETRY_TIMES: 3 }; diff --git a/lib/logger.js b/lib/logger.js index f65fb33..3681bd7 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -101,6 +101,12 @@ function Logger(key, options) { this._flushLimit = configs.FLUSH_BYTE_LIMIT; } + if (options.retryTimes && Number.isInteger(options.retryTimes)) { + this._retryTimes = options.retryTimes; + } else { + this._retryTimes = configs.RETRY_TIMES; + } + if (options.flushInterval && Number.isInteger(options.flushInterval)) { this._flushInterval = options.flushInterval; } else { @@ -108,7 +114,7 @@ function Logger(key, options) { } if (options.retryTimeout && Number.isInteger(options.retryTimeout)) { - this._retryTimeout = options._retryTimeout; + this._retryTimeout = options.retryTimeout; } else { this._retryTimeout = configs.BACKOFF_PERIOD; } @@ -131,6 +137,7 @@ function Logger(key, options) { this._buf = []; this._meta = {}; this._isLoggingBackedOff = false; + this._attempts = 0; this.source = { hostname: options.hostname || os.hostname() @@ -313,6 +320,7 @@ Logger.prototype._sendRequest = function(config, instance) { .then((response) => { if (response && response.status === 200) { instance._isLoggingBackedOff = false; + instance._attempts = 0; return instance.callback(null, { lines: instance.__dbgLines , httpStatus: response.status @@ -324,8 +332,14 @@ Logger.prototype._sendRequest = function(config, instance) { .catch((err) => { if (err.response) instance._isLoggingBackedOff = true; // Return to buffer - instance._buf = JSON.parse(config.data).ls; - instance._bufByteLength = instance.__bufSizePreserve; + instance._buf = instance._buf.concat(JSON.parse(config.data).ls); + instance._bufByteLength += instance.__bufSizePreserve; + if (!instance._flusher && instance._attempts < instance._retryTimes) { + instance._attempts += 1; + instance._flusher = setTimeout(() => { + instance._flush(instance.callback); + }, instance._retryTimeout); + } let message = 'An error occured while making the request.'; if (err && err.response) { @@ -339,14 +353,15 @@ Logger.prototype._flush = function(callback) { if (!callback || typeof callback !== 'function') { throw new Error('flush function expects a callback'); } + + clearTimeout(this._flusher); + this._flusher = null; + if (this._buf.length === 0) { debug('Nothing to flush'); return callback(); } - clearTimeout(this._flusher); - this._flusher = null; - const data = stringify({ e: 'ls', ls: this._buf }); this._req.qs.now = Date.now(); @@ -374,7 +389,6 @@ Logger.prototype._flush = function(callback) { this._sendRequest(_config, this); }; - /* * Populate short-hand for each supported Log Level */ diff --git a/test/logger.js b/test/logger.js index 479b958..0a3f9b1 100644 --- a/test/logger.js +++ b/test/logger.js @@ -532,37 +532,43 @@ describe('ambient meta', function() { describe('HTTP Exception Handling', function() { let httpExcServer; - let countHits = 0; + let countServertHits = 0; let statusCode = 302; - let edgeCaseFlag = false; + let willEventuallySucceed = false; const port = 1336; + const retryTimeout = 500; + const retryTimes = 2; const options = testHelper.createOptions({ port: port + , retryTimeout + , retryTimes + }); let whenSuccessConnection = 0; beforeEach(function(done) { httpExcServer = http.createServer(function(req, res) { - if (edgeCaseFlag && countHits >= 1) { + if (willEventuallySucceed && countServertHits >= 1) { statusCode = 200; whenSuccessConnection = Date.now(); } res.writeHead(statusCode, {'Content-Type': 'text/plain'}); res.write('Hello World'); - res.end(() => {++countHits;}); + res.end(() => {++countServertHits;}); }); httpExcServer.on('listening', done); httpExcServer.listen(port); }); afterEach(function(done) { - countHits = 0; + countServertHits = 0; httpExcServer.close(); httpExcServer.on('close', function() { httpExcServer = null; done(); }); + willEventuallySucceed = false; sentMeta = []; body = ''; statusCode = 302; @@ -570,39 +576,50 @@ describe('HTTP Exception Handling', function() { }); const httpExcLogger = Logger.createLogger(testHelper.apikey, options); - it('when fails to connect, it should put the _isLoggingBackedOff flag on', function(done) { + it('when fails to connect, it should retry within retryTimeout period', function(done) { + this.timeout(retryTimeout * 3 + 400); + willEventuallySucceed = true; + const logSentTime = Date.now(); httpExcLogger.debug('The line'); setTimeout(function() { - assert(httpExcLogger._isLoggingBackedOff === true); + assert(whenSuccessConnection - logSentTime >= retryTimeout); + assert(httpExcLogger._buf.length === 0); + done(); + }, retryTimeout * 3 + 200); + }); + it('when fails to connect, it should retry only options.retryTimes and save the log until the next one comes in', function(done) { + this.timeout(retryTimeout * 4 + 400); + httpExcLogger.debug('The line'); + + setTimeout(function() { + assert(countServertHits === retryTimes + 1); assert(httpExcLogger._buf.length === 1); done(); - }, configs.FLUSH_INTERVAL + 200); + }, retryTimeout * 4); }); - it('*!!depends on the previouce test!!* Send the log after the previouse one has failed', function(done) { - this.timeout(3500); - edgeCaseFlag = true; - countHits = 1; + it('*!!depends on the previous test!!* Include the log from the previously failed flush', function(done) { + this.timeout(retryTimeout + 300); + willEventuallySucceed = true; + countServertHits = 1; const thisSendTime = Date.now(); httpExcLogger.debug('The second line'); + assert(httpExcLogger._buf.length === 2); setTimeout(function() { - assert(whenSuccessConnection - thisSendTime >= configs.BACKOFF_PERIOD); - assert(httpExcLogger._buf.length === 0); - assert(httpExcLogger._isLoggingBackedOff === false); + assert(whenSuccessConnection - thisSendTime >= retryTimeout); done(); - }, configs.BACKOFF_PERIOD + 200); + }, retryTimeout + 200); }); - it('*!!depends on the previouce test!!* Should clear backoff after success', function(done) { + it('*!!depends on the previous test!!* Should clear backoff after success and nullify attempts', function(done) { this.timeout(3500); - edgeCaseFlag = true; - countHits = 1; + willEventuallySucceed = true; + countServertHits = 1; const thisSendTime = Date.now(); httpExcLogger.debug('The second line'); setTimeout(function() { - assert(whenSuccessConnection - thisSendTime < configs.BACKOFF_PERIOD); - assert(httpExcLogger._buf.length === 0); - assert(httpExcLogger._isLoggingBackedOff === false); + assert(whenSuccessConnection - thisSendTime < retryTimeout); + assert(httpExcLogger._attempts === 0); done(); - }, configs.BACKOFF_PERIOD + 200); + }, retryTimeout + 200); }); it('should not exceed the failed buf retention limit', function(done) { this.timeout(3500); @@ -616,7 +633,7 @@ describe('HTTP Exception Handling', function() { const byteSizeOfBuf = sizeof(tooManyFails._buf[0]) * tooManyFails._buf.length; assert(byteSizeOfBuf <= opts.failedBufRetentionLimit); done(); - }, configs.BACKOFF_PERIOD + 200); + }, retryTimeout + 200); }); it('flushAll should recieve err message', function(done) { const opts = testHelper.createOptions({port: port}); diff --git a/test/testHelper.js b/test/testHelper.js index 482d1be..b1e8ed4 100644 --- a/test/testHelper.js +++ b/test/testHelper.js @@ -103,6 +103,7 @@ module.exports.createOptions = function({ , failedBufRetentionLimit = null , retryTimeout = null , flushInterval = null + , retryTimes = 3 , shimProperties } = {}) { return { @@ -116,6 +117,7 @@ module.exports.createOptions = function({ , failedBufRetentionLimit: failedBufRetentionLimit , retryTimeout: retryTimeout , flushInterval: flushInterval + , retryTimes , shimProperties }; };