使用python3和flask_socketio ,实现服务器上的tail和top命令的实时展示,将结果实时展示在web上
tail在页面上限制了显示长度,自动滚动显示最新数据
效果如下:
tail效果
top效果
和Vue配合使用时,可能会出现如下问题
GET http://127.0.0.1:5000/socket.io/?EIO=3&transport=polling&t=M-9xlys 400 (BAD REQUEST)
Access to XMLHttpRequest at 'http://127.0.0.1:5000/socket.io/?EIO=3&transport=polling&t=M-9xlys' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
提示的很明显,就是跨域了,然后理所当然的按平时的解决方式
比如使用flask_cors或者自己利用flask的钩子函数在请求前后,设置请求头等,但是依然会报错!!!
正确的解决方式是,在实例化SocketIO时,加上 cors_allowed_origins="*"
socketio = SocketIO(app , cors_allowed_origins="*")
主要代码如下:
# coding=utf-8
import os
import re
from threading import Lock
from flask import Flask, render_template
from flask_socketio import SocketIO
from config import LOG_FILE, SECRET_KEY
app = Flask(__name__)
app.config['SECRET_KEY'] = SECRET_KEY
socketio = SocketIO(app)
# 跨域时使用下面的
# socketio = SocketIO(app,cors_allowed_origins="*")
close = False
thread = None
thread_lock = Lock()
client_num = 0 # tail页面连入数量
def get_tail_n_info(n):
'''
tail按行获取
:param n: 行数
:return:
'''
try:
tail_pipe = os.popen(f'tail -n {n} {LOG_FILE} ')
except:
print('文件不存在')
return ''
else:
tail_output = iter(tail_pipe.readlines())
tail_pipe.close()
return tail_output
def tail_close():
with thread_lock:
global close, thread, client_num
client_num -= 1
print('有客户端离开tail页面,当前页面客户端剩余数量为', client_num)
if client_num <= 0:
close = True
client_num = 0
thread = None
print('tail页面客户端全部关闭')
def get_top_info():
'''获取top命令结果,并做处理'''
top_pipe = os.popen('top -n 1')
try:
top_output = top_pipe.read()
finally:
top_pipe.close()
# 用print输出top_output看着没问题,但是用repr输出会发现有很多其他字符,这些字符会被发往前端,导致页面上数据混乱
# 暂时先直接替换处理
top_output = top_output.replace("\x1b(B\x1b[m", "").replace("\x1b(B\x1b[m\x1b[39;49m\x1b[K", "").replace(
"\x1b[?1h\x1b=\x1b[?25l\x1b[H\x1b[2J", "").replace("\x1b[39;49m\x1b[1m", "").replace("\x1b[39;49m\x1b[K",
"").replace("\x1b[39;49m",
"").replace(
"\x1b[K", "").replace("\x1b[7m", "").replace("\x1b[?1l\x1b>\x1b[45;1H", "").replace("\x1b[?12l\x1b[?25h",
"").replace("\x1b[1m", "")
_html = ''
for num, line in enumerate(top_output.split('\n')):
if num >= 6:
if num == 6:
new_line = "<table> <tr>"
else:
new_line = "<tr>"
td_list = re.split(r" +", line)
if len(td_list) > 1:
for td in td_list:
if td.strip():
new_line += f"<td>{(8-len(td))*' '+td}</td>"
new_line += "</tr>"
else:
new_line = '<div>' + line.replace(' ', " ") + '</div>'
_html += new_line
_html += '</table>'
return _html
@app.route('/')
def index():
return render_template('index.html')
@app.route('/tail', methods=['GET'])
def tail_html():
return render_template('tail.html')
@app.route('/top', methods=['GET'])
def top_html():
return render_template('top.html')
@socketio.on('connect', namespace="/shell")
def connect():
print("connect..")
@socketio.on('disconnect', namespace="/shell")
def disconnect():
print("disconnect..")
@socketio.on('open_tail', namespace="/shell")
def open_tail(message):
print('received open_tail message: ' + message.get('data', ''))
global thread, close, client_num
with thread_lock:
client_num += 1
print('有客户端进入tail页面,当前页面客户端数量为', client_num)
if thread is None:
close = False
thread = socketio.start_background_task(target=background_thread)
else: # 有其他客户端正在使用时,则先发送最近30条过去
for line in get_tail_n_info(n=30):
if line.strip():
socketio.emit('tail_response', {'text': line}, namespace='/shell')
@socketio.on('close_tail', namespace="/shell")
def close_tail(message):
print('准备关闭tail', message.get('data', ''))
tail_close()
@socketio.on('handle_top', namespace="/shell")
def handle_top(message):
print('received handle_top message: ' + message.get('data', ''))
top_info = get_top_info()
socketio.emit('top_response', {'text': top_info}, namespace='/shell')
def background_thread():
try:
tail_pipe = os.popen('tail -f ' + LOG_FILE)
except:
print('文件不存在')
return
else:
while not close:
tail_output = tail_pipe.readline()
if tail_output.strip():
socketio.emit('tail_response', {'text': tail_output}, namespace='/shell')
tail_pipe.close()
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', port=8000)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript"
src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
<script type="text/javascript"
src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
var child_num = 0;
var namespace = '/shell';
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
socket.on('tail_response', function (res) {
if (child_num < 40) {
$('#terminal').append('<div>' + res.text + '</div>');
child_num += 1
} else {
// 先删后加
$('#terminal div:first').remove();
$('#terminal').append('<div>' + res.text + '</div>');
}
$(document).scrollTop($(document).height()); // 将滚动条滚到最下方
console.log(res.text);
});
socket.on('connect', function (res) {
socket.emit('open_tail', {'data': 'I\'m connected!'});
});
//socket.on('disconnect', function (data) {
// socket.emit('close_tail', {'data': 'I\'m disconnected!'});
//});
$(window).bind('beforeunload', function () { // 离开页面前关闭tail
socket.emit('close_tail', {'data': 'I\'m leave!'});
}
);
});
$(window).resize(function () {
var cliWidth = document.body.clientWidth;
var cliHeight = document.body.clientHeight;
var divWidth = cliWidth - 2;
var divHeight = cliHeight - 2;
$('#terminal').css("width", divWidth + "px");
$('#terminal').css("height", divHeight + "px");
$(document).scrollTop($(document).height()); // 将滚动条滚到最下方
})
</script>
<style>
html, body {
height: 100%;
margin: 0;
}
.outer {
height: 100%;
}
#terminal {
height: 100%;
background-color: black;
color: white;
padding-left: 10px;
}
#terminal div {
background-color: black;
color: white;
}
</style>
</head>
<body>
<div class="outer">
<div id="terminal">
</div>
</div>
</body>
</html>
tail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript"
src="https://code.jquery.com/jquery-3.4.0.min.js"></script>
<script type="text/javascript"
src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
var namespace = "/shell";
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
socket.on('connect', function (res) {
socket.emit('handle_top', {'data': 'I\'m connected!'});
});
socket.on('top_response', function (res) {
var top_info = res.text;
document.getElementById("terminal").innerHTML = top_info;
setTimeout(function () {
socket.emit("handle_top", {"data": "handle_top"});
}, 2000)
});
});
</script>
<style type="text/css">
#terminal {
background-color: black;
color: white;
}
#terminal div {
width: 1024px;
text-align: justify;
}
table {
width: 1024px;
table-layout: fixed;
text-align: right;
}
</style>
</head>
<body>
<div>
<div id="terminal">
</div>
</div>
</body>
</html>
top.html
项目完整地址:https://github.com/Mark-IT/system_log_web
Access to XMLHttpRequest at 'http://127.0.0.1:5000/socket.io/?EIO=3&transport=polling&t=M-9xlys' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.