Search code examples
node.jssocket.iochild-processtail

Nodejs spawn remote tail and socket-io to client


I'm fairly new to Nodejs and I'm building an app that ssh to a remote machine and get a tail -f of a log file.

The lines of the log file I'm receiving I'm sending to the client via socket-io (ver. 2.0.3)

Now I'm facing a problem that when a second browser tries to tail a different log, the new log is sent to both of the browsers instead of only the one who made the request. I'm not sure if it's a problem with my socket-io code or the child_process.

Here's the server:

const express = require('express'),
    app = express(),
    path = require('path'),
    bodyParser = require('body-parser'),
    logger = require('morgan'),
    server = require('http').Server(app),
    io = require('socket.io')(server),
    spawn = require('child_process').spawn,
    events = require('events'),
    eventEmitter = new events.EventEmitter();


// Fix body of requests
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

// Log the requests
app.use(logger('dev'));

// Serve static files
app.use(express.static(path.join(__dirname, '.')));

// Add a basic route – index page
app.get('/', function (req, res) {
    res.sendFile(path.join(__dirname, 'index.html'));
});

io.on('connection', (socket) => {
    console.log(`client connected ${socket.client.id}`);
    eventEmitter.on('tail', (data) => {
        socket.tail = spawn('ssh', ['root@' + 'quality-p.company.com', 'tail -f', data.service], { shell: true });
        socket.tail.stdout.on('data', (data) => {
            console.log(`got new data ${data.toString()}`);
            socket.emit('newLine', {line: data.toString().replace(/\n/g, '<br />')});
        });
    });
});

app.get('/tail', (req, res) => {
    eventEmitter.emit('tail', req.query);
    res.sendStatus(200);
});

// Bind to a port
server.listen(3005, () => {
    console.log('running on localhost:' + 3005);
});

Client:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="./node_modules/socket.io-client/dist/socket.io.js"></script>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script>
        $(() => {
            let socket = io();
            socket.on('connect', () => {
                console.log('connected');
            });
            socket.on('newLine', (data) => {
                console.log(`new data: ${data.line}`);
                $("#tailing").append(data.line);
            });
            $('#tail').click(() => {
                $.get('/tail', {
                    service: $('#service').val()
                });
            });
        });
    </script>
    <title>Title</title>
</head>
<body>
<select id="service">
    <option id="tnet" value="/var/log/tnet">tnet</option>
    <option id="consul" value="/var/log/consul">consul</option>
</select>
<button id="tail">tail</button>
<div id="tailing" style="background-color: antiquewhite;">
</div>
</body>
</html>

Solution

  • Server

    const express = require('express'),
        app = express(),
        path = require('path'),
        bodyParser = require('body-parser'),
        logger = require('morgan'),
        server = require('http').Server(app),
        io = require('socket.io')(server),
        spawn = require('child_process').spawn;
    
    
    // Fix body of requests
    app.use(bodyParser.urlencoded({
        extended: true
    }));
    app.use(bodyParser.json());
    
    // Log the requests
    app.use(logger('dev'));
    
    // Serve static files
    app.use(express.static(path.join(__dirname, '.')));
    
    // Add a basic route – index page
    app.get('/', function(req, res) {
        res.sendFile(path.join(__dirname, 'index.html'));
    });
    
    var tails = {};
    
    io.on('connection', (socket) => {
        console.log(`client connected ${socket.client.id}`);
        socket.on('tail', (data) => {
            socket.join(data.service);
            if (typeof tails[data.service] == "undefined") {
                tails[data.service] = spawn('ssh', ['root@' + 'quality-p.company.com', 'tail -f', data.service], {
                    shell: true
                });
                tails[data.service].stdout.on('data', (data) => {
                    console.log(`got new data ${data.toString()}`);
                    io.to(data.service).emit('newLine', {
                        line: data.toString().replace(/\n/g, '<br />')
                    });
                });
            }
        });
    });
    
    
    // Bind to a port
    server.listen(3005, () => {
        console.log('running on localhost:' + 3005);
    });
    

    Client

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <script src="./node_modules/socket.io-client/dist/socket.io.js"></script>
        <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
        <script>
            $(() => {
                let socket = io();
                socket.on('connect', () => {
                    console.log('connected');
                });
                socket.on('newLine', (data) => {
                    console.log(`new data: ${data.line}`);
                    $("#tailing").append(data.line);
                });
                $('#tail').click(() => {
                    socket.emit('tail', {
                        service: $('#service').val()
                    });
                });
            });
        </script>
        <title>Title</title>
    </head>
    
    <body>
        <select id="service">
        <option id="tnet" value="/var/log/tnet">tnet</option>
        <option id="consul" value="/var/log/consul">consul</option>
    </select>
        <button id="tail">tail</button>
        <div id="tailing" style="background-color: antiquewhite;">
        </div>
    </body>
    
    </html>