log.io-ng
Version:
Realtime log monitoring in the browser
478 lines (406 loc) • 14.4 kB
JavaScript
// Generated by CoffeeScript 1.10.0
/*
* Log.io Log Server #
Relays inbound log messages to web clients
LogServer receives log messages via TCP:
"+log|my_stream|my_server_host|info|this is a log message\r\n"
Announce a node, optionally with stream associations
"+node|my_server_host\r\n"
"+node|my_server_host|my_stream1,my_stream2,my_stream3\r\n"
Announce a stream, optionally with node associations
"+stream|my_stream1\r\n"
"+stream|my_stream1|my_server_host1,my_host_server2\r\n"
Remove a node or stream
"-node|my_server_host1\r\n"
"-stream|stream2\r\n"
WebServer listens for events emitted by LogServer and
forwards them to web clients via socket.io
* Usage:
logServer = new LogServer port: 28777
webServer = new WebServer logServer, port: 28778
webServer.run()
*/
(function() {
var LogNode, LogServer, LogStream, WebServer, _LogObject, events, express, fs, http, https, io, net, winston,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
slice = [].slice;
events = require('events');
fs = require('fs');
http = require('http');
https = require('https');
net = require('net');
express = require('express');
io = require('socket.io');
winston = require('winston');
_LogObject = (function() {
_LogObject.prototype._type = 'object';
_LogObject.prototype._pclass = function() {};
_LogObject.prototype._pcollection = function() {};
function _LogObject(logServer, name1, _pairs) {
var i, len, pname;
this.logServer = logServer;
this.name = name1;
if (_pairs == null) {
_pairs = [];
}
this.logServer.emit("add_" + this._type, this);
this.pairs = {};
this.pclass = this._pclass();
this.pcollection = this._pcollection();
for (i = 0, len = _pairs.length; i < len; i++) {
pname = _pairs[i];
this.addPair(pname);
}
}
_LogObject.prototype.addPair = function(pname) {
var pair;
if (!(pair = this.pairs[pname])) {
if (!(pair = this.pcollection[pname])) {
pair = this.pcollection[pname] = new this.pclass(this.logServer, pname);
}
pair.pairs[this.name] = this;
this.pairs[pname] = pair;
return this.logServer.emit("add_" + this._type + "_pair", this, pname);
}
};
_LogObject.prototype.remove = function() {
var name, p, ref, results;
this.logServer.emit("remove_" + this._type, this);
ref = this.pairs;
results = [];
for (name in ref) {
p = ref[name];
results.push(delete p.pairs[this.name]);
}
return results;
};
_LogObject.prototype.toDict = function() {
var name, obj;
return {
name: this.name,
pairs: (function() {
var ref, results;
ref = this.pairs;
results = [];
for (name in ref) {
obj = ref[name];
results.push(name);
}
return results;
}).call(this)
};
};
return _LogObject;
})();
LogNode = (function(superClass) {
extend(LogNode, superClass);
function LogNode() {
return LogNode.__super__.constructor.apply(this, arguments);
}
LogNode.prototype._type = 'node';
LogNode.prototype._pclass = function() {
return LogStream;
};
LogNode.prototype._pcollection = function() {
return this.logServer.streams;
};
return LogNode;
})(_LogObject);
LogStream = (function(superClass) {
extend(LogStream, superClass);
function LogStream() {
return LogStream.__super__.constructor.apply(this, arguments);
}
LogStream.prototype._type = 'stream';
LogStream.prototype._pclass = function() {
return LogNode;
};
LogStream.prototype._pcollection = function() {
return this.logServer.nodes;
};
return LogStream;
})(_LogObject);
/*
LogServer listens for TCP connections. It parses & validates
inbound TCP messages, and emits events.
*/
LogServer = (function(superClass) {
extend(LogServer, superClass);
function LogServer(config) {
var ref, ref1;
if (config == null) {
config = {};
}
this._flush = bind(this._flush, this);
this._receive = bind(this._receive, this);
this.host = config.host, this.port = config.port;
if (this.host == null) {
this.host = '127.0.0.1';
}
if (this.port == null) {
this.port = 28777;
}
this.debug = (ref = config.logging) != null ? ref : winston;
this.delimiter = (ref1 = config.delimiter) != null ? ref1 : '\r\n';
this.nodes = {};
this.streams = {};
}
LogServer.prototype.run = function() {
this.server = net.createServer((function(_this) {
return function(socket) {
socket._buffer = '';
socket.on('data', _this._receive.bind(_this, socket));
socket.on('error', _this._tearDown.bind(_this, socket));
return socket.on('close', _this._tearDown.bind(_this, socket));
};
})(this));
return this.server.listen(this.port, this.host);
};
LogServer.prototype._tearDown = function(socket) {
this.debug.error('Lost TCP connection...');
if (socket.node) {
this._removeNode(socket.node.name);
return delete socket.node;
}
};
LogServer.prototype._receive = function(socket, data) {
var part;
part = data.toString();
socket._buffer += part;
this.debug.debug("Received TCP message: " + part);
if (socket._buffer.indexOf(this.delimiter >= 0)) {
return this._flush(socket);
}
};
LogServer.prototype._flush = function(socket) {
var i, j, len, msg, msgs, ref, results;
socket.pause();
ref = socket._buffer.split(this.delimiter), msgs = 2 <= ref.length ? slice.call(ref, 0, i = ref.length - 1) : (i = 0, []), socket._buffer = ref[i++];
socket.resume();
results = [];
for (j = 0, len = msgs.length; j < len; j++) {
msg = msgs[j];
results.push(this._handle(socket, msg));
}
return results;
};
LogServer.prototype._handle = function(socket, msg) {
var args, mtype, ref;
this.debug.debug("Handling message: " + msg);
ref = msg.split('|'), mtype = ref[0], args = 2 <= ref.length ? slice.call(ref, 1) : [];
switch (mtype) {
case '+log':
return this._newLog.apply(this, args);
case '+node':
return this._addNode.apply(this, args);
case '+stream':
return this._addStream.apply(this, args);
case '-node':
return this._removeNode.apply(this, args);
case '-stream':
return this._removeStream.apply(this, args);
case '+bind':
return this._bindNode.apply(this, [socket].concat(slice.call(args)));
default:
return this.debug.error("Invalid TCP message: " + msg);
}
};
LogServer.prototype._addNode = function(nname, snames) {
if (snames == null) {
snames = '';
}
return this.addEntity(nname, snames, this.nodes, LogNode, 'node');
};
LogServer.prototype._addStream = function(sname, nnames) {
if (nnames == null) {
nnames = '';
}
return this.addEntity(sname, nnames, this.streams, LogStream, 'stream');
};
LogServer.prototype._removeNode = function(nname) {
return this.__remove(nname, this.nodes, 'node');
};
LogServer.prototype._removeStream = function(sname) {
return this.__remove(sname, this.streams, 'stream');
};
LogServer.prototype._newLog = function() {
var logLevel, message, nname, node, sname, stream;
sname = arguments[0], nname = arguments[1], logLevel = arguments[2], message = 4 <= arguments.length ? slice.call(arguments, 3) : [];
message = message.join('|');
this.debug.debug("Log message: (" + sname + ", " + nname + ", " + logLevel + ") " + message);
node = this.nodes[nname] || this._addNode(nname, sname);
stream = this.streams[sname] || this._addStream(sname, nname);
return this.emit('new_log', stream, node, logLevel, message);
};
LogServer.prototype.addEntity = function(name, pnames, _collection, _objClass, objName) {
var i, len, obj, p, results;
this.debug.info("Adding " + objName + ": " + name + " (" + pnames + ")");
pnames = pnames.split(',');
obj = _collection[name] = _collection[name] || new _objClass(this, name, pnames);
results = [];
for (i = 0, len = pnames.length; i < len; i++) {
p = pnames[i];
if (!obj.pairs[p]) {
results.push(obj.addPair(p));
}
}
return results;
};
LogServer.prototype.__remove = function(name, _collection, objType) {
var obj;
if (obj = _collection[name]) {
this.debug.info("Removing " + objType + ": " + name);
obj.remove();
return delete _collection[name];
}
};
LogServer.prototype._bindNode = function(socket, obj, nname) {
var node;
if (node = this.nodes[nname]) {
this.debug.info("Binding node '" + nname + "' to TCP socket");
socket.node = node;
return this._ping(socket);
}
};
LogServer.prototype._ping = function(socket) {
if (socket.node) {
socket.write('ping');
return setTimeout(((function(_this) {
return function() {
return _this._ping(socket);
};
})(this)), 2000);
}
};
return LogServer;
})(events.EventEmitter);
/*
WebServer relays LogServer events to web clients via socket.io.
*/
WebServer = (function() {
function WebServer(logServer, config) {
var app, ref, ref1, ref2;
this.logServer = logServer;
this.host = config.host, this.port = config.port, this.auth = config.auth;
ref = this.logServer, this.nodes = ref.nodes, this.streams = ref.streams;
this.restrictSocket = (ref1 = config.restrictSocket) != null ? ref1 : '*:*';
this.debug = (ref2 = config.logging) != null ? ref2 : winston;
app = this._buildServer(config);
this.http = this._createServer(config, app);
}
WebServer.prototype._buildServer = function(config) {
var app, ips, ref, staticPath;
app = express();
if (this.auth != null) {
app.use(express.basicAuth(this.auth.user, this.auth.pass));
}
if (config.restrictHTTP) {
ips = new RegExp(config.restrictHTTP.join('|'));
app.all('/', (function(_this) {
return function(req, res, next) {
if (!req.ip.match(ips)) {
return res.send(403, "Your IP (" + req.ip + ") is not allowed.");
}
return next();
};
})(this));
}
staticPath = (ref = config.staticPath) != null ? ref : __dirname + '/../';
return app.use(express["static"](staticPath));
};
WebServer.prototype._createServer = function(config, app) {
if (!config.ssl) {
return http.createServer(app);
} else {
return https.createServer({
key: fs.readFileSync(config.ssl.key),
cert: fs.readFileSync(config.ssl.cert)
}, app);
}
};
WebServer.prototype.run = function() {
var _emit, _on, logNodes, logStreams, sockets;
this.debug.info('Starting Log.io Web Server...');
this.logServer.run();
io = io.listen(this.http.listen(this.port, this.host));
io.set('log level', 1);
io.set('origins', this.restrictSocket);
sockets = io.sockets;
_on = this.logServer.on.bind(this.logServer);
_emit = (function(_this) {
return function(_event, msg) {
_this.debug.debug("Relaying: " + _event);
return sockets.emit(_event, msg);
};
})(this);
_on('add_node', function(node) {
return _emit('add_node', node.toDict());
});
_on('add_stream', function(stream) {
return _emit('add_stream', stream.toDict());
});
_on('add_stream_pair', function(stream, nname) {
return _emit('add_pair', {
stream: stream.name,
node: nname
});
});
_on('add_node_pair', function(node, sname) {
return _emit('add_pair', {
stream: sname,
node: node.name
});
});
_on('remove_node', function(node) {
return _emit('remove_node', node.toDict());
});
_on('remove_stream', function(stream) {
return _emit('remove_stream', stream.toDict());
});
_on('new_log', function(stream, node, level, message) {
_emit('ping', {
stream: stream.name,
node: node.name
});
return sockets["in"](stream.name + ":" + node.name).emit('new_log', {
stream: stream.name,
node: node.name,
level: level,
message: message
});
});
logNodes = this.logNodes, logStreams = this.logStreams;
sockets.on('connection', function(wclient) {
var n, node, ref, s, stream;
wclient.on('watch', wclient.join.bind(wclient));
wclient.on('unwatch', wclient.leave.bind(wclient));
for (n in logNodes) {
node = logNodes[n];
wclient.emit('add_node', node.toDict());
}
for (s in logStreams) {
stream = logStreams[s];
wclient.emit('add_stream', stream.toDict());
}
for (n in logNodes) {
node = logNodes[n];
ref = node.pairs;
for (s in ref) {
stream = ref[s];
wclient.emit('add_pair', {
stream: s,
node: n
});
}
}
return wclient.emit('initialized');
});
return this.debug.info('Server started, listening...');
};
return WebServer;
})();
exports.LogServer = LogServer;
exports.WebServer = WebServer;
}).call(this);