Search code examples
node.jshttpexpressspawn

Node spawn process


Unable to find out the issue in following script, what i want to achieve with the script is to have a node log server that would listen to post requests with log title and log details as query parameters, write to a file and then throw back as json on get request.

Problem: It constantly shows loader sometime and gives the required log sometime.

Note: The process spawning is done to update the browser during the logging, if someone has better solution, plz suggest

Post Call:

http://127.0.0.1:8081/log?title="test"&detail="test detail"

Code:

var express = require("express");
var spawn = require('child_process').spawn;
var fs = require("fs");

var srv = express();

var outputFilename = '/tmp/my.json';

function getParamsObject(context) {
    var params = {};

    for (var propt_params in context.params) {
        params[propt_params] = context.params[propt_params];
        //define(params, propt_params, context.params[propt_params]);
    }

    for (var propt_body in context.body) {
        params[propt_body] = context.body[propt_body];
        //define(params, propt_body, context.body[propt_body]);
    }

    for (var propt_query in context.query) {
        params[propt_query] = context.query[propt_query];
        //define(params, propt_query, context.query[propt_query]);
    }

    return params;
}


srv.get("/", function(req, res) {
    res.send("Hello World From Index\n");

});

srv.get("/Main", function(req, res) {
    res.send("Hello World From Main\n");
});

srv.get("/ReadFile", function(req, res) {
    fs.readFile("example_one.txt", function(err, data) {

        if(err) throw err;

        res.send(data.toString());

    });
});

srv.get("/ReadFileJSON", function(req, res) {
    fs.readFile("example_one.txt", function(err, data) {

        if(err) throw err;

        res.setHeader("content-type", "application/json");
        res.send(new Parser().parse(data.toString()));

    });

});


srv.post("/log", function(req, res) {

    var input = getParamsObject(req);

    if(input.detail) {
    var myData = {
        Date: (new Date()).toString(),
                Title: input.title,
                Detail: input.detail
    }

    fs.writeFile(outputFilename, JSON.stringify(myData, null, 4), function(err) {
            if(err) {
                console.log(err);
            }
    }); 
    }
    res.setHeader("content-type", "application/json");
    res.send({message:"Saved"});

});

srv.get("/log", function(req, res) {

    var child = spawn('tail', ['-f', outputFilename]); 
    child.stdout.pipe(res);

    res.on('end', function() {
        child.kill(); 
    });
});


srv.listen(8081);
console.log('Server running on port 8081.');

Solution

  • To clarify the question...

    • You want some requests to write to a log file.
    • You want to effectively do a log tail over HTTP, and are currently doing that by spawning tail in a child process.
    • This isn't all that effective.

    Problem: It constantly shows loader sometime and gives the required log sometime.

    Web browsers buffer data. You're sending the data, sure, but the browser isn't going to display it until a minimum buffer size is reached. And then, there are rules for what will display when all the markup (or just text in this case) hasn't loaded yet. Basically, you can't stream a response to the client and reliably expect the client to do anything with it until it is done streaming. Since you're tailing a log, that puts you in a bad predicament.

    What you must do is find a different way to send that data to the client. This is a good candidate for web sockets. You can create a persistent connection between the client and the server and then handle the data immediately rather than worrying about a client buffer. Since you are using Node.js already, I suggest looking into Socket.IO as it provides a quick way to get up and running with web sockets, and long-polling JSON (among others) as a fallback in case web sockets aren't available on the current browser.

    Next, there is no need to spawn another process to read a file in the same way tail does. As Trott has pointed out, there is an NPM package for doing exactly what you need: https://github.com/lucagrulla/node-tail Just set up an event handler for the line event, and then fire a line event on the web socket so that your JavaScript client receives it and displays it to the user immediately.