UNPKG

log.io-ng

Version:

Realtime log monitoring in the browser

478 lines (406 loc) 14.4 kB
// 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);