Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jaakkos committed Apr 30, 2013
1 parent 8b9687c commit 6e271c9
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 2 deletions.
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
# OS generated files #
######################
lib-cov
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db

# node.js related
node_modules
*.seed
*.log
*.csv
Expand All @@ -12,3 +25,4 @@ logs
results

npm-debug.log
test/runner.js
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: node_js
node_js:
- "0.10"
- "0.8"
- "0.6"
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
winston-logstash
================
# winston-logstash

A [Logstash TCP][0] transport for [winston][1].

## Usage
``` js
var winston = require('winston');

//
// Requiring `winston-logstash` will expose
// `winston.transports.Logio`
//
require('winston-logstash');

winston.add(winston.transports.Logstash, {
port: 28777,
node_name: 'my node name',
host: '127.0.0.1'
});
```

## Inspiration
[winston-loggly][2]

## Run Tests

```
npm test
```

## TODO

1. Cleanup & check tests
2. SSL Support
3. Change format

#### Author: [Jaakko Suutarla](https://github.com/jaakkos)
#### License: MIT

[0]: http://logstash.net/
[1]: https://github.com/flatiron/winston
[2]: https://github.com/indexzero/winston-loggly
158 changes: 158 additions & 0 deletions lib/winston-logstash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
*
* (C) 2013 Jaakko Suutarla
* MIT LICENCE
*
*/

var net = require('net'),
util = require('util'),
os = require('os'),
winston = require('winston'),
common = require('winston/lib/winston/common');

var Logstash = exports.Logstash = function (options) {
winston.Transport.call(this, options);
options = options || {};

this.name = 'logstash';
this.localhost = options.localhost || os.hostname();
this.host = options.host || '127.0.0.1';
this.port = options.port || 28777;
this.node_name = options.node_name || process.title;
this.pid = options.pid || process.pid;

// Connection state
this.log_queue = [];
this.connected = false;
this.socket = null;
this.retries = 0;

// Protocol definition
this.delimiter = '\r\n';

this.connect();
};

//
// Inherit from `winston.Transport`.
//
util.inherits(Logstash, winston.Transport);

//
// Define a getter so that `winston.transports.Syslog`
// is available and thus backwards compatible.
//
winston.transports.Logstash = Logstash;


Logstash.prototype.log = function (level, msg, meta, callback) {
var self = this,
meta = winston.clone(meta || {}),
log_entry;

if (self.silent) {
return callback(null, true);
}

log_entry = common.log({
level: level,
message: msg,
meta: meta,
timestamp: self.timestamp,
json: true
});

if (!self.connected) {
self.log_queue.push({
message: log_entry,
callback: function () {
self.emit('logged');
callback(null, true);
}
});
} else {
self.sendLog(log_entry, function () {
self.emit('logged');
callback(null, true);
});
}
};

Logstash.prototype.connect = function () {
var self = this;
this.socket = new net.Socket();

this.socket.on('error', function (err) {
self.connected = false;
self.socket.destroy();

if (self.retries < 3) {
self.retries++;

setTimeout(function () {
self.connect();
}, 100);
} else {
self.log_queue = [];
self.silent = true;
}
});

this.socket.on('timeout', function() {
if (self.socket.readyState !== 'open') {
self.socket.destroy();
}
});

this.socket.on('close', function () {
self.connected = false;
self.connect();
});

this.socket.connect(self.port, self.host, function () {
self.announce();
});
};

Logstash.prototype.announce = function () {
var self = this;
self.connected = true;
self.flush();
};

Logstash.prototype.flush = function () {
var self = this;

for (var i = 0; i < self.log_queue.length; i++) {
self.sendLog(self.log_queue[i].message, self.log_queue[i].callback);
self.emit('logged');
}
self.log_queue.length = 0;
};

Logstash.prototype.sendLog = function (message, callback) {
var self = this;

self.socket.write(message);
callback();
};



















51 changes: 51 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "winston-logstash",
"version": "0.0.1RC",
"description": "A Log.io transport for winston",
"main": "./lib/winston-logstash",
"homepage": "https://github.com/jaakkos/winston-logstash",
"scripts": {
"test": "mocha test/*_test.js"
},
"engines": {
"node": ">=0.8.x"
},
"repository": {
"type": "git",
"url": "https://github.com/jaakkos/winston-logstash.git"
},
"dependencies": {
"winston": ">=0.6.2"
},
"devDependencies": {
"winston": ">=0.6.2",
"mocha": ">=1.8.2",
"chai": ">=1.5.0",
"timekeeper": ">=0.0.3"
},
"keywords": [
"logging",
"sysadmin",
"tools"
],
"bugs": {
"url" : "https://github.com/jaakkos/winston-logstash/issues"
},
"author": {
"name": "Jaakko Suutarla",
"email": "jaakko@suutarla.com"
},
"license": "MIT",
"contributors": [
{
"name": "Jaakko Suutarla",
"email": "jaakko@suutarla.com"
}
],
"maintainers": [
{
"name": "Jaakko Suutarla",
"email": "jaakko@suutarla.com"
}
]
}
3 changes: 3 additions & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--ui exports
--reporter spec
--timeout 30000
Binary file added test/support/logstash/logstash-1.1.10-flatjar.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions test/support/logstash/logstash.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
input {
stdin { type => "stdin-type"}
tcp { format => "json" charset => "UTF-8" port => 28777 type=>"merchii-worker-v2" }
}
output {
stdout { debug => true debug_format => "json"}
}
2 changes: 2 additions & 0 deletions test/support/logstash/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
java -jar ./logstash-1.1.10-flatjar.jar agent -f ./logstash.conf
88 changes: 88 additions & 0 deletions test/winston-logstash_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
process.env.NODE_ENV = 'test';

var chai = require('chai'),
expect = chai.expect,
net = require('net'),
winston = require('winston'),
timekeeper = require('timekeeper'),
freezed_time = new Date(1330688329321);

chai.Assertion.includeStack = true;

require('../lib/winston-logstash');

describe('winston-logstash transport', function() {
var test_server, port = 28777;

function createTestServer(port, on_data) {
var server = net.createServer(port, function (socket) {
socket.on('end', function () { });
socket.on('data', on_data);
});
server.listen(port, function() {});

return server;
}

function createLogger(port) {
return new (winston.Logger)({
transports: [
new (winston.transports.Logstash)( { port: port, node_name: 'test', localhost: 'localhost', pid: 12345 } )
]
});
}

describe('with logstash server', function () {
var test_server, port = 28777;

beforeEach(function(done) {
timekeeper.freeze(freezed_time);
done();
});

it('send logs over TCP', function(done) {
var response;
var logger = createLogger(port);

test_server = createTestServer(port, function (data) {
response = data.toString();
console.log(response)
expect(response).to.be.equal('{"stream":"worker_feed_split","level":"info","message":"hello world"}');
done();
});
logger.log('info', 'hello world', {stream: 'worker_feed_split'});
done();
});

// Teardown
afterEach(function () {
if (test_server) {
test_server.close(function () {});
}
timekeeper.reset();
test_server = null;
});

});

describe('without logstash server', function () {
it('fallback to silent mode if log.io server is down', function(done) {
var response;
var logger = createLogger(28747);
logger.log('info', 'hello world', {stream: 'worker_feed_split'});

expect(logger.transports.logstash.retries).to.be.equal(0);

setTimeout( function () {
expect(logger.transports.logstash.retries).to.be.equal(3);
expect(logger.transports.logstash.silent).to.be.true;
done();
}, 1000);

});
});


});


0 comments on commit 6e271c9

Please sign in to comment.