1

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

  • Are you seeing your log statements at all in the browser console when you receive a custom-server-msg event? – rdrw Mar 10 '21 at 15:36
  • @rdrw yeah! I'll add the screenshot of my browser console too. Please have a look – Shubhi Manral Mar 10 '21 at 18:09
  • I'm not quite sure on this but the context of the `custom-server-msg` handler may not be the same as the class instance, therefore not really calling `setState` as expected and silently failing. I'm curious if you log the value of `this` in the emit handler if it will be `undefined` or a context other than the current class component. – rdrw Mar 10 '21 at 20:19

1 Answers1

0

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})
Bhavesh
  • 16
  • 1
  • Hi @Bhavesh! Thank you for such a detailed answer. Just tried it and it works perfect. Earlier I used `transports: ['websocket']` to stop continuous polling, but it raises an error. `os.stat(FILE_NAME).st_mtime` solves my polling problem. – Shubhi Manral Mar 24 '21 at 10:27