一、开始的话

  使用python实现websocket服务器,可以在浏览器上实时显示远程服务器的日志。

  之前写了一个发布系统,每次发布版本后,为了了解发布情况(进度、是否有错误)都会登录到服务器上查看日志,有点麻烦,如果发布的服务器比较多,难道要登录到每台服务器去看日志吗?作为新时代的运维,太不能接收这种重复操作的体力劳动了,于是一个看日志的功能就这么诞生了。下面是效果图,页面丑陋不堪,将就着吧。

二、行动

  打开页面时,自动连接websocket服务器,完成握手,并发送ip和type给服务端,所以可以看不同类型,不同机器上的日志,websocket服务器接收到信息后,去数据库查找对应的日志路径和主机账号密码,然后起一个线程ssh登录到远程服务器上tail查看日志,再推送给浏览器,代码如下:

 # coding:utf-8
import os
import struct
import base64
import hashlib
import socket
import threading
import paramiko def get_ssh(ip, user, pwd):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip, 22, user, pwd, timeout=15)
return ssh
except Exception, e:
print e
return "False" def recv_data(conn): # 服务器解析浏览器发送的信息
try:
all_data = conn.recv(1024)
if not len(all_data):
return False
except:
pass
else:
code_len = ord(all_data[1]) & 127
if code_len == 126:
masks = all_data[4:8]
data = all_data[8:]
elif code_len == 127:
masks = all_data[10:14]
data = all_data[14:]
else:
masks = all_data[2:6]
data = all_data[6:]
raw_str = ""
i = 0
for d in data:
raw_str += chr(ord(d) ^ ord(masks[i % 4]))
i += 1
return raw_str def send_data(conn, data): # 服务器处理发送给浏览器的信息
if data:
data = str(data)
else:
return False
token = "\x81"
length = len(data)
if length < 126:
token += struct.pack("B", length) # struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。
elif length <= 0xFFFF:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
data = '%s%s' % (token, data)
conn.send(data)
return True def handshake(conn, address, thread_name): # 握手建立连接
headers = {}
shake = conn.recv(1024)
if not len(shake):
return False print ('%s : Socket start handshaken with %s:%s' % (thread_name, address[0], address[1]))
header, data = shake.split('\r\n\r\n', 1)
for line in header.split('\r\n')[1:]:
key, value = line.split(': ', 1)
headers[key] = value if 'Sec-WebSocket-Key' not in headers:
print ('%s : This socket is not websocket, client close.' % thread_name)
conn.close()
return False MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: {1}\r\n" \
"WebSocket-Origin: {2}\r\n" \
"WebSocket-Location: ws://{3}/\r\n\r\n" sec_key = headers['Sec-WebSocket-Key']
res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())
str_handshake = HANDSHAKE_STRING.replace('{1}', res_key).replace('{2}', headers['Origin']).replace('{3}', headers['Host'])
conn.send(str_handshake) # 发送建立连接的信息
print ('%s : Socket handshaken with %s:%s success' % (thread_name, address[0], address[1]))
print 'Start transmitting data...'
print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
return True def dojob(conn, address, thread_name):
handshake(conn, address, thread_name) # 握手
conn.setblocking(0) # 设置socket为非阻塞 ssh = get_ssh('192.168.1.1', 'root', '') # 连接远程服务器(日志所在的服务器)
ssh_t = ssh.get_transport()
chan = ssh_t.open_session()
chan.setblocking(0) # 设置非阻塞
chan.exec_command('tail -f /var/log/messages') # 执行查看日志命令 while True: # 下面这个逻辑是在看日志时能停止或继续,写的有点混乱,还没想到优化的方案
clientdata = recv_data(conn)
if clientdata is not None and 'quit' in clientdata: # 当浏览器点击stop按钮或close按钮时,断开连接
print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
send_data(conn, 'close connect')
conn.close()
break
while True:
while chan.recv_ready():
clientdata1 = recv_data(conn)
if clientdata1 is not None and 'quit' in clientdata1:
print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
send_data(conn, 'close connect')
conn.close()
break
log_msg = chan.recv(10000).strip() # 接收日志信息
print log_msg
send_data(conn, log_msg)
if chan.exit_status_ready():
break
clientdata2 = recv_data(conn)
if clientdata2 is not None and 'quit' in clientdata2:
print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))
send_data(conn, 'close connect')
conn.close()
break
break def ws_service(): index = 1
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("127.0.0.1", 12345))
sock.listen(100) print ('\r\n\r\nWebsocket server start, wait for connect!')
print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'
while True:
connection, address = sock.accept()
thread_name = 'thread_%s' % index
print ('%s : Connection from %s:%s' % (thread_name, address[0], address[1]))
t = threading.Thread(target=dojob, args=(connection, address, thread_name))
t.start()
index += 1 ws_service()

页面代码如下:

<!DOCTYPE html>
<html>
<head>
<title>WebSocket</title> <style>
#log {
width: 440px;
height: 200px;
border: 1px solid #7F9DB9;
overflow: auto;
}
pre {
margin: 0 0 0;
padding: 0;
border: hidden;
background-color: #0c0c0c;
color: #00ff00;
}
#btns {
text-align: right;
}
</style> <script>
var socket;
function init() {
var host = "ws://127.0.0.1:12345/"; try {
socket = new WebSocket(host);
socket.onopen = function () {
log('Connected');
};
socket.onmessage = function (msg) {
log(msg.data);
var obje = document.getElementById("log"); //日志过多时清屏
var textlength = obje.scrollHeight;
if (textlength > 10000) {
obje.innerHTML = '';
}
};
socket.onclose = function () {
log("Lose Connection!");
$("#start").attr('disabled', false);
$("#stop").attr('disabled', true);
};
$("#start").attr('disabled', true);
$("#stop").attr('disabled', false);
}
catch (ex) {
log(ex);
}
}
window.onbeforeunload = function () {
try {
socket.send('quit');
socket.close();
socket = null;
}
catch (ex) {
log(ex);
}
};
function log(msg) {
var obje = document.getElementById("log");
obje.innerHTML += '<pre><code>' + msg + '</code></pre>';
obje.scrollTop = obje.scrollHeight; //滚动条显示最新数据
}
function stop() {
try {
log('Close connection!');
socket.send('quit');
socket.close();
socket = null;
$("#start").attr('disabled', false);
$("#stop").attr('disabled', true);
}
catch (ex) {
log(ex);
}
}
function closelayer() {
try {
log('Close connection!');
socket.send('quit');
socket.close();
socket = null;
}
catch (ex) {
log(ex);
}
var index = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引
parent.layer.close(index); //再执行关闭
}
</script> </head> <body onload="init()">
<div class="row">
<div class="col-lg-12">
<div id="log" style="width: 100%;height:440px;background-color: #0c0c0c;overflow:scroll;overflow-x: auto;"></div>
<br>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div id="btns">
<input disabled="disabled" type="button" class="btn btn-primary btn-sm" value="start" id="start" onclick="init()">
<input disabled="disabled" type="button" class="btn btn-primary btn-sm" value="stop" id="stop" onclick="stop()" >
<input type="button" class="btn btn-primary btn-sm" value="close" id="close" onclick="closelayer()" >
</div>
</div>
</div>
</body> </html>
 

python2.7实现websocket服务器,可以在web实时显示远程服务器日志的更多相关文章

  1. python实现websocket服务器,可以在web实时显示远程服务器日志

    一.开始的话 使用python简单的实现websocket服务器,可以在浏览器上实时显示远程服务器的日志信息. 之前做了一个web版的发布系统,但没实现在线看日志,每次发布版本后,都需要登录到服务器上 ...

  2. python websocket网页实时显示远程服务器日志信息

    功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息 一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看.你还在用 ...

  3. 运维开发:python websocket网页实时显示远程服务器日志信息

    功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息 一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看.你还在用 ...

  4. PHP Web实时消息后台服务器推送技术---GoEasy

    越来越多的项目需要用到实时消息的推送与接收,怎样用php实现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 ...

  5. Ruby Web实时消息后台服务器推送技术---GoEasy

    越来越多的项目需要用到实时消息的推送与接收,怎样用Ruby实现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推 ...

  6. Maven部署web应用到远程服务器

    Maven部署web应用到远程服务器 找到了一个很详细的地址:http://www.mkyong.com/maven/how-to-deploy-maven-based-war-file-to-tom ...

  7. 远哥谈 使用WebSocket开发在线实时看远程服务器log日志的工具

    我们开发软件的,通常会有一个测试环境/开发环境,但是系统开发完成后,还会有一个生产环境,也叫正式环境.正式环境我们一般是不能让开发人员去远程登录和维护的,一般正规的生产环境是专门的负责人员去负责更新, ...

  8. Java用WebSocket + tail命令实现Web实时日志

    原文:http://blog.csdn.net/xiao__gui/article/details/50041673 在Linux操作系统中,经常需要查看日志文件的实时输出内容,通常会使用tail - ...

  9. 通过VS2017发布.net core程序并使用Web 部署到远程服务器最新教程

    最近一个项目中,为App开发后台接口,技术选型为最新 .net core版本,使用.net core开发web api接口过程中,为了方便app团队成员直接在线调用接口,找了公网上的一台服务器做为ap ...

随机推荐

  1. 取消word中所有超链接

    我用的是M-O-W2007,最近做了一个文档,里面很多网上拷下来的图片,很多都有超链接,不想一个一个的取消,请高手帮忙教下有没有措施一次性都取消? 最佳答案: 三种方法,任意选用其中之一: 第一种方法 ...

  2. Http常见状态码说明

    一些常见的状态码为: 200 - 服务器成功返回网页404 - 请求的网页不存在503 - 服务不可用 详细分解: 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码.代码 说明100 ...

  3. day 2Linux软件从主机安装到服务器和安装JDK软件

    软件安装 1.如何上传安装包到服务器**可以使用图形化工具,如: filezilla**可以使用sftp工具: alt+p 调出后,用put命令上传上传(如果不cd指定目录,则上传到当前用户的主目录) ...

  4. $.each()与$(selector).each()

    $.each()与$(selector).each()不同, 后者专用于jquery对象的遍历, 前者可用于遍历任何的集合(无论是数组或对象),如果是数组,回调函数每次传入数组的索引和对应的值(值亦可 ...

  5. 2017年最新cocoapods安装教程(解决淘宝镜像源无效以及其他源下载慢问题)

    首先,先来说一下一般的方法吧,就是把之前的淘宝源替换成一个可用的的源: 使用终端查看当前的源 gem sources -l gem sources -r https://rubygems.org/ # ...

  6. 关于springmvc 返回json数据null字段的显示问题-转https://blog.csdn.net/qq_23911069/article/details/62063450

    最近做项目(ssm框架)的时候,发现从后台返回的json(fastjson)数据对应不上实体类,从数据库查询的数据,如果对应的实体类的字段没有信息的话,json数据里面就不显示,这不是我想要的结果,准 ...

  7. 第一章 spring起步

    点击 网址 http://start.spring.io/ 就可以获得spring-boot的项目结构. 如下: 将项目解压到自己的项目中,然后找到mian函数所在启动类.运行.出现: 表示已经运行了 ...

  8. Java性能分析之线程栈详解与性能分析

    Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...

  9. mac上安装nginx

    终端执行: brew install nginx nginx 默认安装在 /usr/local/Cellar/nginx/1.12.2 conf 文件默认安装在 /usr/local/etc/ngin ...

  10. Spring 框架中 ModelAndView、Model、ModelMap 的区别

    Model Model 是一个接口, 其实现类为ExtendedModelMap,继承了ModelMap类. public class ExtendedModelMap extends ModelMa ...