12

I'm trying to create a stream of data to the browser using websocket. The data is the output of a log file. (tail -f filename) Using node js, I've manage to log into stdout, but I haven't been able to create the server and create the client (js/html) code to create a websocket and receive all the output of this child process. Can anyone help me?

NODE.JS SERVER OUTPUTTING TAIL TO STDOUT (as seen in http://snippets.dzone.com/posts/show/12067)

var sys = require('sys')
var spawn = require('child_process').spawn;
var filename = process.ARGV[2];

if (!filename)
  return sys.puts("Usage: node <server.js> <filename>");

var tail = spawn("tail", ["-f", filename]);
sys.puts("start tailing");

tail.stdout.on("data", function (data) {
  sys.puts(data);
});

My goal is to have the simplest stream possible. Any other simple solution is well received for this. Thanks.

jdelard
  • 450
  • 1
  • 4
  • 11

3 Answers3

16

This simple?

var sys = require('sys')
var spawn = require('child_process').spawn;
var filename = process.ARGV[2];
if (!filename)
  return sys.puts("Usage: node <server.js> <filename>");

var tail = spawn("tail", ["-f", filename]);

http = require('http');
http.createServer(function (req, res) {
  sys.puts("new connection..");
  res.writeHead(200, {'Content-Type': "text/plain;charset=UTF-8"});
  tail.stdout.on("data", function (data) {
    res.write(data);
  }); 
}).listen(3000);

Connect to the server and you'll get your tail. You'll have to watch for timeouts on the client side, depending on your browser, if the tail goes idle.

If you want to access this data from javascript within the browser, consider using socket.io as this will use the best method the browser has available to access the stream (websocket, long poll, flash etc.). If you need a client javascript example, I can post that too.

bxjx
  • 1,838
  • 17
  • 11
  • 1
    if you just `res.write(data)`, you leave the client with quite a chore processing that object of encoded characters :( – Kato Dec 02 '11 at 17:20
  • a client javascript example that doesnt need CORS would be nice! and it'd be able to auto scroll the file, like `tail -f` – Brad Parks Dec 18 '15 at 19:50
7

Here's a simple sample I mostly grabbed from this gist

First, switch into an empty directory

mkdir socket-tail-app; cd socket-tail-app;

Then, install what's needed

npm install socket.io               

Run it like this

node server.js /path/to/file/to/tail

After it's running, open a browser at

http://localhost:8000

Here are the files you need:

server.js

var http    = require('http'),
    io      = require('socket.io'),
    fs      = require('fs');

var spawn = require('child_process').spawn;

var filename = process.argv[2];
if (!filename) 
{
  console.log("Usage: node server.js filename_to_tail");
  return;
}


// -- Node.js Server ----------------------------------------------------------

server = http.createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'})
  fs.readFile(__dirname + '/index.html', function(err, data){
    res.write(data, 'utf8');
    res.end();
  });
})
server.listen(8000, '0.0.0.0');

// -- Setup Socket.IO ---------------------------------------------------------

var io = io.listen(server);

io.on('connection', function(client){
  console.log('Client connected');
  var tail = spawn("tail", ["-f", filename]);
  client.send( { filename : filename } );

  tail.stdout.on("data", function (data) {
    console.log(data.toString('utf-8'))
    client.send( { tail : data.toString('utf-8') } )
  }); 

});

console.log('Server running at http://0.0.0.0:8000/, connect with a browser to see tail output');

index.html

<!DOCTYPE html>
<html>
<head>
  <title>tail.js</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
  <script src="//cdn.socket.io/socket.io-1.3.7.js"></script>

  <style>
    body
      { color: #1a2c37;
        font-family: 'Helvetica', sans-serif; font-size: 86%;
        padding: 2em; }
    #info
      { font-size: 120%;
        font-weight: bold; }
    #tail
      { border: 1px solid #ccc;
        height: 300px;
        padding: 0.5em;
        overflow: hidden;
        position: relative;
        overflow-y: scroll; }
  </style>

</head>
<body>
  <pre id="info"></pre>
  <pre id="tail"></pre>

  <script>

  var Application = function() {


    var socket = io.connect('http://127.0.0.1:8000/');

    socket.on('connect', function() {
      console.log('Connected to:', socket.host);
    });
    socket.on('message', function(message) {
      console.log('Received message:', message);
      if (message.filename) {
        $('#info').html( '$ tail -f ' + message.filename );
      };
      if (message.tail) {
        $('#tail').html( $('#tail').html() + message.tail );
        bottom = $("#tail")[0].scrollHeight - $("#tail").height()
        $('#tail').scrollTop(bottom);

      }
    });

    return {
      socket : socket
    };
  };

  $(function() { var app = Application(); });

  </script>

</body>
</html>
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
7

This seems like an old question & very possibly the problem is already solved, but in case it isn't here is a gist https://gist.github.com/867575 .

it uses socket.io and instead of spawning "tail -f" processes(which takes more memory), fs.watchFile is used.

NetRoY
  • 191
  • 2
  • 4
  • That's a really cool project. I see you split on "\n". If the `createReadStream` listener flushes halfway through a line, will the client output it as two separate lines? – 000 Apr 18 '12 at 14:41
  • It's cool, but actually tail will use inotify itself when possible. (I noticed when my system ran out of inotify handle thanks to sublime text and tail complained about having to use stat instead of inotify). So spawning tails is not such a bad things if you're on a UNIX server anyway. – Aktau Jun 19 '13 at 22:16
  • Also fs.watchFile uses stat, while fs.watch uses inotify. Needless to say, fs.watchFile uses a lot more system calls – Aktau Jun 19 '13 at 22:17