first commit

This commit is contained in:
Sami Samhuri 2013-06-04 10:31:48 -07:00
commit 131deb7c06
8 changed files with 253 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules

27
Readme.md Normal file
View file

@ -0,0 +1,27 @@
# kwik-e-mon
Monitor one-off tasks on your servers.
## Installation
npm install -g kwikemon
## Usage
I can't think of a concise example use of the command line tool. Here's how it works:
- you continuously pipe data to `kwikemon <name of thing you are watching>` on stdin
- every time a full line of text is received on stdin it becomes the new status for <name of thing you are watching>
- there's a simple web server, `kwikemond`, that serves up these monitors in a big list or individually
This is very much a work in progress and as the functionality is fleshed out this readme will improve as well.
## License
Copyright 2013 Sami Samhuri <sami@samhuri.net>
[MIT license](http://sjs.mit-license.org)

10
bin/kwikemon Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env node
var kwikemon = require('../kwikemon.js')
, name = process.argv[2]
;
process.stdin.pipe(kwikemon.createWriter(name));
process.stdin.on('end', function() {
process.exit(0);
});

8
bin/kwikemond Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env node
var server = require('../server.js')
, port = process.argv[2]
, host = process.argv[3]
;
server.start(port, host);

76
kwikemon.js Normal file
View file

@ -0,0 +1,76 @@
// Copyright 2013 Sami Samhuri
module.exports = {
// read
fetchMonitor: fetchMonitor
, fetchMonitors: fetchMonitors
// write
, monitor: monitor
, createWriter: createWriter
};
var redis = require('redis').createClient()
, LineEmitter = require('./line_emitter.js')
;
function monitor(name, text, options) {
console.log(name,'=',text)
options = options || {};
if (typeof options == 'function') {
options = { cb: options };
}
var key = 'kwikemon:monitor:' + name
, timeout = options.timeout || 86400
;
console.log('set',key,text)
redis.set(key, text, function(err, status) {
console.log('set',key,text)
if (err) throw err;
if (timeout >= 0) {
redis.expire(key, timeout);
}
redis.sadd('kwikemon:monitors', name, function(err, status) {
if (options.cb) options.cb();
});
});
}
function createWriter(name) {
var le = new LineEmitter();
le.on('line', function(line) {
monitor(name, line);
});
return le;
}
function fetchMonitor(name, cb) {
redis.get('kwikemon:monitor:' + name, cb);
}
function fetchMonitors(cb) {
var monitors = {}
, i = 0
, n
, checkIfDone = function() {
i += 1;
if (i == n) cb(null, monitors);
}
;
redis.smembers('kwikemon:monitors', function(err, names) {
if (err) return cb(err);
n = names.length;
names.forEach(function(name) {
fetchMonitor(name, function(err, text) {
if (err) {
// missing? probably don't care
}
else {
monitors[name] = text;
}
checkIfDone();
});
});
});
}

54
line_emitter.js Normal file
View file

@ -0,0 +1,54 @@
// Copyright 2013 Sami Samhuri
module.exports = LineEmitter;
var stream = require('stream')
, Transform = stream.Transform
;
function LineEmitter(options) {
Transform.call(this, options);
this._buffer = [];
}
LineEmitter.prototype = Object.create(Transform.prototype, {
constructor: { value: LineEmitter }
});
LineEmitter.prototype._transform = function(chunk, encoding, done) {
// check for a newline
var split = -1;
for (var i = 0; i < chunk.length; i++) {
if (chunk[i] === 10) { // '\n'
split = i;
break;
}
}
// buffer until we see a newline
if (split == -1) {
this._buffer.push(chunk);
}
// construct & emit the line, buffering the rest of the next line
else {
this._buffer.push(chunk.slice(0, split));
var line = Buffer.concat(this._buffer).toString();
this.emit('line', line);
// skip over newline
this._buffer = [chunk.slice(split + 1)];
}
// no actual transform
this.push(chunk);
done();
}
LineEmitter.prototype._flush = function(cb) {
var line = Buffer.concat(this._buffer).toString();
if (line) {
this.emit('line', line);
}
this._buffer = [];
}

20
package.json Normal file
View file

@ -0,0 +1,20 @@
{ "name": "kwikemon"
, "version": "0.0.1"
, "description": "monitor one-off things on your servers"
, "author": "Sami Samhuri <sami@samhuri.net>"
, "license": "MIT"
, "repository": "https://github.com/samsonjs/kwikemon"
, "keywords": [ "monitor", "server", "watch", "one-off", "task" ]
, "main": "kwikemon.js"
, "bin": {
"kwikemon": "./bin/kwikemon"
, "kwikemond": "./bin/kwikemond"
}
, "dependencies": {
"paramify": "0.0.x"
, "redis": "0.8.x"
}
, "engines": {
"node": ">=0.10"
}
}

57
server.js Normal file
View file

@ -0,0 +1,57 @@
// Copyright 2013 Sami Samhuri
module.exports = {
create: create
, start: start
, stop: stop
};
var http = require('http')
, paramify = require('paramify')
, kwikemon = require('./kwikemon.js')
, _server
;
function create() {
return http.createServer(handleRequest);
}
function start(port, host) {
port = port || 1111;
host = host || '127.0.0.1';
_server = create();
_server.listen(port, host);
console.log('kwikemond listening on ' + host + ':' + port);
return _server;
}
function stop() {
_server.close();
_server = null;
}
function handleRequest(req, res) {
var name = req.url.replace(/^\//, '');
if (name) {
kwikemon.fetchMonitor(name, function(err, text) {
if (err) {
res.end('error: ' + (err.message || 'unknown'));
return;
}
res.end(text);
});
}
// all
else {
kwikemon.fetchMonitors(function(err, monitors) {
if (err) {
res.end('error: ' + (err.message || 'unknown'));
return;
}
Object.keys(monitors).sort().forEach(function(name) {
res.write(name + ': ' + monitors[name] + '\n');
});
res.end();
});
}
}