A Logging Server with NodeJS, Https, CORS and MongoDB

It was just a question of time that logging was necessary for my RM Extension. After some review I choose log4javascript, because there is a AjaxAppender. What does it mean? As you know, the RM Extension is running inside the Browser and this Browser don’t allow write access to the file system ==>How to store the log results? ==>Send it to a so called “Logging Server”. OK, so we need a external Logging Server. The next challenge is SOP (same origin policy), the RM Javascript is not allowed to send (POST) the Log results to a Logging Server in a different domain/port. They best solution is to enable CORS. If there is a running Logging Server, why not using it to host the RM Extension files? Because the Browser is complaining about a mix of https and http, we need also https.

At the end we have the following Requirements for a Logging Server:

  • NodeJS, because it is easy to set up and is also Javascript based
  • CORS
  • HTTPS with self-signed-certificates Storing the the Log information to the filessystem (or DB like MongoDB?)

Codesnipppets (will be completed, currently just a starting point):

…with MongoDB

var http = require('http');
var url = require('url');

var mongojs = require('mongojs');
var uri = "mongodb://kanban:27017/demo_database",
    db = mongojs.connect(uri, ["demo_collection"]);

var collection = db.collection('Logs');

var contentType = {'Content-Type': 'text/html'};
var OK = 'OK', BAD = 'BAD REQUEST';
var MAX_URL_LENGTH = 2048, MAX_POST_DATA = 10240;

function createLog(request) {
    var logObj = {};
    try {
        logObj.time = new Date();
        logObj.method = request.method;
        logObj.host = request.headers.host;
        logObj.url = request.url;
        var queryStrings = url.parse(request.url, true).query;
        // Check if it's an empty object
        if (Object.keys(queryStrings).length) {
            logObj.query = queryStrings;
            for (var attrname in queryStrings) {
                logObj[attrname] = queryStrings[attrname];
            }
        }
    } catch (ex) {}
    return logObj;
}
http.createServer(function (request, response) {
    // Bypass request with URL larger than 2K
    if (request.url.length > MAX_URL_LENGTH) {
        response.writeHead(414, contentType);
        response.end(BAD);
    } else if (request.method == 'GET') {  // HTTP GET
        var logObj = createLog(request);
        if (logObj.query) {
            delete logObj.query;
            collection.save(logObj);
            response.writeHead(200, contentType);
            response.end(OK);
        } else {  // Empty request
            response.writeHead(400, contentType);
            response.end(BAD);
            request.connection.destroy();
        }
    } else if(request.method == 'POST') {  // HTTP POST
        var logObj = createLog(request);
        var postData = '';
        request.on('data', function(data) {
            postData += data;
            // Bypass request with POST data larger than 10K
            if(postData.length > MAX_POST_DATA) {
                postData = "";
                response.writeHead(413, contentType);
                response.end(BAD);
                request.connection.destroy();
            }
        });
        request.on('end', function() {
            if (!postData && !logObj.query) {  // Empty request
                response.writeHead(400, contentType);
                response.end(BAD);
                request.connection.destroy();
            } else {
                if (postData) {
                    try {
                        postObjs = JSON.parse(postData);
                        for (var attrname in postObjs) {
                            logObj[attrname] = postObjs[attrname];
                        }
                    } catch (ex) {
                        logObj.postData = postData;
                    }
                }
                if (logObj.query) {
                    delete logObj.query;
                }
                collection.save(logObj);
                response.writeHead(200, contentType);
                response.end(OK);
            }
        });
    }
}).listen(process.env.PORT || 8080);
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: