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

一、开始的话

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

  之前做了一个web版的发布系统,但没实现在线看日志,每次发布版本后,都需要登录到服务器上查看日志,非常麻烦,为了偷懒,能在页面点几下按钮完成工作,所以这几天查找了这方面的资料,实现了这个功能,瞬间觉的看日志什么的,太方便了,以后也可以给开发们查日志,再也不用麻烦运维了,废话少说,先看效果吧。

二、代码

  在实现这功能前,看过别人的代码,发现很多都是只能在web上显示本地的日志,不能看远程主机上的日志,有些能看远程日志的是引用了其他框架(例如bottle,tornado)来实现的,而且所有这些都是重写thread的run方法来实现的,由于本人技术太菜,不知道怎么改成自己需要的样子,而且我是用django这个web框架的,不想引用太多框架,搞的太复杂,所以用python来实现websocket服务器。由于技术问题,代码有点粗糙,不过能实现功能就行,先将就着用吧。

执行下面命令启动django和websocketserver

nohup python manage.py runserver 10.1.12.110 &
nohup python websocketserver.py &

  启动websocket后,接收到请求,起一个线程和客户端握手,然后根据客户端发送的ip和type,去数据库查找对应的日志路径,用paramiko模块ssh登录到远程服务器上tail查看日志,再推送给浏览器,服务端完整代码如下:

  1 # coding:utf-8
  2 import struct
  3 import base64
  4 import hashlib
  5 import socket
  6 import threading
  7 from public.public import get_ssh
  8 import os
  9
 10
 11 def recv_data(conn):    # 服务器解析浏览器发送的信息
 12     try:
 13         all_data = conn.recv(1024)
 14         if not len(all_data):
 15             return False
 16     except:
 17         pass
 18     else:
 19         code_len = ord(all_data[1]) & 127
 20         if code_len == 126:
 21             masks = all_data[4:8]
 22             data = all_data[8:]
 23         elif code_len == 127:
 24             masks = all_data[10:14]
 25             data = all_data[14:]
 26         else:
 27             masks = all_data[2:6]
 28             data = all_data[6:]
 29         raw_str = ""
 30         i = 0
 31         for d in data:
 32             raw_str += chr(ord(d) ^ ord(masks[i % 4]))
 33             i += 1
 34         return raw_str
 35
 36
 37 def send_data(conn, data):   # 服务器处理发送给浏览器的信息
 38     if data:
 39         data = str(data)
 40     else:
 41         return False
 42     token = "\x81"
 43     length = len(data)
 44     if length < 126:
 45         token += struct.pack("B", length)    # struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。
 46     elif length <= 0xFFFF:
 47         token += struct.pack("!BH", 126, length)
 48     else:
 49         token += struct.pack("!BQ", 127, length)
 50     data = ‘%s%s‘ % (token, data)
 51     conn.send(data)
 52     return True
 53
 54
 55 def handshake(conn, address, thread_name):
 56     headers = {}
 57     shake = conn.recv(1024)
 58     if not len(shake):
 59         return False
 60
 61     print (‘%s : Socket start handshaken with %s:%s‘ % (thread_name, address[0], address[1]))
 62     header, data = shake.split(‘\r\n\r\n‘, 1)
 63     for line in header.split(‘\r\n‘)[1:]:
 64         key, value = line.split(‘: ‘, 1)
 65         headers[key] = value
 66
 67     if ‘Sec-WebSocket-Key‘ not in headers:
 68         print (‘%s : This socket is not websocket, client close.‘ % thread_name)
 69         conn.close()
 70         return False
 71
 72     MAGIC_STRING = ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11‘
 73     HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n"  74                        "Upgrade:websocket\r\n"  75                        "Connection: Upgrade\r\n"  76                        "Sec-WebSocket-Accept: {1}\r\n"  77                        "WebSocket-Origin: {2}\r\n"  78                        "WebSocket-Location: ws://{3}/\r\n\r\n"
 79
 80     sec_key = headers[‘Sec-WebSocket-Key‘]
 81     res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())
 82     str_handshake = HANDSHAKE_STRING.replace(‘{1}‘, res_key).replace(‘{2}‘, headers[‘Origin‘]).replace(‘{3}‘, headers[‘Host‘])
 83     conn.send(str_handshake)
 84     print (‘%s : Socket handshaken with %s:%s success‘ % (thread_name, address[0], address[1]))
 85     print ‘Start transmitting data...‘
 86     print ‘- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -‘
 87     return True
 88
 89
 90 def dojob(conn, address, thread_name):
 91     from mode import models
 92     handshake(conn, address, thread_name)     # 握手
 93     log_info = recv_data(conn)
 94     log_ip = log_info.split(":")[0]
 95     log_type = log_info.split(":")[1]
 96
 97     auth_ = models.Authentication.objects.get(ip=log_ip)
 98     user = auth_.a_user
 99     pwd = auth_.a_password
100     try:
101         log_path = models.LogPath.objects.get(ip=log_ip, type_name__contains=log_type).log_path
102     except Exception, e:
103         send_data(conn, e)
104         conn.close()
105         print "Error:" + str(e)
106         print (‘%s : Socket close with %s:%s‘ % (thread_name, address[0], address[1]))
107         return
108     conn.setblocking(0)                       # 设置socket为非阻塞
109     ssh = get_ssh(log_ip, user, pwd)
110     ssh_t = ssh.get_transport()
111     chan = ssh_t.open_session()
112     chan.setblocking(0)   # 设置非阻塞
113     chan.exec_command(‘tail -f %s‘ % log_path)
114     while True:
115         clientdata = recv_data(conn)
116         if clientdata is not None and ‘quit‘ in clientdata:
117             print (‘%s : Socket close with %s:%s‘ % (thread_name, address[0], address[1]))
118             send_data(conn, ‘close connect‘)
119             conn.close()
120             break
121         while True:
122             while chan.recv_ready():
123                 clientdata1 = recv_data(conn)
124                 if clientdata1 is not None and ‘quit‘ in clientdata1:
125                     print (‘%s : Socket close with %s:%s‘ % (thread_name, address[0], address[1]))
126                     send_data(conn, ‘close connect‘)
127                     conn.close()
128                     break
129                 log_msg = chan.recv(10000).strip()
130                 print log_msg
131                 send_data(conn, log_msg)
132             if chan.exit_status_ready():
133                 break
134             clientdata2 = recv_data(conn)
135             if clientdata2 is not None and ‘quit‘ in clientdata2:
136                 print (‘%s : Socket close with %s:%s‘ % (thread_name, address[0], address[1]))
137                 send_data(conn, ‘close connect‘)
138                 conn.close()
139                 break
140         break
141
142
143 def ws_service():
144
145     os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lbg.settings")
146     index = 1
147     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
148     sock.bind(("127.0.0.1", 12345))
149     sock.listen(100)
150
151     print (‘\r\n\r\nWebsocket server start, wait for connect!‘)
152     print ‘- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -‘
153     while True:
154         connection, address = sock.accept()
155         thread_name = ‘thread_%s‘ % index
156         print (‘%s : Connection from %s:%s‘ % (thread_name, address[0], address[1]))
157         t = threading.Thread(target=dojob, args=(connection, address, thread_name))
158         t.start()
159         index += 1
160
161
162 ws_service()
get_ssh的代码如下:
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"

打开页面时,自动连接websocket服务器,完成握手,并发送ip和type给服务端,所以可以看不同类型,不同机器上的日志,

页面代码如下:

<!DOCTYPE html>
<html>
<head>
{% include ‘head_script.html‘ %}
<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/";
            var ips="{{ ip }}";
            var types="{{ type }}";
            var log_msg = ips +":"+ types;

            console.log(log_msg);
            try {
                socket = new WebSocket(host);
                socket.onopen = function () {
                    log(‘Connected‘);
                    socket.send(log_msg);
                };
                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>
{% include ‘foot_script.html‘ %}
</html> 
 
时间: 2024-10-05 14:19:43

python实现websocket服务器,可以在web实时显示远程服务器日志的相关文章

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

功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息 一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看.你还在用ajax每隔段时间去获取服务器日志?out了,试试用websocket方式吧 我用bottle框架,写了个websocket服务端,浏览器连接到websocket server,再用python subprocess获取远程服务器的日志信息,subprocess,就是用Popen调用shell的sh

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

越来越多的项目需要用到实时消息的推送与接收,怎样用Python现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等. 支持不同的开发语言:    GoEasy推送 提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通过R

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

越来越多的项目需要用到实时消息的推送与接收,怎样用ASP.NET现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等. 支持不同的开发语言:    GoEasy推送 提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通过

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

越来越多的项目需要用到实时消息的推送与接收,怎样用Ruby现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等. 支持不同的开发语言:    GoEasy推送 提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通过Res

C# Web实时消息后台服务器推送技术---GoEasy

越来越多的项目需要用到实时消息的推送与接收,怎样用C#现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等. 支持不同的开发语言:    GoEasy推送 提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通过Restf

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

越来越多的项目需要用到实时消息的推送与接收,怎样用node.js实现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等. 支持不同的开发语言:    GoEasy推送 提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通

Web实时消息后台服务器推送技术GoEasy(支持多语言)---附GoEasy web 推送实例

越来越多的项目需要用到实时消息的推送与接收,怎样实现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等. 支持不同的开发语言:    GoEasy推送 提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通过Restful

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

在Linux操作系统中,经常需要查看日志文件的实时输出内容,通常会使用tail -f或者tailf命令.查看实时日志可能会需要首先SSH连上Linux主机,步骤很麻烦不说,如果是生产环境的服务器,可能还会控制各种权限.基于Web的实时日志可以解决这个问题. 由于传统的HTTP协议是请求/响应模式,而实时日志需要不定时的持续的输出,由服务器主动推送给客户端浏览器.所以这里使用的是HTML5的WebSocket协议. 按照惯例,先上图: Java后台 JSR 356是Java实现WebSocket的

远程服务器返回错误: 404错误、远程服务器返回错误:500错误、 HttpWebResponse远程服务器返回错误:(404、500) 错误。

现象 我们编码实现请求一个页面时,请求的代码类似如下代码: HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strUrl);req.UserAgent = "MSIE6.0";req.Method = "GET";HttpWebResponse res = (HttpWebResponse)req.GetResponse();StreamReader sr = new StreamReader(res.G