Search code examples
reactjsflaskwebsocketsocket.iodata-stream

React app not refreshing the log streamed through socket.io


In my project I am using flask_socketio as the server and socket.io-client with react as the client. My main.py(flask server) is constantly reading a log file(console.log) that is continuously updating. When the start button is clicked in the UI, the data of the log file is displayed, but as my log file is updated, the updated data is not displayed in the UI. I have to refresh the page or click the button again to see the updated data. I want the data of the log file to live stream on the UI with a button click. How to fix this?

flask code

from flask import Flask, jsonify
# Needed for localhost testing.
from flask_cors import CORS, cross_origin
from flask_socketio import SocketIO, emit
from time import sleep
import pdb
import json

app = Flask(__name__)

# Socket io setup.
app.config['SECRET_KEY'] = 'secret!'
# |cors_allowed_origins| is required for localhost testing.
socket = SocketIO(app, cors_allowed_origins="*")

# For localhost testing.
CORS(app)


@socket.on('event_stream', namespace='/logConsole')
def test_connect():
    def generate():
        fname = "./src/console.log"
        with open(fname) as f:
            yield f.read()
    emit_data = next(generate())
    socket.sleep(0)
    emit('custom-server-msg', {'data': emit_data})


if __name__ == '__main__':
    socket.run(app)

React code

import React from 'react'
import io from 'socket.io-client'

class App extends React.Component {
state = { startVar: true, setVar: false };

  setSocketListeners() {
    let socket = io.connect('ws://localhost:5000/logConsole');

    socket.emit('event_stream', () => {
      console.log("Websocket connected: " + socket.connected)
    })

    socket.on('custom-server-msg', (data) => {
      console.log("Data received: " + data.data)
      const setup_logs = data.data;
      this.setState({ setup_logs });
    })
  }
  
  render() {
    return (
      <div className="App">
        <h1>Data from log file</h1>
        <button type="button" onClick={() => this.setSocketListeners()}>Start</button>
            <p>{this.state.setup_logs}</p>
      </div>
    );
  }
}

export default App;

This is how my browser console looks like--> enter image description here

And this is my backend console--> enter image description here


Solution

  • In your flask code, if you want to stream continuously, the next() needs to be called in a loop, now either that can be done by putting a infinite loop with a sleep time,

    @socket.on('event_stream')
    def test_connect():
        def generate():
            fname = "./src/console.log"
            with open(fname, "r+") as f:
                yield f.read()
        while True:
            emit_data = next(generate())
            socket.sleep(2)
            emit('custom-server-msg', {'data':emit_data})
    

    or else, if the log file is too being updated continuously, os.stat(FILE_NAME).st_mtime can be used which will check the time stamp of the file being updated, and if any change is there in the log file, next() will be called to stream it:

    @socket.on('event_stream')
    def test_connect():
        cached_stamp = 0
        def generate():
            fname = "./src/console.log"
            with open(fname, "r+") as f:
                yield f.read()
        while True:
            stamp = os.stat('./src/console.log').st_mtime
            if stamp != cached_stamp:
                cached_stamp = stamp
                emit_data = next(generate())
                emit('topo-server-msg', {'data':emit_data})