先来发大概的技术实现,参考好多博客,地址忘记了 后面看到再加
用脚本将要查看的日志文件读取出来,使用redis的发布订阅模式将内容发布到redis的指定频道
写一个websocket服务端监听一个端口,等待长连接接入
在websocket里面 使用redis的发布订阅模式订阅指定频道,如果有用户访问进来就把订阅内容推送出去
在html建立与websocket的长连接,显示websocket返回的内容
读取日志文件脚本:
# coding:utf8import paramikoimport selectimport redisimport 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 服务端代码
# coding:utf8import osimport sysimport tornado.websocketimport tornado.webimport tornado.ioloopfrom tornado import genfrom tornado.escape import to_unicodeimport redisimport subprocessclass 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代码:
Title 服务启动日志{ {salt_key_id}}
遇到的问题:
开始使用的一个单独的index.html调试的代码没问题,放到项目中出现了不能跨域请求的问题 ,然后我就把跨域请求关掉了,不检查是否跨域