demand
You want to see real-time output logs of log files/processes on the admin page. This is useful for scenarios where you cannot directly log in to the server using SSH, you do not have a good logging tool, or you simply want to check the output of the run
Train of thought
to
Use tail -f to get real-time file streams or pM2 logs to get output streams of the PM2 process. Consider adding a subscription ID to each stream so that multiple simultaneous subscriptions can be implemented
Go to the
When it comes to real-time on a Web page, websocket naturally comes to mind. The logic is not complicated. When a client sends a subscription ID, it links it to the data event of the stream corresponding to the ID, so that the data can be passed over
The core code
This article is mainly to give some practical ideas, so there will be no explanation of how socket. IO works
Generate flow
Run the child process using child_process.exec, which returns the stdout property as a readable stream:
const exec = require('child_process').exec; Const streams = {}; * @param file * @return id */ function watchFile(file) {return watchProcess(' tail -f ${file} '); } /** * custom command line, Pm2 logs 0 * @param CMD * @return id */ function watchProcess(CMD) {return watchStream(exec(CMD). } @param stream @return id */ function watchStream(stream) {const id = date.now (); streams[id] = stream; stream._buff = ''; Stream. on('data', data => {stream._buff += data; let lines = stream._buff.split('\n'); stream._buff = lines.pop(); lines.forEach(line => stream.emit('line', line)); }); }Copy the code
Bind the stream to the socket. IO subscription
const io = SocketIO(httpServer); io.on('connection', function (socket) { socket.on('sub', function (id) { if (! Streams [id]) return socket.emit('line', 'this subscription id does not exist: ${id}'); // Const pipe = line => socket.emit('line', ansiHTML(line)); // Const pipe = line => socket.emit('line', ansiHTML(line)); // Streams [id]. On ('line', pipe); }); });Copy the code
Front-end subscription ID
var socket = io.connect(); socket.on('line', function (data) { app.rawLines.push(data); if (app.rawLines.length > 2000) app.rawLines.splice(0, app.rawLines.length - 2000); }); socket.on('connect', function () { console.log('connect succeed'); socket.emit('sub', 'time'); // get a subscription id according to various business logic and subscribe});Copy the code
The front-end page uses VUE to render elements with very many lines, so the code is not pasted, and the final result is as follows
conclusion
This thing is close to webshell function, at least the output can be displayed, plus an input interface can be remote command execution, warning here allow remote command execution is very dangerous. The main idea is the idea, the code itself is not difficult, demo has been uploaded to Github, because it is demo, various error capture and recycling processing are not perfect, this also please study ~
Did this article help you? Welcome to join the front End learning Group wechat group: