先來發大概的技術實現,參考好多博客,地址忘記了 後面看到再加javascript
用腳本將要查看的日誌文件讀取出來,使用redis的發佈訂閱模式將內容發佈到redis的指定頻道html
寫一個websocket服務端監聽一個端口,等待長鏈接接入java
在websocket裏面 使用redis的發佈訂閱模式訂閱指定頻道,若是有用戶訪問進來就把訂閱內容推送出去python
在html創建與websocket的長鏈接,顯示websocket返回的內容linux
讀取日誌文件腳本:web
# coding:utf8 import paramiko import select import redis import sys #此方法是進行實時返回,例如tail -f這樣的命令,本次監控就用的是此方法。 def send_content_redis(ip, port, user, pwd, subscribe, logfile): redis_config = { "host": "xx.xx.xx.xx", "port": 6379 } redis_pool = redis.ConnectionPool(**redis_config) r = redis.Redis(connection_pool=redis_pool) r.pubsub_channels(subscribe) # 進行鏈接 client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(ip, port, username=user, password=pwd, timeout=4) # 開啓channel 管道 transport = client.get_transport() channel = transport.open_session() channel.get_pty() tail = 'tail -f %s' %logfile #將命令傳入管道中 channel.exec_command(tail) while True: #判斷退出的準備狀態 if channel.exit_status_ready(): break try: # 經過socket進行讀取日誌,我的理解,linux至關於客戶端,我本地至關於服務端請求獲取數據(此處如有理解錯誤,望請指出。。謝謝) rl, wl, el = select.select([channel], [], []) if len(rl) > 0: recv = channel.recv(10240) # 此處將獲取的數據解碼成gbk的存入本地日誌 print(recv.decode('utf-8', 'ignore')) r.publish(subscribe, recv.decode('utf-8', 'ignore')) # 鍵盤終端異常 except KeyboardInterrupt: channel.send("\x03") # 發送 ctrl+c channel.close() client.close() def main(): send_content_redis(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6]) if __name__ == '__main__': main()
websocket 服務端代碼redis
# coding:utf8 import os import sys import tornado.websocket import tornado.web import tornado.ioloop from tornado import gen from tornado.escape import to_unicode import redis import subprocess class SubWebSocket(tornado.websocket.WebSocketHandler): """ 此handler處理遠程日誌查看 """ # 容許跨域請求 def check_origin(self, origin): return True def open(self, *args, **kwargs): print("opened") self.ip = self.get_argument('ip', '') self.redis_ip = self.get_argument('redis_ip', '') self.redis_port = self.get_argument('redis_port', '') self.pubscribe = self.get_argument('pubscribe', '') self.user = self.get_argument('user', '') self.password = self.get_argument('password', '') self.port = self.get_argument('port', '') self.logfile = self.get_argument('logfile', '') self.sub = subprocess.Popen('python 1.py %s %s %s %s %s %s' %(self.ip, self.port, self.user, self.password, self.pubscribe, self.logfile ), shell=True) @gen.coroutine def on_message(self, message): r = redis.StrictRedis(host=self.redis_ip) # 訂閱頻道,服務器和日誌路徑肯定一個頻道 channel = r.pubsub() channel.subscribe(self.pubscribe) try: while True: data = channel.get_message() if not data: # 若是讀取不到消息,間隔必定時間,避免無謂的CPU消耗 yield gen.sleep(0.05) continue if data["type"] == "message": line = data["data"] self.write_message(line) except tornado.websocket.WebSocketClosedError: self.close() def on_close(self): print("closed") self.sub.kill() class Application(tornado.web.Application): def __init__(self): handlers = [ (r'/', MainHandler), # 提供瀏覽頁面,頁面中的JS與服務器創建鏈接 (r'/log', SubWebSocket), # 處理遠程日誌實時查看,稍微複雜 ] settings = { "debug": True, "template_path": os.path.join(os.path.dirname(__file__), "templates"), "static_path": os.path.join(os.path.dirname(__file__), "static"), } super(Application, self).__init__(handlers, **settings) class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html") def main(): app = Application() app.listen(8888) tornado.ioloop.IOLoop.current().start() if __name__ == '__main__': main()
html代碼:shell
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript"> window.onload = function(){ if("WebSocket" in window){ console.log('瀏覽器支持websocket!');var ws = new WebSocket("ws://xx.xx.xx.xx:8888/log?ip={{ip}}&" + "redis_ip={{redis_ip}}&redis_port={{redis_port}}&pubscribe={{pubscribe}}&" + "user={{user}}&password={{password}}&port={{port}}&logfile={{logfile}}"); var div = document.getElementById('content'); ws.onopen = function () { ws.send('websocket 連接創建第一次發送數據!'); console.log('第一次發送數據結束!'); }; ws.onmessage = function (evt) { console.log("開始接收數據!"); var received_msg = evt.data; div.innerText = div.innerText + received_msg; content_end.scrollIntoView(); }; }else{ console.log("你的瀏覽器不支持websocket") } } </script> </head> <body> <div id="sse"> <h3>服務啓動日誌{{salt_key_id}}</h3> </div> <div id="content"> </div> <div id="content_end" style="height:100px; overflow:hidden"> </div> </body> </html>
遇到的問題:跨域
開始使用的一個單獨的index.html調試的代碼沒問題,放到項目中出現了不能跨域請求的問題 ,而後我就把跨域請求關掉了,不檢查是否跨域瀏覽器