首页 > 代码库 > python实现websocket服务器,可以在web实时显示远程服务器日志

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 = http://www.mamicode.com/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 = http://www.mamicode.com/all_data[8:] 23         elif code_len == 127: 24             masks = all_data[10:14] 25             data = http://www.mamicode.com/all_data[14:] 26         else: 27             masks = all_data[2:6] 28             data = http://www.mamicode.com/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 =http://www.mamicode.com/ 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 = http://www.mamicode.com/%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 = http://www.mamicode.com/shake.split(\r\n\r\n, 1) 63     for line in header.split(\r\n)[1:]: 64         key, value = http://www.mamicode.com/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_password100     try:101         log_path = models.LogPath.objects.get(ip=log_ip, type_name__contains=log_type).log_path102     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         return108     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 =http://www.mamicode.com/ 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             break121         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                     break129                 log_msg = chan.recv(10000).strip()130                 print log_msg131                 send_data(conn, log_msg)132             if chan.exit_status_ready():133                 break134             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                 break140         break141 142 143 def ws_service():144 145     os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lbg.settings")146     index = 1147     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 % index156         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 += 1160 161 162 ws_service()
get_ssh的代码如下:
import paramikodef 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> 

 

 

 

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