大致思路:
1.利用tornado提供的websocket功能與瀏覽器建立長連接,讀取實時日志并輸出到瀏覽器
2.寫一個實時讀取日志的腳本,利用saltstack遠程執行,并把實時日志發往redis中。
3.tornado讀取redis中的信息,發往瀏覽器。
此過程用到了redis的發布和訂閱功能。
先看一下tornado中是如何處理的:
import osimport sysimport tornado.websocketimport tornado.webimport tornado.ioloopimport redisimport salt.clientfrom tornado import genfrom tornado.escape import to_unicodefrom logs.utility import get_last_linesfrom logs import settingsclass SubWebSocket(tornado.websocket.WebSocketHandler): """ 此handler處理遠程日志查看 """ def open(self, *args, **kwargs): print("opened") @gen.coroutine def on_message(self, message): # 主機名,要查看的日志路徑,運行腳本的命令這些信息從瀏覽器傳過來 hostname, log_path, cmd = message.split("||") local = salt.client.LocalClient() r = redis.StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, password=settings.REDIS_PASSWD, db=5) # 訂閱頻道,服務器和日志路徑確定一個頻道 key = settings.LOG_KEY.format(server=hostname.strip(), log_path=log_path.strip()) channel = r.pubsub() channel.subscribe(key) # 異步方式執行命令,遠程運行腳本 local.cmd_async(hostname, "cmd.run", [cmd]) try: while True: data = channel.get_message() if not data: # 如果讀取不到消息,間隔一定時間,避免無謂的CPU消耗 yield gen.sleep(0.05) continue if data["type"] == "message": line = format_line(data["data"]) self.write_message(line) except tornado.websocket.WebSocketClosedError: self.close() def on_close(self): global FLAG FLAG = False print("closed")def format_line(line): line = to_unicode(line) if "INFO" in line: color = "#46A3FF" elif "WARN" in line: color = "#FFFF37" elif "ERROR" in line: color = "red" elif "CRITICAL" in line: color = "red" else: color = "#FFFFFF" return "<span >{}</span>".format(color, line)class EchoWebSocket(tornado.websocket.WebSocketHandler): def open(self): print("WebSocket opened") @gen.coroutine def on_message(self, message): log = message print "log file: ", log try: with open(log, 'r') as f: for line in get_last_lines(f): line1 = format_line(line) self.write_message(line1) while True: line = f.readline() if not line: yield gen.sleep(0.05) continue self.write_message(format_line(line.strip())) except tornado.websocket.WebSocketClosedError as e: print e self.close() # def check_origin(self, origin): # print origin, self.request.headers.get("Host") # # super(EchoWebSocket, self).check_origin() # return True def on_close(self): print("WebSocket closed")class Application(tornado.web.Application): def __init__(self): handlers = [ (r'/log/', MainHandler), # 提供瀏覽頁面,頁面中的JS與服務器建立連接 (r'/log/local', EchoWebSocket), # 處理本地日志實時查看,比較簡單 (r'/log/remote', 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): # 要查看的日志路徑 log = self.get_argument("log", None) # hostname實際上是saltstack中這臺機器對應的minion id hostname = self.get_argument("hostname", None) # 本地日志還是遠程日志 type = self.get_argument("type", "local") # 運行讀取實時日志的腳本,參數比較多,后面會有 cmd = self.get_argument("cmd", "") context = { "log": log, "hostname": hostname, "type": type, "cmd": cmd, } self.render("index.html", **context)
新聞熱點
疑難解答