Skip to content

Commit

Permalink
Build 0.0.7 - getNext event emitter and HTTP server logging
Browse files Browse the repository at this point in the history
  • Loading branch information
robtweed committed Nov 10, 2010
1 parent d34fe1b commit 2add04b
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 3 deletions.
100 changes: 99 additions & 1 deletion README.md
Expand Up @@ -149,6 +149,8 @@ Now you can use any of the node-mwire APIs.
- transaction (Execute a sequence of Global manipulations in strict order, specified as an array of setJSON and kill JSON documents.)
- version (returns the M/Wire build number and date)

- onNext (built-in event emitter that supports sequential processing of Global nodes by subscript)

## Commands

( substitute *client.* with *mwire.clientPool[mwire.connection()]* )
Expand Down Expand Up @@ -336,7 +338,7 @@ Now you can use any of the node-mwire APIs.

In the example above, the actions are invoked in the GT.M or Caché back-end in strict sequence according to their position in the *json* array, ie *action1*, followed by *action 2*. The transaction details are sent as a single request to the back-end from Node.js and the invocation of the commands that make up the transaction occurs entirely within the back-end system. As a result, the Node.js thread is not blocked. The call-back function is invoked only when the entire transaction has completed at the back-end.
- mdbm.remoteFunction(functionName, parameters, function(error, results) {});
- client.remoteFunction(functionName, parameters, function(error, results) {});

Execute a native GTM or Caché function. This is usually for legacy applications:

Expand All @@ -348,6 +350,34 @@ Now you can use any of the node-mwire APIs.

results.value = the response/result returned by the remote function
- mwire.onNext(GlobalName, subscripts, callback)

This is an event emitter wrapper around the *getNextSubscript* command that can be used to invoke a specified callback for each successive subscript found by the *getNextSubscript* command.

GlobalName = name of Global (literal)
subscripts = array specifying the subscripts ('' if the first 1st subscript is to be returned)
eg ["a","b","c"] will return the value of the 3rd subscript the follows the value "c" where subscript1 = "a" and subscript2 = "b"
callback = the name of the callback function to invoke.

For example, suppose you have a Global: ^nwd("session",sessionId)=someData

To apply some processing to every sessionId node in this global, first fire a callback for the first sessionId in the global:

mwire.onNext("nwd", ["session", ""], processSession);

The *processSession* callback would look something like the following:

var processSession = function(error, results) {
var sessionId = results.subscriptValue;
if (sessionId != '') {
// process the Global Node here...
mwire.onNext("nwd", ["session", sessionId], processSession);
}
};

*mwire.onNext* will emit a new "getNext" event for each sessionId found in the global, and will stop when no more subscript values are found.

## Examples

To set the Global:
Expand Down Expand Up @@ -392,6 +422,74 @@ and the original JSON could be retrieved using:
console.log("getJSON: " + JSON.stringify(json));
});

## HTTP Server Logging

*node-mwire* includes an HTTP server log mechanism, allowing you to store in a GT.M or Caché database the complete details of every incoming HTTP request to your Node.js web server (ie the equivalent of Apache's log file, but saved in JSON format).

To use this facility, simply install node-mwire, install and configure a GT.M or Caché database and add the following *requires* at the top of your web server code:

var mwireLib = require("node-mwire");
var mwire = new mwireLib.Client({port:6330, host:'127.0.0.1'});

[Modify the host and port paramaters if required]

Then, add the following within your main web server logic:

mwire.httpLog(request);
eg:

http.createServer(function(request, response) {
var urlObj = url.parse(request.url);
var uri = urlObj.pathname;
if (uri === '/favicon.ico') {
display404(response);
return;
}
mwire.httpLog(request);
//....etc
}).listen(8080);

By default, up to 5 days' worth of logs will be maintained in the GT.M or Caché database. A timed event kicks in automatically each hour to trim down the HTTP log Global.

Each request is saved as an event record. Here's a typical example of an event in the Global:

^nodeHTTPLog("log",214,"headers","accept")="application/xml,application/xhtml+xm
l,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
^nodeHTTPLog("log",214,"headers","accept-encoding")="gzip, deflate"
^nodeHTTPLog("log",214,"headers","accept-language")="en-us"
^nodeHTTPLog("log",214,"headers","cache-control")="max-age=0"
^nodeHTTPLog("log",214,"headers","connection")="keep-alive"
^nodeHTTPLog("log",214,"headers","host")="192.168.1.115:8080"
^nodeHTTPLog("log",214,"headers","user-agent")="Mozilla/5.0 (Macintosh; U; Intel
Mac OS X 10_6_4; en-us) AppleWebKit/533.18.1 (KHTML, like Gecko) Vers
ion/5.0.2 Safari/533.18.5"
^nodeHTTPLog("log",214,"httpVerion")=1.1
^nodeHTTPLog("log",214,"method")="GET"
^nodeHTTPLog("log",214,"remoteAddr")="192.168.1.100"
^nodeHTTPLog("log",214,"time")="Wed, 10 Nov 2010 13:07:44 GMT"
^nodeHTTPLog("log",214,"timeStamp")=1289394464293
^nodeHTTPLog("log",214,"url")="/ewd/testpage2/?a=1&b=2"

You can change the number of days' worth of logs that will be maintained in this Global using the command:

mwire.setHttpLogDays(noOfDays);
eg:
mwire.setHttpLogDays(7);
You can retrieve a logged event using the getJSON command, eg:

mwire.clientPool[mwire.connection()].getJSON('nodeHTTPLog', ["log", 214] ,
function(err, json) {
console.log("HTTP request event 214: " + JSON.stringify(json));
console.log("The browser used was " + json.headers["user-agent"]);
console.log("The request was from IP address " + json.remoteAddr);
});

## Using node-mwire with Caché

The node-mwire client can be used with a Caché database
Expand Down
82 changes: 81 additions & 1 deletion lib/mwire.js
Expand Up @@ -40,11 +40,12 @@ General Public License along with this program.
If not, see <http://www.gnu.org/licenses/>.
----------------------------------------------------------
Build 0.0.5 - 07 November 2010
Build 0.0.7 - 10 November 2010
*/

var redis = require("redis-node");
var events = require("events");

var mwireClient = function(params) {
this.init(params);
Expand All @@ -64,6 +65,17 @@ mwireClient.prototype.init = function(params) {
this.clientPool[no] = redis.createClient(this.port, this.host);
this.addCommands(this.clientPool[no]);
}
this.event = new events.EventEmitter();
var mwire = this;
this.event.on("getNext",
function(globalName, subscripts, callback) {
mwire.clientPool[mwire.connection()].getNextSubscript(globalName, subscripts, callback);
}
);
};

mwireClient.prototype.onNext = function(globalName, subscripts, callback) {
this.event.emit("getNext", globalName, subscripts, callback);
};

mwireClient.prototype.connection = function() {
Expand Down Expand Up @@ -289,4 +301,72 @@ mwireClient.prototype.addCommands = function(redisClient) {

};

mwireClient.prototype.setHttpLogDays = function(noOfDays) {
var mwire = this;
if (noOfDays === undefined) noOfDays = 5;
if (noOfDays === '') noOfDays = 5;
this.clientPool[this.connection()].setGlobal("nodeHTTPLog", ["noOfDaysToKeep"], noOfDays,
function(error,results) {
var expiryTime = new Date().getTime() - (noOfDays * 86400000);
mwire.HTTPLogExpiry = expiryTime;
}
);
};

mwireClient.prototype.httpLog = function(request) {
var mwire = this;
var urlObj = require("url").parse(request.url);

var logHTTPEvent = function(error,results) {
var date = new Date();
var event = {
timeStamp: date.getTime(),
time: date.toUTCString(),
headers: request.headers,
url: request.url,
method: request.method,
httpVerion: request.httpVersion,
remoteAddr: request.connection.remoteAddress
};
mwire.clientPool[mwire.connection()].setJSON("nodeHTTPLog", ["log", results.value], event, false, function(e,r){});
};

this.clientPool[this.connection()].increment("nodeHTTPLog", ["nextEventNo"], 1, logHTTPEvent);

var deleteLogIfExpired = function(error, results) {
var eventNo = results.subscriptValue;
if (eventNo !== '') {
mwire.clientPool[mwire.connection()].getGlobal("nodeHTTPLog", ["log", eventNo, "timeStamp"],
function(error,results) {
//console.log("checking HTTP Log event " + eventNo + ": timeStamp = " + results.value + "; expiry = " + mwire.HTTPLogExpiry);
if ( results.value < mwire.HTTPLogExpiry ) {
//console.log("HTTP Log entry " + eventNo + " has been deleted");
mwire.clientPool[mwire.connection()].kill("nodeHTTPLog", ["log", eventNo], function(error,results){});
mwire.onNext("nodeHTTPLog", ["log", eventNo], deleteLogIfExpired);
}
}
);
}
};

if (!this.HTTPExpiryDaemonRunning) {
var mwire = this;
setInterval(
function() {
mwire.clientPool[mwire.connection()].getGlobal("nodeHTTPLog", ["noOfDaysToKeep"],
function(error,results) {
var noOfDays = results.value;
if (noOfDays === '') noOfDays = 7;
var expiryTime = new Date().getTime() - (noOfDays * 86400000);
mwire.HTTPLogExpiry = expiryTime;
mwire.onNext("nodeHTTPLog", ["log", ""], deleteLogIfExpired)
}
);
},
3600000);
this.HTTPExpiryDaemonRunning = true;
}

};

exports.Client = mwireClient;
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{ "name" : "node-mwire"
, "description" : "Extension to redis-node client for accessing GT.M and Cache Globals (via M/Wire interface)"
, "version" : "0.0.6"
, "version" : "0.0.7"
, "author" : "Rob Tweed <rtweed@mgateway.com>"
, "repository" :
{ "type" : "git"
Expand Down

0 comments on commit 2add04b

Please sign in to comment.