From 4ef3855bc98b8dca7b5573432743c015b9665c1c Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Thu, 3 Mar 2016 15:45:26 +0530 Subject: [PATCH 1/9] added support for Bulk Mode and Created Mock test Cases --- .travis.yml | 8 +- lib/loggly/client.js | 11 +- lib/loggly/common.js | 94 ++++++++++++---- package.json | 10 +- test-as-mock/common-test.js | 32 ++++++ test-as-mock/common-test.js~ | 32 ++++++ test-as-mock/config.json | 8 ++ test-as-mock/config.json.enc | 1 + test-as-mock/customer-test.js | 43 +++++++ test-as-mock/helpers.js | 48 ++++++++ test-as-mock/log-test.js | 203 ++++++++++++++++++++++++++++++++++ test-as-mock/search-test.js | 195 ++++++++++++++++++++++++++++++++ 12 files changed, 645 insertions(+), 40 deletions(-) create mode 100644 test-as-mock/common-test.js create mode 100644 test-as-mock/common-test.js~ create mode 100644 test-as-mock/config.json create mode 100644 test-as-mock/config.json.enc create mode 100644 test-as-mock/customer-test.js create mode 100644 test-as-mock/helpers.js create mode 100644 test-as-mock/log-test.js create mode 100644 test-as-mock/search-test.js diff --git a/.travis.yml b/.travis.yml index 8c27bcb..8fd8ffe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,15 +8,9 @@ node_js: before_install: - travis_retry npm install -g npm@2.14.5 - travis_retry npm install --loglevel=http - - > - openssl aes-256-cbc \ - -K $encrypted_4fb8e306b739_key \ - -iv $encrypted_4fb8e306b739_iv \ - -in test/config.json.enc \ - -out test/config.json -d script: - - npm test + - npm run test-as-mock matrix: allow_failures: diff --git a/lib/loggly/client.js b/lib/loggly/client.js index 81b4838..b95f27f 100644 --- a/lib/loggly/client.js +++ b/lib/loggly/client.js @@ -46,15 +46,15 @@ var Loggly = exports.Loggly = function (options) { } events.EventEmitter.call(this); + this.subdomain = options.subdomain; this.token = options.token; this.host = options.host || 'logs-01.loggly.com'; this.json = options.json || null; this.auth = options.auth || null; - this.proxy = options.proxy || null; this.userAgent = 'node-loggly ' + loggly.version; this.useTagHeader = 'useTagHeader' in options ? options.useTagHeader : true; - + this.isBulk = options.isBulk || false; // // Set the tags on this instance. // @@ -101,7 +101,6 @@ Loggly.prototype.log = function (msg, tags, callback) { // Remark: Have some extra logic for detecting if we want to make a bulk // request to loggly // - var isBulk = Array.isArray(msg); function serialize(msg) { if (msg instanceof Object) { return self.json ? stringify(msg) : common.serialize(msg); @@ -111,13 +110,13 @@ Loggly.prototype.log = function (msg, tags, callback) { } } - msg = isBulk ? msg.map(serialize).join('\n') : serialize(msg); + msg = serialize(msg); logOptions = { - uri: isBulk ? this.urls.bulk : this.urls.log, + uri: this.isBulk ? this.urls.bulk : this.urls.log, method: 'POST', body: msg, - proxy: this.proxy, + isBulk: this.isBulk, headers: { host: this.host, accept: '*/*', diff --git a/lib/loggly/common.js b/lib/loggly/common.js index 633f715..aa9a625 100644 --- a/lib/loggly/common.js +++ b/lib/loggly/common.js @@ -6,6 +6,13 @@ * */ +// +// Variables for Bulk +// +var arrSize = 100, + arrMsg = [], + timerFunction = null; + var https = require('https'), util = require('util'), request = require('request'), @@ -68,7 +75,7 @@ common.loggly = function () { headers, method, auth, - proxy, + isBulk, uri; // @@ -88,8 +95,8 @@ common.loggly = function () { uri = args[0].uri; requestBody = args[0].body; auth = args[0].auth; + isBulk = args[0].isBulk; headers = args[0].headers; - proxy = args[0].proxy; } } else if (args.length === 2) { @@ -109,41 +116,80 @@ common.loggly = function () { if (callback) { callback(err) } } } - var requestOptions = { - uri: uri, + uri: isBulk ? uri + '/tag/' + headers['X-LOGGLY-TAG'] : uri, method: method, - headers: headers || {}, - proxy: proxy + headers: isBulk ? {} : headers || {} // Set headers empty for bulk }; if (auth) { requestOptions.headers.authorization = 'Basic ' + new Buffer(auth.username + ':' + auth.password).toString('base64'); } - if (requestBody) { requestOptions.body = requestBody; } - - try { - request(requestOptions, function (err, res, body) { - if (err) { - return onError(err); - } - - var statusCode = res.statusCode.toString(); - if (Object.keys(failCodes).indexOf(statusCode) !== -1) { - return onError((new Error('Loggly Error (' + statusCode + '): ' + failCodes[statusCode]))); - } - - success(res, body); - }); + function sendLogs() { + try { + request(requestOptions, function (err, res, body) { + if (err) { + return onError(err); + } + var statusCode = res.statusCode.toString(); + if (Object.keys(failCodes).indexOf(statusCode) !== -1) { + return onError((new Error('Loggly Error (' + statusCode + '): ' + failCodes[statusCode]))); + } + success(res, body); + }); + } + catch (ex) { + onError(ex); + } } - catch (ex) { - onError(ex); + function sendBulkLogs() { + if (arrMsg.length === 0) { + return; + } + // + // Join Array Message with new line ('\n') character + // + requestOptions.body = arrMsg.join('\n'); + try { + request(requestOptions, function (err, res, body) { + if (err) { + return onError(err); + } + var statusCode = res.statusCode.toString(); + if (Object.keys(failCodes).indexOf(statusCode) !== -1) { + return onError((new Error('Loggly Error (' + statusCode + '): ' + failCodes[statusCode]))); + } + success(res, body); + }); + } + catch (ex) { + onError(ex); + } + finally { + // + // Empty the array + // + arrMsg.length = 0; + } + } + if (isBulk === true) { + if (timerFunction === null) { + timerFunction = setInterval(function () { + sendBulkLogs(); + },30000); + } + arrMsg.push(requestBody); + if (arrMsg.length === arrSize) { + sendBulkLogs(); + } + } + else { + sendLogs(); } }; - // // ### function serialize (obj, key) // #### @obj {Object|literal} Object to serialize diff --git a/package.json b/package.json index 5282934..2eb0b42 100644 --- a/package.json +++ b/package.json @@ -16,16 +16,20 @@ "dependencies": { "request": "2.67.x", "timespan": "2.3.x", - "json-stringify-safe": "5.0.x" + "json-stringify-safe": "5.0.x", + "nock": "~7.2.2" }, "devDependencies": { "common-style": "^3.1.0", - "vows": "0.8.x" + "vows": "0.8.x", + "nock": "~7.2.2" }, "main": "./lib/loggly", "scripts": { "pretest": "common lib/**/*.js lib/*.js test/helpers.js", - "test": "vows test/*-test.js --spec" + "test": "vows test/*-test.js --spec", + "pretest-as-mock": "common lib/**/*.js lib/*.js test-as-mock/helpers.js", + "test-as-mock": "vows test-as-mock/*-test.js --spec" }, "license": "MIT", "engines": { diff --git a/test-as-mock/common-test.js b/test-as-mock/common-test.js new file mode 100644 index 0000000..ac5d3f4 --- /dev/null +++ b/test-as-mock/common-test.js @@ -0,0 +1,32 @@ +/* + * common-test.js: Tests for Loggly `common` utility module + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +var path = require('path'), + vows = require('vows'), + assert = require('assert'), + common = require('../lib/loggly/common'); + +vows.describe('node-loggly/common').addBatch({ + "When using the common module": { + "the clone() method": { + topic: function () { + this.obj = { + name: 'common', + deep: { + first: 'first', + second: 'second' + } + }; + return common.clone(this.obj); + }, + "should return a deep clone of the object": function (clone) { + assert.isFalse(this.obj.deep === clone.deep); + } + } + } +}).export(module); \ No newline at end of file diff --git a/test-as-mock/common-test.js~ b/test-as-mock/common-test.js~ new file mode 100644 index 0000000..ce49376 --- /dev/null +++ b/test-as-mock/common-test.js~ @@ -0,0 +1,32 @@ +/* + * common-test.js: Tests for Loggly `common` utility module + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +var path = require('path'), + vows = require('vows'), + assert = require('assert'), + common = require('.../lib/loggly/common'); + +vows.describe('node-loggly/common').addBatch({ + "When using the common module": { + "the clone() method": { + topic: function () { + this.obj = { + name: 'common', + deep: { + first: 'first', + second: 'second' + } + }; + return common.clone(this.obj); + }, + "should return a deep clone of the object": function (clone) { + assert.isFalse(this.obj.deep === clone.deep); + } + } + } +}).export(module); diff --git a/test-as-mock/config.json b/test-as-mock/config.json new file mode 100644 index 0000000..21ec805 --- /dev/null +++ b/test-as-mock/config.json @@ -0,0 +1,8 @@ +{ + "token": "test", + "subdomain": "testSubdomain", + "auth": { + "username": "test", + "password": "test" + } + } diff --git a/test-as-mock/config.json.enc b/test-as-mock/config.json.enc new file mode 100644 index 0000000..93242f8 --- /dev/null +++ b/test-as-mock/config.json.enc @@ -0,0 +1 @@ +�Sx̣��v5�j7�v���xC 1۟.j���aD4p�us����#,*a:�1��v~3�3V�!J ���VC�?���Ҿ�c-𕿄y���\7�)��S��%.�6h�@t�pЕu �-jV�W}m��~w�vX�AHF[�P�M��<��-��0c� F]҄�Z��#)rr�Y����`��# \ No newline at end of file diff --git a/test-as-mock/customer-test.js b/test-as-mock/customer-test.js new file mode 100644 index 0000000..15f1ca1 --- /dev/null +++ b/test-as-mock/customer-test.js @@ -0,0 +1,43 @@ +/* + * search-test.js: Tests for Loggly search requests + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ +var path = require('path'), + vows = require('vows'), + assert = require('assert'), + nock = require('nock'), + helpers = require('./helpers'); + +var options = {}, + testContext = {}, + config = helpers.loadConfig(), + loggly = require('../lib/loggly').createClient(config); + +vows.describe('node-loggly/customer').addBatch({ + "When using the node-loggly client": { + "the customer() method": { + topic: function() { + nock("https://" + config.subdomain + ".loggly.com") + .get('/apiv2/customer') + .reply(200, { + "tokens": ["test", "test2"], + "subdomain": config.subdomain, + "subscription": { + "key1": "value1" + } + }); + loggly.customer(this.callback); + + }, + "should return a valid customer": function(err, customer) { + assert.isNull(err); + assert.isArray(customer.tokens); + assert.isString(customer.subdomain); + assert.isObject(customer.subscription); + } + } + } +}).export(module); diff --git a/test-as-mock/helpers.js b/test-as-mock/helpers.js new file mode 100644 index 0000000..7ac92e0 --- /dev/null +++ b/test-as-mock/helpers.js @@ -0,0 +1,48 @@ +/* + * helpers.js: Test helpers for node-loggly + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +var fs = require('fs'), + util = require('util'), + path = require('path'), + vows = require('vows'), + assert = require('assert'), + loggly = require('../lib/loggly'); + +var helpers = exports; + +helpers.validConfig = function (config) { + return config + && config.subdomain !== 'test-subdomain' + && config.auth + && config.auth.username !== 'test-username' + && config.auth.password !== 'test-password' + && config.token; +}; + +helpers.loadConfig = function () { + try { + var config = require('./config'); + if (!helpers.validConfig(config)) { + throw new Error(util.format('test/config.json: invalid data %j', config)); + } + + helpers.config = config || {}; + return helpers.config; + } + catch (ex) { + console.log('Error parsing test/config.json'); + throw ex; + } +}; + +helpers.assertSearch = function (err, results) { + assert.isNull(err); + assert.isObject(results); + assert.isArray(results.events); + assert.isTrue(typeof results.total_events !== 'undefined'); +}; diff --git a/test-as-mock/log-test.js b/test-as-mock/log-test.js new file mode 100644 index 0000000..4caee30 --- /dev/null +++ b/test-as-mock/log-test.js @@ -0,0 +1,203 @@ +/* + * log-test.js: Tests for vanilla logging with no authentication. + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ +var path = require('path'), + vows = require('vows'), + assert = require('assert'), + nock = require('nock'), + helpers = require('./helpers'); + +var config = helpers.loadConfig(), + loggly = require('../lib/loggly').createClient({ + subdomain: config.subdomain, + token: config.token + }), + logglyJSON = require('../lib/loggly').createClient({ + subdomain: config.subdomain, + token: config.token, + json: true + }); + +vows.describe('node-loggly/inputs (no auth)').addBatch({ + "When using the node-loggly client without authentication": { + "the log() method": { + "to a 'text' input": { + "when passed a callback": { + topic: function() { + + nock("https://logs-01.loggly.com") + .post('/inputs/' + config.token) + .reply(200, { + "response": "ok" + }); + + loggly.log( + 'this is a test logging message from /test/input-test.js', + this.callback + ); + }, + "should log messages to loggly": function(err, result) { + assert.isNull(err); + assert.isObject(result); + assert.equal(result.response, 'ok'); + } + }, + } + } + } + }) + .addBatch({ + "When using the node-loggly client without authentication": { + "the log() method": { + "to a 'text' input": { + "when not passed a callback": { + topic: function() { + nock("https://logs-01.loggly.com") + .post('/inputs/' + config.token).reply(200, { + "response": "ok" + }); + loggly.log('this is a test logging message from /test/input-test.js'); + loggly.on('log', this.callback.bind(null, null)); + }, + "should log messages to loggly": function(err, result) { + assert.isNull(err); + assert.isObject(result); + assert.equal(result.response, 'ok'); + } + } + }, + } + } + }) + .addBatch({ + "When using the node-loggly client without authentication": { + "the log() method": { + "to a 'json' input": { + "when passed a callback": { + topic: function() { + + nock("https://logs-01.loggly.com") + .post('/inputs/' + config.token).reply(200, { + "response": "ok" + }); + logglyJSON.log({ + timestamp: new Date().getTime(), + message: 'this is a test logging message from /test/input-test.js' + }, this.callback); + }, + "should log messages to loggly": function(err, result) { + assert.isNull(err); + assert.isObject(result); + assert.equal(result.response, 'ok'); + } + } + } + } + } + }) + .addBatch({ + "When using the node-loggly client without authentication": { + "the log() method": { + "to a 'json' input": { + "when not passed a callback": { + topic: function() { + nock("https://logs-01.loggly.com") + .post('/inputs/' + config.token).reply(200, { + "response": "ok" + }); + logglyJSON.log({ + timestamp: new Date().getTime(), + message: 'this is a test logging message from /test/input-test.js' + }); + logglyJSON.on('log', this.callback.bind(null, null)); + }, + "should log messages to loggly": function(err, result) { + assert.isNull(err); + assert.isObject(result); + assert.equal(result.response, 'ok'); + } + } + } + } + } + }).addBatch({ + "When using the node-loggly client without authentication": { + "the log() method": { + "to a 'json' input with a single tag": { + "when not passed a callback": { + topic: function() { + nock("https://logs-01.loggly.com") + .post('/inputs/' + config.token).reply(200, { + "response": "ok" + }); + logglyJSON.log({ + timestamp: new Date().getTime(), + message: 'this is a test logging message from /test/input-test.js' + }, "WOOOO-TAG"); + logglyJSON.on('log', this.callback.bind(null, null)); + }, + "should log messages to loggly": function(err, result) { + assert.isNull(err); + assert.isObject(result); + assert.equal(result.response, 'ok'); + } + } + } + } + } + }).addBatch({ + "When using the node-loggly client without authentication": { + "the log() method": { + "to a 'json' input with tags that exist as an array": { + "when not passed a callback": { + topic: function() { + nock("https://logs-01.loggly.com") + .post('/inputs/' + config.token).reply(200, { + "response": "ok" + }); + logglyJSON.log({ + timestamp: new Date().getTime(), + message: 'this is a test logging message from /test/input-test.js' + }, ["tag", "tag2"]); + logglyJSON.on('log', this.callback.bind(null, null)); + }, + "should log messages to loggly": function(err, result) { + assert.isNull(err); + assert.isObject(result); + assert.equal(result.response, 'ok'); + } + } + } + } + } + }).addBatch({ + "When using the node-loggly client without authentication": { + "the log() method": { + "takes an array": { + "when not passed a callback": { + topic: function() { + nock("https://logs-01.loggly.com") + .post('/inputs/' + config.token).reply(200, { + "response": "ok" + }); + logglyJSON.log([{ + work: 'it harder' + }, { + make: 'it better' + }]); + logglyJSON.on('log', this.callback.bind(null, null)); + }, + "should log messages to loggly": function(err, result) { + assert.isNull(err); + assert.isObject(result); + assert.equal(result.response, 'ok'); + } + } + } + } + } + }).export(module); diff --git a/test-as-mock/search-test.js b/test-as-mock/search-test.js new file mode 100644 index 0000000..27d3e9f --- /dev/null +++ b/test-as-mock/search-test.js @@ -0,0 +1,195 @@ +/* + * search-test.js: Tests for Loggly search requests + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ +var path = require('path'), + vows = require('vows'), + nock = require('nock'), + assert = require('assert'), + helpers = require('./helpers'); + +var options = {}, + testContext = {}, + config = helpers.loadConfig(), + loggly = require('../lib/loggly').createClient(config); + +vows.describe('node-loggly/search').addBatch({ + "When using the node-loggly client": { + "the search() method": { + "when searching without chaining": { + topic: function() { + nock("https://" + config.subdomain + ".loggly.com", { + reqheaders: { + 'authorization': 'Basic ' + new Buffer(config.auth.username + ":" + config.auth.password).toString('base64') + } + }) + .get('/apiv2/search') + .query({ + q: 'logging message' + }) + .reply(200, { + total_events: 1, + page: 0, + events: [{ + raw: 'this is a test logging message from /test/input-test.js', + logtypes: [], + timestamp: 1456830373968, + unparsed: null, + logmsg: 'this is a test logging message from /test/input-test.js', + id: '9ce38479-df9d-11e5-802c-12a650209768', + tags: [], + event: {} + }, ], + callback: '', + rsid: { + status: 'SCHEDULED', + date_from: 1455971607000, + elapsed_time: 0.026120901107788086, + date_to: 1456835607000, + id: '897886661' + } + + }); + nock("https://" + config.subdomain + ".loggly.com", { + reqheaders: { + 'authorization': 'Basic ' + new Buffer(config.auth.username + ":" + config.auth.password).toString('base64') + } + }) + .get('/apiv2/events') + .query({ + rsid: '897886661' + }) + .reply(200, { + total_events: 1, + page: 0, + events: [{ + raw: 'this is a test logging message from /test/input-test.js', + logtypes: [], + timestamp: 1456830373968, + unparsed: null, + logmsg: 'this is a test logging message from /test/input-test.js', + id: '9ce38479-df9d-11e5-802c-12a650209768', + tags: [], + event: {} + }, ], + callback: '', + rsid: { + status: 'SCHEDULED', + date_from: 1455971607000, + elapsed_time: 0.026120901107788086, + date_to: 1456835607000, + id: '897886661' + } + + }); + loggly.search('logging message', this.callback) + }, + "should return a set of valid search results": function(err, results) { + helpers.assertSearch(err, results); + } + }, + "when searching with chaining": { + topic: function() { + nock("https://" + config.subdomain + ".loggly.com", { + reqheaders: { + 'authorization': 'Basic ' + new Buffer(config.auth.username + ":" + config.auth.password).toString('base64') + } + }) + .get('/apiv2/search') + .query({ + q: 'logging message', + callback: '' + }) + .reply(200, { + total_events: 1, + page: 0, + events: [{ + raw: 'this is a test logging message from /test/input-test.js', + logtypes: [], + timestamp: 1456830373968, + unparsed: null, + logmsg: 'this is a test logging message from /test/input-test.js', + id: '9ce38479-df9d-11e5-802c-12a650209768', + tags: [], + event: {} + }], + callback: '', + rsid: { + status: 'SCHEDULED', + date_from: 1455971607000, + elapsed_time: 0.026120901107788086, + date_to: 1456835607000, + id: '897886661' + } + }); + nock("https://" + config.subdomain + ".loggly.com", { + reqheaders: { + 'authorization': 'Basic ' + new Buffer(config.auth.username + ":" + config.auth.password).toString('base64') + } + }).get('/apiv2/events') + .query({ + rsid: '897886661' + }) + .reply(200, { + total_events: 1, + page: 0, + events: [{ + raw: 'this is a test logging message from /test/input-test.js', + logtypes: [], + timestamp: 1456830373968, + unparsed: null, + logmsg: 'this is a test logging message from /test/input-test.js', + id: '9ce38479-df9d-11e5-802c-12a650209768', + tags: [], + event: {} + }], + callback: '', + rsid: { + status: 'SCHEDULED', + date_from: 1455971607000, + elapsed_time: 0.026120901107788086, + date_to: 1456835607000, + id: '' + } + }); + loggly.search('logging message') + .run(this.callback); + }, + "should return a set of valid search results": function(err, results) { + helpers.assertSearch(err, results); + } + } + }, + "the _checkRange() method": { + "with invalid options set": { + "should correct them": function() { + var search = loggly.search({ + query: 'invalid logging message', + from: 'now', + until: '-1d' + }) + ._checkRange(); + + assert.equal(search.options.from, 'now'); + assert.equal(search.options.until, '-1d'); + } + }, + "with valid options set": { + "should not modify them": function() { + var search = loggly.search({ + query: 'valid logging message', + from: '-2M', + until: 'now' + }) + ._checkRange(); + + assert.equal(search.options.from, '-2M'); + assert.equal(search.options.until, 'now'); + } + } + } + } +}).export(module); From ba24bf42fe77286ae17ed351cc1bee9b809bd4e0 Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Thu, 3 Mar 2016 16:03:19 +0530 Subject: [PATCH 2/9] remove encrypted config file --- test/config.json.enc | 1 - test/data/test-config.json | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) delete mode 100644 test/config.json.enc create mode 100644 test/data/test-config.json diff --git a/test/config.json.enc b/test/config.json.enc deleted file mode 100644 index c188c20..0000000 --- a/test/config.json.enc +++ /dev/null @@ -1 +0,0 @@ -N�4�Q��/�#m8ێ�d��CU�l�����]����JQ�Y��R�H�����P�[�h�`0�܆��S�_�SzX>0?�q<�Y�+�%?5��t]�H�g��V]5*�$��Z㈮T���׹�Ep<�pf�@��a�?�ߨ��A&.t�޷4�HN+�[�lc� \ No newline at end of file diff --git a/test/data/test-config.json b/test/data/test-config.json new file mode 100644 index 0000000..21ec805 --- /dev/null +++ b/test/data/test-config.json @@ -0,0 +1,8 @@ +{ + "token": "test", + "subdomain": "testSubdomain", + "auth": { + "username": "test", + "password": "test" + } + } From 7becdb8d06c0c06ead3eaa94f91ecbff6a85470b Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Thu, 3 Mar 2016 17:54:35 +0530 Subject: [PATCH 3/9] Added support for bulk mode --- lib/loggly/client.js | 2 + lib/loggly/client.js~ | 250 +++++++++++++++++++++++++++++++++++ lib/loggly/common.js | 5 +- lib/loggly/common.js~ | 259 +++++++++++++++++++++++++++++++++++++ test/config.json~ | 8 ++ test/data/test-config.json | 8 -- 6 files changed, 523 insertions(+), 9 deletions(-) create mode 100644 lib/loggly/client.js~ create mode 100644 lib/loggly/common.js~ create mode 100644 test/config.json~ delete mode 100644 test/data/test-config.json diff --git a/lib/loggly/client.js b/lib/loggly/client.js index b95f27f..9f18f30 100644 --- a/lib/loggly/client.js +++ b/lib/loggly/client.js @@ -52,6 +52,7 @@ var Loggly = exports.Loggly = function (options) { this.host = options.host || 'logs-01.loggly.com'; this.json = options.json || null; this.auth = options.auth || null; + this.proxy = options.proxy || null; this.userAgent = 'node-loggly ' + loggly.version; this.useTagHeader = 'useTagHeader' in options ? options.useTagHeader : true; this.isBulk = options.isBulk || false; @@ -116,6 +117,7 @@ Loggly.prototype.log = function (msg, tags, callback) { uri: this.isBulk ? this.urls.bulk : this.urls.log, method: 'POST', body: msg, + proxy: this.proxy, isBulk: this.isBulk, headers: { host: this.host, diff --git a/lib/loggly/client.js~ b/lib/loggly/client.js~ new file mode 100644 index 0000000..9f18f30 --- /dev/null +++ b/lib/loggly/client.js~ @@ -0,0 +1,250 @@ +/* + * client.js: Core client functions for accessing Loggly + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +var events = require('events'), + util = require('util'), + qs = require('querystring'), + common = require('./common'), + loggly = require('../loggly'), + Search = require('./search').Search, + stringifySafe = require('json-stringify-safe'); + +function stringify(msg) { + var payload; + + try { payload = JSON.stringify(msg) } + catch (ex) { payload = stringifySafe(msg, null, null, noop) } + + return payload; +} +// +// function createClient (options) +// Creates a new instance of a Loggly client. +// +exports.createClient = function (options) { + return new Loggly(options); +}; + +// +// ### function Loggly (options) +// #### @options {Object} Options for this Loggly client +// #### @subdomain +// #### @token +// #### @json +// #### @auth +// #### @tags +// Constructor for the Loggly object +// +var Loggly = exports.Loggly = function (options) { + if (!options || !options.subdomain || !options.token) { + throw new Error('options.subdomain and options.token are required.'); + } + + events.EventEmitter.call(this); + + this.subdomain = options.subdomain; + this.token = options.token; + this.host = options.host || 'logs-01.loggly.com'; + this.json = options.json || null; + this.auth = options.auth || null; + this.proxy = options.proxy || null; + this.userAgent = 'node-loggly ' + loggly.version; + this.useTagHeader = 'useTagHeader' in options ? options.useTagHeader : true; + this.isBulk = options.isBulk || false; + // + // Set the tags on this instance. + // + this.tags = options.tags + ? this.tagFilter(options.tags) + : null; + + var url = 'https://' + this.host, + api = options.api || 'apiv2'; + + this.urls = { + default: url, + log: [url, 'inputs', this.token].join('/'), + bulk: [url, 'bulk', this.token].join('/'), + api: 'https://' + [this.subdomain, 'loggly', 'com'].join('.') + '/' + api + }; +}; + +// +// Inherit from events.EventEmitter +// +util.inherits(Loggly, events.EventEmitter); + +// +// ### function log (msg, tags, callback) +// #### @msg {string|Object} Data to log +// #### @tags {Array} **Optional** Tags to send with this msg +// #### @callback {function} Continuation to respond to when complete. +// Logs the message to the token associated with this instance. If +// the message is an Object we will attempt to serialize it. If any +// `tags` are supplied they will be passed via the `X-LOGGLY-TAG` header. +// - http://www.loggly.com/docs/api-sending-data/ +// +Loggly.prototype.log = function (msg, tags, callback) { + if (!callback && typeof tags === 'function') { + callback = tags; + tags = null; + } + + var self = this, + logOptions; + + // + // Remark: Have some extra logic for detecting if we want to make a bulk + // request to loggly + // + function serialize(msg) { + if (msg instanceof Object) { + return self.json ? stringify(msg) : common.serialize(msg); + } + else { + return self.json ? stringify({ message: msg }) : msg; + } + } + + msg = serialize(msg); + + logOptions = { + uri: this.isBulk ? this.urls.bulk : this.urls.log, + method: 'POST', + body: msg, + proxy: this.proxy, + isBulk: this.isBulk, + headers: { + host: this.host, + accept: '*/*', + 'user-agent': this.userAgent, + 'content-type': this.json ? 'application/json' : 'text/plain', + 'content-length': Buffer.byteLength(msg) + } + }; + + // + // Remark: if tags are passed in run the filter on them and concat + // with any tags that were passed or just use default tags if they exist + // + tags = tags + ? (this.tags ? this.tags.concat(this.tagFilter(tags)) : this.tagFilter(tags)) + : this.tags; + + // + // Optionally send `X-LOGGLY-TAG` if we have them + // + if (tags) { + // Decide whether to add tags as http headers or add them to the URI. + if (this.useTagHeader) { + logOptions.headers['X-LOGGLY-TAG'] = tags.join(','); + } + else { + logOptions.uri += '/tag/' + tags.join(',') + '/'; + } + } + + common.loggly(logOptions, callback, function (res, body) { + try { + var result = JSON.parse(body); + self.emit('log', result); + if (callback) { + callback(null, result); + } + } + catch (ex) { + if (callback) { + callback(new Error('Unspecified error from Loggly: ' + ex)); + } + } + }); + + return this; +}; + +// +// ### function tag (tags) +// #### @tags {Array} Tags to use for `X-LOGGLY-TAG` +// Sets the tags on this instance +// +Loggly.prototype.tagFilter = function (tags) { + var isSolid = /^[\w\d][\w\d-_.]+/; + + tags = !Array.isArray(tags) + ? [tags] + : tags; + + // + // TODO: Filter against valid tag names with some Regex + // http://www.loggly.com/docs/tags/ + // Remark: Docs make me think we dont need this but whatevs + // + return tags.filter(function (tag) { + // + // Remark: length may need to use Buffer.byteLength? + // + return isSolid.test(tag) && tag.length <= 64; + }); +}; + +// +// ### function customer (callback) +// ### @callback {function} Continuation to respond to. +// Retrieves the customer information from the Loggly API: +// - http://www.loggly.com/docs/api-account-info/ +// +Loggly.prototype.customer = function (callback) { + common.loggly({ + uri: this.logglyUrl('customer'), + auth: this.auth + }, callback, function (res, body) { + var customer; + try { customer = JSON.parse(body) } + catch (ex) { return callback(ex) } + callback(null, customer); + }); +}; + +// +// function search (query, callback) +// Returns a new search object which can be chained +// with options or called directly if @callback is passed +// initially. +// +// Sample Usage: +// +// client.search('404', function () { /* ... */ }) +// .on('rsid', function (rsid) { /* ... */ }) +// +// client.search({ query: '404', rows: 100 }) +// .on('rsid', function (rsid) { /* ... */ }) +// .run(function () { /* ... */ }); +// +Loggly.prototype.search = function (query, callback) { + var options = typeof query === 'string' + ? { query: query } + : query; + + options.callback = callback; + return new Search(options, this); +}; + +// +// function logglyUrl ([path, to, resource]) +// Helper method that concats the string params into a url +// to request against a loggly serverUrl. +// +Loggly.prototype.logglyUrl = function (/* path, to, resource */) { + var args = Array.prototype.slice.call(arguments); + return [this.urls.api].concat(args).join('/'); +}; + +// +// Simple noop function for reusability +// +function noop() {} diff --git a/lib/loggly/common.js b/lib/loggly/common.js index aa9a625..785563d 100644 --- a/lib/loggly/common.js +++ b/lib/loggly/common.js @@ -75,6 +75,7 @@ common.loggly = function () { headers, method, auth, + proxy, isBulk, uri; @@ -97,6 +98,7 @@ common.loggly = function () { auth = args[0].auth; isBulk = args[0].isBulk; headers = args[0].headers; + proxy = args[0].proxy; } } else if (args.length === 2) { @@ -119,7 +121,8 @@ common.loggly = function () { var requestOptions = { uri: isBulk ? uri + '/tag/' + headers['X-LOGGLY-TAG'] : uri, method: method, - headers: isBulk ? {} : headers || {} // Set headers empty for bulk + headers: isBulk ? {} : headers || {}, // Set headers empty for bulk + proxy: proxy }; if (auth) { diff --git a/lib/loggly/common.js~ b/lib/loggly/common.js~ new file mode 100644 index 0000000..9235408 --- /dev/null +++ b/lib/loggly/common.js~ @@ -0,0 +1,259 @@ +/* + * common.js: Common utility functions for requesting against Loggly APIs + * + * (C) 2010 Charlie Robbins + * MIT LICENSE + * + */ + +// +// Variables for Bulk +// +var arrSize = 100, + arrMsg = [], + timerFunction = null; + +var https = require('https'), + util = require('util'), + request = require('request'), + loggly = require('../loggly'); + +var common = exports; + +// +// Failure HTTP Response codes based +// off Loggly specification. +// +var failCodes = common.failCodes = { + 400: 'Bad Request', + 401: 'Unauthorized', + 403: 'Forbidden', + 404: 'Not Found', + 409: 'Conflict / Duplicate', + 410: 'Gone', + 500: 'Internal Server Error', + 501: 'Not Implemented', + 503: 'Throttled' +}; + +// +// Success HTTP Response codes based +// off Loggly specification. +// +var successCodes = common.successCodes = { + 200: 'OK', + 201: 'Created', + 202: 'Accepted', + 203: 'Non-authoritative information', + 204: 'Deleted' +}; + +// +// Core method that actually sends requests to Loggly. +// This method is designed to be flexible w.r.t. arguments +// and continuation passing given the wide range of different +// requests required to fully implement the Loggly API. +// +// Continuations: +// 1. 'callback': The callback passed into every node-loggly method +// 2. 'success': A callback that will only be called on successful requests. +// This is used throughout node-loggly to conditionally +// do post-request processing such as JSON parsing. +// +// Possible Arguments (1 & 2 are equivalent): +// 1. common.loggly('some-fully-qualified-url', auth, callback, success) +// 2. common.loggly('GET', 'some-fully-qualified-url', auth, callback, success) +// 3. common.loggly('DELETE', 'some-fully-qualified-url', auth, callback, success) +// 4. common.loggly({ method: 'POST', uri: 'some-url', body: { some: 'body'} }, callback, success) +// +common.loggly = function () { + var args = Array.prototype.slice.call(arguments), + success = args.pop(), + callback = args.pop(), + responded, + requestBody, + headers, + method, + auth, + proxy, + isBulk, + uri; + + // + // Now that we've popped off the two callbacks + // We can make decisions about other arguments + // + if (args.length === 1) { + if (typeof args[0] === 'string') { + // + // If we got a string assume that it's the URI + // + method = 'GET'; + uri = args[0]; + } + else { + method = args[0].method || 'GET'; + uri = args[0].uri; + requestBody = args[0].body; + auth = args[0].auth; + isBulk = args[0].isBulk; + headers = args[0].headers; + proxy = args[0].proxy; + } + } + else if (args.length === 2) { + method = 'GET'; + uri = args[0]; + auth = args[1]; + } + else { + method = args[0]; + uri = args[1]; + auth = args[2]; + } + + function onError(err) { + if (!responded) { + responded = true; + if (callback) { callback(err) } + } + } + var requestOptions = { + uri: isBulk ? uri + '/tag/' + headers['X-LOGGLY-TAG'] : uri, + method: method, + headers: isBulk ? {} : headers || {} // Set headers empty for bulk + proxy: proxy + }; + + if (auth) { + requestOptions.headers.authorization = 'Basic ' + new Buffer(auth.username + ':' + auth.password).toString('base64'); + } + if (requestBody) { + requestOptions.body = requestBody; + } + function sendLogs() { + try { + request(requestOptions, function (err, res, body) { + if (err) { + return onError(err); + } + var statusCode = res.statusCode.toString(); + if (Object.keys(failCodes).indexOf(statusCode) !== -1) { + return onError((new Error('Loggly Error (' + statusCode + '): ' + failCodes[statusCode]))); + } + success(res, body); + }); + } + catch (ex) { + onError(ex); + } + } + function sendBulkLogs() { + if (arrMsg.length === 0) { + return; + } + // + // Join Array Message with new line ('\n') character + // + requestOptions.body = arrMsg.join('\n'); + try { + request(requestOptions, function (err, res, body) { + if (err) { + return onError(err); + } + var statusCode = res.statusCode.toString(); + if (Object.keys(failCodes).indexOf(statusCode) !== -1) { + return onError((new Error('Loggly Error (' + statusCode + '): ' + failCodes[statusCode]))); + } + success(res, body); + }); + } + catch (ex) { + onError(ex); + } + finally { + // + // Empty the array + // + arrMsg.length = 0; + } + } + if (isBulk === true) { + if (timerFunction === null) { + timerFunction = setInterval(function () { + sendBulkLogs(); + },30000); + } + arrMsg.push(requestBody); + if (arrMsg.length === arrSize) { + sendBulkLogs(); + } + } + else { + sendLogs(); + } +}; +// +// ### function serialize (obj, key) +// #### @obj {Object|literal} Object to serialize +// #### @key {string} **Optional** Optional key represented by obj in a larger object +// Performs simple comma-separated, `key=value` serialization for Loggly when +// logging for non-JSON values. +// +common.serialize = function (obj, key) { + if (obj === null) { + obj = 'null'; + } + else if (obj === undefined) { + obj = 'undefined'; + } + else if (obj === false) { + obj = 'false'; + } + + if (typeof obj !== 'object') { + return key ? key + '=' + obj : obj; + } + + var msg = '', + keys = Object.keys(obj), + length = keys.length; + + for (var i = 0; i < length; i++) { + if (Array.isArray(obj[keys[i]])) { + msg += keys[i] + '=['; + + for (var j = 0, l = obj[keys[i]].length; j < l; j++) { + msg += common.serialize(obj[keys[i]][j]); + if (j < l - 1) { + msg += ', '; + } + } + + msg += ']'; + } + else { + msg += common.serialize(obj[keys[i]], keys[i]); + } + + if (i < length - 1) { + msg += ', '; + } + } + + return msg; +}; + +// +// function clone (obj) +// Helper method for deep cloning pure JSON objects +// i.e. JSON objects that are either literals or objects (no Arrays, etc) +// +common.clone = function (obj) { + var clone = {}; + for (var i in obj) { + clone[i] = obj[i] instanceof Object ? common.clone(obj[i]) : obj[i]; + } + + return clone; +}; diff --git a/test/config.json~ b/test/config.json~ new file mode 100644 index 0000000..5367486 --- /dev/null +++ b/test/config.json~ @@ -0,0 +1,8 @@ + { + "token": "your-really-long-token-you-got-when-you-created-an-http-input", + "subdomain": "your-subdomain", + "auth": { + "username": "your-username", + "password": "your-password" + } + } diff --git a/test/data/test-config.json b/test/data/test-config.json deleted file mode 100644 index 21ec805..0000000 --- a/test/data/test-config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "token": "test", - "subdomain": "testSubdomain", - "auth": { - "username": "test", - "password": "test" - } - } From 43b348323219f60829770f171e3defef6ffa4073 Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Thu, 3 Mar 2016 17:59:40 +0530 Subject: [PATCH 4/9] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56dca1d..a20c242 100644 --- a/README.md +++ b/README.md @@ -158,11 +158,11 @@ See the [Loggly search guide][search] for more details on how to effectively sea ``` ## Run Tests -All of the node-loggly tests are written in [vows][vows], and cover all of the use cases described above. You will need to add your Loggly username, password, subdomain, and a two test inputs to test/data/test-config.json before running tests. When configuring the test inputs on Loggly, the first test input should be named 'test' using the HTTP service. The second input should be name 'test_json' using the HTTP service with the JSON logging option enabled: +All of the node-loggly tests are written in [vows][vows], and cover all of the use cases described above. You will need to add your Loggly username, password, subdomain, and you loggly token to test/config.json before running tests. ``` js { - "token": "your-really-long-token-you-got-when-you-created-an-http-input", + "token": "your-loggly-token", "subdomain": "your-subdomain", "auth": { "username": "your-username", From 775f78492477ada21805bce6d5272a12d5bf3322 Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Thu, 3 Mar 2016 18:02:30 +0530 Subject: [PATCH 5/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a20c242..a8a5ca7 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ See the [Loggly search guide][search] for more details on how to effectively sea ``` ## Run Tests -All of the node-loggly tests are written in [vows][vows], and cover all of the use cases described above. You will need to add your Loggly username, password, subdomain, and you loggly token to test/config.json before running tests. +All of the node-loggly tests are written in [vows][vows], and cover all of the use cases described above. You will need to add your Loggly username, password, subdomain, and your loggly token to test/config.json before running tests. ``` js { From 3c3a216fabe0d9b417695451853d6bc0a917f8d0 Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Thu, 3 Mar 2016 18:09:51 +0530 Subject: [PATCH 6/9] removed unwanted files --- lib/loggly/client.js~ | 250 --------------------------------- lib/loggly/common.js~ | 259 ----------------------------------- test-as-mock/common-test.js~ | 32 ----- test-as-mock/config.json.enc | 1 - 4 files changed, 542 deletions(-) delete mode 100644 lib/loggly/client.js~ delete mode 100644 lib/loggly/common.js~ delete mode 100644 test-as-mock/common-test.js~ delete mode 100644 test-as-mock/config.json.enc diff --git a/lib/loggly/client.js~ b/lib/loggly/client.js~ deleted file mode 100644 index 9f18f30..0000000 --- a/lib/loggly/client.js~ +++ /dev/null @@ -1,250 +0,0 @@ -/* - * client.js: Core client functions for accessing Loggly - * - * (C) 2010 Charlie Robbins - * MIT LICENSE - * - */ - -var events = require('events'), - util = require('util'), - qs = require('querystring'), - common = require('./common'), - loggly = require('../loggly'), - Search = require('./search').Search, - stringifySafe = require('json-stringify-safe'); - -function stringify(msg) { - var payload; - - try { payload = JSON.stringify(msg) } - catch (ex) { payload = stringifySafe(msg, null, null, noop) } - - return payload; -} -// -// function createClient (options) -// Creates a new instance of a Loggly client. -// -exports.createClient = function (options) { - return new Loggly(options); -}; - -// -// ### function Loggly (options) -// #### @options {Object} Options for this Loggly client -// #### @subdomain -// #### @token -// #### @json -// #### @auth -// #### @tags -// Constructor for the Loggly object -// -var Loggly = exports.Loggly = function (options) { - if (!options || !options.subdomain || !options.token) { - throw new Error('options.subdomain and options.token are required.'); - } - - events.EventEmitter.call(this); - - this.subdomain = options.subdomain; - this.token = options.token; - this.host = options.host || 'logs-01.loggly.com'; - this.json = options.json || null; - this.auth = options.auth || null; - this.proxy = options.proxy || null; - this.userAgent = 'node-loggly ' + loggly.version; - this.useTagHeader = 'useTagHeader' in options ? options.useTagHeader : true; - this.isBulk = options.isBulk || false; - // - // Set the tags on this instance. - // - this.tags = options.tags - ? this.tagFilter(options.tags) - : null; - - var url = 'https://' + this.host, - api = options.api || 'apiv2'; - - this.urls = { - default: url, - log: [url, 'inputs', this.token].join('/'), - bulk: [url, 'bulk', this.token].join('/'), - api: 'https://' + [this.subdomain, 'loggly', 'com'].join('.') + '/' + api - }; -}; - -// -// Inherit from events.EventEmitter -// -util.inherits(Loggly, events.EventEmitter); - -// -// ### function log (msg, tags, callback) -// #### @msg {string|Object} Data to log -// #### @tags {Array} **Optional** Tags to send with this msg -// #### @callback {function} Continuation to respond to when complete. -// Logs the message to the token associated with this instance. If -// the message is an Object we will attempt to serialize it. If any -// `tags` are supplied they will be passed via the `X-LOGGLY-TAG` header. -// - http://www.loggly.com/docs/api-sending-data/ -// -Loggly.prototype.log = function (msg, tags, callback) { - if (!callback && typeof tags === 'function') { - callback = tags; - tags = null; - } - - var self = this, - logOptions; - - // - // Remark: Have some extra logic for detecting if we want to make a bulk - // request to loggly - // - function serialize(msg) { - if (msg instanceof Object) { - return self.json ? stringify(msg) : common.serialize(msg); - } - else { - return self.json ? stringify({ message: msg }) : msg; - } - } - - msg = serialize(msg); - - logOptions = { - uri: this.isBulk ? this.urls.bulk : this.urls.log, - method: 'POST', - body: msg, - proxy: this.proxy, - isBulk: this.isBulk, - headers: { - host: this.host, - accept: '*/*', - 'user-agent': this.userAgent, - 'content-type': this.json ? 'application/json' : 'text/plain', - 'content-length': Buffer.byteLength(msg) - } - }; - - // - // Remark: if tags are passed in run the filter on them and concat - // with any tags that were passed or just use default tags if they exist - // - tags = tags - ? (this.tags ? this.tags.concat(this.tagFilter(tags)) : this.tagFilter(tags)) - : this.tags; - - // - // Optionally send `X-LOGGLY-TAG` if we have them - // - if (tags) { - // Decide whether to add tags as http headers or add them to the URI. - if (this.useTagHeader) { - logOptions.headers['X-LOGGLY-TAG'] = tags.join(','); - } - else { - logOptions.uri += '/tag/' + tags.join(',') + '/'; - } - } - - common.loggly(logOptions, callback, function (res, body) { - try { - var result = JSON.parse(body); - self.emit('log', result); - if (callback) { - callback(null, result); - } - } - catch (ex) { - if (callback) { - callback(new Error('Unspecified error from Loggly: ' + ex)); - } - } - }); - - return this; -}; - -// -// ### function tag (tags) -// #### @tags {Array} Tags to use for `X-LOGGLY-TAG` -// Sets the tags on this instance -// -Loggly.prototype.tagFilter = function (tags) { - var isSolid = /^[\w\d][\w\d-_.]+/; - - tags = !Array.isArray(tags) - ? [tags] - : tags; - - // - // TODO: Filter against valid tag names with some Regex - // http://www.loggly.com/docs/tags/ - // Remark: Docs make me think we dont need this but whatevs - // - return tags.filter(function (tag) { - // - // Remark: length may need to use Buffer.byteLength? - // - return isSolid.test(tag) && tag.length <= 64; - }); -}; - -// -// ### function customer (callback) -// ### @callback {function} Continuation to respond to. -// Retrieves the customer information from the Loggly API: -// - http://www.loggly.com/docs/api-account-info/ -// -Loggly.prototype.customer = function (callback) { - common.loggly({ - uri: this.logglyUrl('customer'), - auth: this.auth - }, callback, function (res, body) { - var customer; - try { customer = JSON.parse(body) } - catch (ex) { return callback(ex) } - callback(null, customer); - }); -}; - -// -// function search (query, callback) -// Returns a new search object which can be chained -// with options or called directly if @callback is passed -// initially. -// -// Sample Usage: -// -// client.search('404', function () { /* ... */ }) -// .on('rsid', function (rsid) { /* ... */ }) -// -// client.search({ query: '404', rows: 100 }) -// .on('rsid', function (rsid) { /* ... */ }) -// .run(function () { /* ... */ }); -// -Loggly.prototype.search = function (query, callback) { - var options = typeof query === 'string' - ? { query: query } - : query; - - options.callback = callback; - return new Search(options, this); -}; - -// -// function logglyUrl ([path, to, resource]) -// Helper method that concats the string params into a url -// to request against a loggly serverUrl. -// -Loggly.prototype.logglyUrl = function (/* path, to, resource */) { - var args = Array.prototype.slice.call(arguments); - return [this.urls.api].concat(args).join('/'); -}; - -// -// Simple noop function for reusability -// -function noop() {} diff --git a/lib/loggly/common.js~ b/lib/loggly/common.js~ deleted file mode 100644 index 9235408..0000000 --- a/lib/loggly/common.js~ +++ /dev/null @@ -1,259 +0,0 @@ -/* - * common.js: Common utility functions for requesting against Loggly APIs - * - * (C) 2010 Charlie Robbins - * MIT LICENSE - * - */ - -// -// Variables for Bulk -// -var arrSize = 100, - arrMsg = [], - timerFunction = null; - -var https = require('https'), - util = require('util'), - request = require('request'), - loggly = require('../loggly'); - -var common = exports; - -// -// Failure HTTP Response codes based -// off Loggly specification. -// -var failCodes = common.failCodes = { - 400: 'Bad Request', - 401: 'Unauthorized', - 403: 'Forbidden', - 404: 'Not Found', - 409: 'Conflict / Duplicate', - 410: 'Gone', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 503: 'Throttled' -}; - -// -// Success HTTP Response codes based -// off Loggly specification. -// -var successCodes = common.successCodes = { - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non-authoritative information', - 204: 'Deleted' -}; - -// -// Core method that actually sends requests to Loggly. -// This method is designed to be flexible w.r.t. arguments -// and continuation passing given the wide range of different -// requests required to fully implement the Loggly API. -// -// Continuations: -// 1. 'callback': The callback passed into every node-loggly method -// 2. 'success': A callback that will only be called on successful requests. -// This is used throughout node-loggly to conditionally -// do post-request processing such as JSON parsing. -// -// Possible Arguments (1 & 2 are equivalent): -// 1. common.loggly('some-fully-qualified-url', auth, callback, success) -// 2. common.loggly('GET', 'some-fully-qualified-url', auth, callback, success) -// 3. common.loggly('DELETE', 'some-fully-qualified-url', auth, callback, success) -// 4. common.loggly({ method: 'POST', uri: 'some-url', body: { some: 'body'} }, callback, success) -// -common.loggly = function () { - var args = Array.prototype.slice.call(arguments), - success = args.pop(), - callback = args.pop(), - responded, - requestBody, - headers, - method, - auth, - proxy, - isBulk, - uri; - - // - // Now that we've popped off the two callbacks - // We can make decisions about other arguments - // - if (args.length === 1) { - if (typeof args[0] === 'string') { - // - // If we got a string assume that it's the URI - // - method = 'GET'; - uri = args[0]; - } - else { - method = args[0].method || 'GET'; - uri = args[0].uri; - requestBody = args[0].body; - auth = args[0].auth; - isBulk = args[0].isBulk; - headers = args[0].headers; - proxy = args[0].proxy; - } - } - else if (args.length === 2) { - method = 'GET'; - uri = args[0]; - auth = args[1]; - } - else { - method = args[0]; - uri = args[1]; - auth = args[2]; - } - - function onError(err) { - if (!responded) { - responded = true; - if (callback) { callback(err) } - } - } - var requestOptions = { - uri: isBulk ? uri + '/tag/' + headers['X-LOGGLY-TAG'] : uri, - method: method, - headers: isBulk ? {} : headers || {} // Set headers empty for bulk - proxy: proxy - }; - - if (auth) { - requestOptions.headers.authorization = 'Basic ' + new Buffer(auth.username + ':' + auth.password).toString('base64'); - } - if (requestBody) { - requestOptions.body = requestBody; - } - function sendLogs() { - try { - request(requestOptions, function (err, res, body) { - if (err) { - return onError(err); - } - var statusCode = res.statusCode.toString(); - if (Object.keys(failCodes).indexOf(statusCode) !== -1) { - return onError((new Error('Loggly Error (' + statusCode + '): ' + failCodes[statusCode]))); - } - success(res, body); - }); - } - catch (ex) { - onError(ex); - } - } - function sendBulkLogs() { - if (arrMsg.length === 0) { - return; - } - // - // Join Array Message with new line ('\n') character - // - requestOptions.body = arrMsg.join('\n'); - try { - request(requestOptions, function (err, res, body) { - if (err) { - return onError(err); - } - var statusCode = res.statusCode.toString(); - if (Object.keys(failCodes).indexOf(statusCode) !== -1) { - return onError((new Error('Loggly Error (' + statusCode + '): ' + failCodes[statusCode]))); - } - success(res, body); - }); - } - catch (ex) { - onError(ex); - } - finally { - // - // Empty the array - // - arrMsg.length = 0; - } - } - if (isBulk === true) { - if (timerFunction === null) { - timerFunction = setInterval(function () { - sendBulkLogs(); - },30000); - } - arrMsg.push(requestBody); - if (arrMsg.length === arrSize) { - sendBulkLogs(); - } - } - else { - sendLogs(); - } -}; -// -// ### function serialize (obj, key) -// #### @obj {Object|literal} Object to serialize -// #### @key {string} **Optional** Optional key represented by obj in a larger object -// Performs simple comma-separated, `key=value` serialization for Loggly when -// logging for non-JSON values. -// -common.serialize = function (obj, key) { - if (obj === null) { - obj = 'null'; - } - else if (obj === undefined) { - obj = 'undefined'; - } - else if (obj === false) { - obj = 'false'; - } - - if (typeof obj !== 'object') { - return key ? key + '=' + obj : obj; - } - - var msg = '', - keys = Object.keys(obj), - length = keys.length; - - for (var i = 0; i < length; i++) { - if (Array.isArray(obj[keys[i]])) { - msg += keys[i] + '=['; - - for (var j = 0, l = obj[keys[i]].length; j < l; j++) { - msg += common.serialize(obj[keys[i]][j]); - if (j < l - 1) { - msg += ', '; - } - } - - msg += ']'; - } - else { - msg += common.serialize(obj[keys[i]], keys[i]); - } - - if (i < length - 1) { - msg += ', '; - } - } - - return msg; -}; - -// -// function clone (obj) -// Helper method for deep cloning pure JSON objects -// i.e. JSON objects that are either literals or objects (no Arrays, etc) -// -common.clone = function (obj) { - var clone = {}; - for (var i in obj) { - clone[i] = obj[i] instanceof Object ? common.clone(obj[i]) : obj[i]; - } - - return clone; -}; diff --git a/test-as-mock/common-test.js~ b/test-as-mock/common-test.js~ deleted file mode 100644 index ce49376..0000000 --- a/test-as-mock/common-test.js~ +++ /dev/null @@ -1,32 +0,0 @@ -/* - * common-test.js: Tests for Loggly `common` utility module - * - * (C) 2010 Charlie Robbins - * MIT LICENSE - * - */ - -var path = require('path'), - vows = require('vows'), - assert = require('assert'), - common = require('.../lib/loggly/common'); - -vows.describe('node-loggly/common').addBatch({ - "When using the common module": { - "the clone() method": { - topic: function () { - this.obj = { - name: 'common', - deep: { - first: 'first', - second: 'second' - } - }; - return common.clone(this.obj); - }, - "should return a deep clone of the object": function (clone) { - assert.isFalse(this.obj.deep === clone.deep); - } - } - } -}).export(module); diff --git a/test-as-mock/config.json.enc b/test-as-mock/config.json.enc deleted file mode 100644 index 93242f8..0000000 --- a/test-as-mock/config.json.enc +++ /dev/null @@ -1 +0,0 @@ -�Sx̣��v5�j7�v���xC 1۟.j���aD4p�us����#,*a:�1��v~3�3V�!J ���VC�?���Ҿ�c-𕿄y���\7�)��S��%.�6h�@t�pЕu �-jV�W}m��~w�vX�AHF[�P�M��<��-��0c� F]҄�Z��#)rr�Y����`��# \ No newline at end of file From 7f33bc67e08ae46bb5db0381dec1c780ce1359ea Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Fri, 4 Mar 2016 16:04:22 +0530 Subject: [PATCH 7/9] Updated repetition --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 2eb0b42..4d829fb 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,7 @@ "dependencies": { "request": "2.67.x", "timespan": "2.3.x", - "json-stringify-safe": "5.0.x", - "nock": "~7.2.2" + "json-stringify-safe": "5.0.x" }, "devDependencies": { "common-style": "^3.1.0", From 0c8693444e1187162b13c220a0f2d8411f43afb2 Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Fri, 4 Mar 2016 16:45:03 +0530 Subject: [PATCH 8/9] Added info to run mock test cases --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index a8a5ca7..32b170d 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,8 @@ See the [Loggly search guide][search] for more details on how to effectively sea ``` ## Run Tests + +### Run Tests by sending events to your Loggly Account All of the node-loggly tests are written in [vows][vows], and cover all of the use cases described above. You will need to add your Loggly username, password, subdomain, and your loggly token to test/config.json before running tests. ``` js @@ -176,6 +178,12 @@ Once you have valid Loggly credentials you can run tests with [vows][vows]: ``` bash $ npm test ``` +### Run Tests with Mock HTTP Request +To mock the HTTP requests and run test cases in your local machine you can run the following command +```bash + $ npm run test-as-mock +``` + #### Author: [Charlie Robbins](http://www.github.com/indexzero) #### Contributors: [Marak Squires](http://github.com/marak), [hij1nx](http://github.com/hij1nx), [Kord Campbell](http://loggly.com), [Erik Hedenström](http://github.com/ehedenst), From 7aaae0b42449bdf7341527b789aa7cc98db7a215 Mon Sep 17 00:00:00 2001 From: Shourya Taneja Date: Fri, 4 Mar 2016 17:34:56 +0530 Subject: [PATCH 9/9] removed config.json from .gitignore --- .gitignore | 1 - test/{config.json~ => config.json} | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) rename test/{config.json~ => config.json} (62%) diff --git a/.gitignore b/.gitignore index c3b9412..93f1361 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -test/*.json node_modules npm-debug.log diff --git a/test/config.json~ b/test/config.json similarity index 62% rename from test/config.json~ rename to test/config.json index 5367486..21072ae 100644 --- a/test/config.json~ +++ b/test/config.json @@ -1,5 +1,5 @@ { - "token": "your-really-long-token-you-got-when-you-created-an-http-input", + "token": "your-loggly-token", "subdomain": "your-subdomain", "auth": { "username": "your-username",