Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bulkmode support #55

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
test/*.json
node_modules
npm-debug.log
8 changes: 1 addition & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,9 @@ node_js:
before_install:
- travis_retry npm install -g [email protected]
- 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:
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,13 @@ 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:

### 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
{
"token": "your-really-long-token-you-got-when-you-created-an-http-input",
"token": "your-loggly-token",
"subdomain": "your-subdomain",
"auth": {
"username": "your-username",
Expand All @@ -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),
Expand Down
9 changes: 5 additions & 4 deletions lib/loggly/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ 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';
Expand All @@ -54,7 +55,7 @@ var Loggly = exports.Loggly = function (options) {
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.
//
Expand Down Expand Up @@ -101,7 +102,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);
Expand All @@ -111,13 +111,14 @@ 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: '*/*',
Expand Down
91 changes: 70 additions & 21 deletions lib/loggly/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
*
*/

//
// Variables for Bulk
//
var arrSize = 100,
arrMsg = [],
timerFunction = null;

var https = require('https'),
util = require('util'),
request = require('request'),
Expand Down Expand Up @@ -69,6 +76,7 @@ common.loggly = function () {
method,
auth,
proxy,
isBulk,
uri;

//
Expand All @@ -88,6 +96,7 @@ 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;
}
Expand All @@ -109,41 +118,81 @@ common.loggly = function () {
if (callback) { callback(err) }
}
}

var requestOptions = {
uri: uri,
uri: isBulk ? uri + '/tag/' + headers['X-LOGGLY-TAG'] : uri,
method: method,
headers: headers || {},
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;
}

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
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
},
"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": {
Expand Down
32 changes: 32 additions & 0 deletions test-as-mock/common-test.js
Original file line number Diff line number Diff line change
@@ -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);
8 changes: 8 additions & 0 deletions test-as-mock/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"token": "test",
"subdomain": "testSubdomain",
"auth": {
"username": "test",
"password": "test"
}
}
43 changes: 43 additions & 0 deletions test-as-mock/customer-test.js
Original file line number Diff line number Diff line change
@@ -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);
48 changes: 48 additions & 0 deletions test-as-mock/helpers.js
Original file line number Diff line number Diff line change
@@ -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');
};
Loading