如何将服务端log实时在页面输出展示
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将服务端log实时在页面输出展示相关的知识,希望对你有一定的参考价值。
参考技术A 在部署状态页面点击查看日志,将会打开日志页面,下个图就是日志的输出页面。简约时尚小清新的日志页面出来了,是不是很想一个terminal,有没有觉得俺弄的页面很漂亮。哈哈哈哈,写不好前端的运维不是好DBA。
刚开始拿到这个需求,咋觉得很难。因为在服务器上我们看一个日志,经常会tail -f 看着刷屏的感觉倍爽。如何在web页面上实现tailf的效果呢?
后来想想这样实现是可行的。前端写一个js定时器,不断的发ajax请求到后台,每回取出一段日志。后台取日志可以直接调用系统命令,或者直接调取shell脚本,取日志,判断日志文件是否存在,是否为空,返回数据的起始位置等,都可以交给shell来做。事实上我就是这么干的。后台程序写一堆shell命令调用有点别扭。想到解决方案之后,实现的过程就比较简单了。我的开发环境是基于python的Django框架来做的,现在就展示这个功能的实现过程。
Django部分
点击查看日志的链接之后会调到views里的ajxGetLog方法,该方法需要两个参数,项目id和主机id,这两个参数主要是用来确定日志文件的位置。该方法返回行号。
execcommand是封装的一个subprocess.Popen的一个工具函数,返回一个列表分别是标准输出,错误输出和shell returncode
def ajxGetLog(request,pid,hid):
project = Project.objects.get(pk=pid)
host = Host.objects.get(pk=hid)
scriptname = '%s%s'%(base_path,'get_log.sh')
res = execcommand(['sh',scriptname,host.hostname,project.servicename])
#如果错误输出不为空,直接返回错误输出
if not res[1]:
try:
#res[0]为行号,如果大于20行,从当前行的上面20行开始输出,为了用户体验,你懂得
if int(res[0]) > 20:
line = int(res[0]) - 20
if int(res[0]) == 0:
line = 1
return render(request,'logs.html','pid':pid,'hid':hid,'line':line)
except Exception,e:
print e
return HttpResponse(e)
else:
return HttpResponse(res[1])
html部分首席写一个ajax请求,传递到后台取日志内容的方法,传递三个参数项目id,主机id,和行号,后台将会返回对应行号的日志内容。该方法代码如下:
def ajxGetLogHandle(request,pid,hid,line):
project = Project.objects.get(pk=pid)
host = Host.objects.get(pk=hid)
scriptname = '%s%s'%(base_path,'get_log.sh')
res = execcommand(['sh',scriptname,host.hostname,project.servicename,line])
if not res[1] and res[0].strip():
return HttpResponse(res[0])
else:
return HttpResponse(500)
redis主动向页面push数据
对于页面上定时刷新显示的数据,之前一直都是比较“传统”的思想——那就是“页面通过ajax请求后台,后台响应后把数据返回给前台展示,如此反复……”,而自己也从来没有过“服务端主动向页面推送数据”的概念。
现在需要用到redis的发布/订阅,页面“订阅”某一channel,服务端在某一channel“发布”内容。服务端发布后,客户端可以通过订阅实时将刚刚发布的内容展示出来。
说说我探索的解决过程吧。。从用tomcat的WebSocketServlet,但发现已经不推荐使用(废弃)了,加上它必须依赖tomcat,并且前台订阅的逻辑也难以表达,所以不行;后来又想到用javax.websocket-api.jar的@ServerEndpoint,但是,还是卡住了。。因为也是难以体现redis的发布/订阅功能。
后来一直苦于用何种解决方案,这时,socket.io+redis+node.js实时聊天的例子让我眼前一亮。。
?
好了,说说我对nodejs的理解——Node是一个Javascript运行环境(runtime),就是Js的服务器,类似tomcat或weblogic是java代码的运行服务器一样。。安装nodejs之后,最新版已经带了npm,而npm是用于安装各种支持nodejs扩展的客户端的。比如你想要用到redis,就必须安装redis的nodejs客户端。把nodejs配好环境变量后,使用如下命令即可安装redis:
npm install redis
安装好后,它默认是在C:\Users\xxx\node_modules多一个redis的文件夹的,但如果你安装的nodejs不在C盘,最好是把刚刚安装的module拷贝至你安装包下的node_modules目录下。安装好后,就可以在Js中使用require(‘redis‘)了。
socket.io连接于browser和nodejs的http服务器之间,可用于二者之间同步数据。同样,它也需要通过npm命令安装。
?
解决过程如下:
编写pubsub.js作为前台页面订阅某一渠道的服务端(便与测试,我这里是把渠道写死的: var c = ‘testchannel‘;):
?
var server = require(‘http‘).createServer(function (request, response) { response.writeHead(200, {‘Content-Type‘: ‘text/plain‘}); response.end(‘Hello World\n‘); }).listen(1379); var redis = require(‘redis‘); var redisclient = redis.createClient(); var sub = function(c) { var c = ‘testchannel‘; redisclient.subscribe(c, function(e) { console.log(‘subscribe channel : ‘ + c); }); } sub(); console.log(‘Server running at http://127.0.0.1:1379/‘); var io = require(‘socket.io‘)(server); io.on(‘connection‘, function(socket) { redisclient.on(‘message‘, function(error, msg) { console.log(‘connection‘); console.log(msg); socket.emit(‘msgReceived‘, msg); }); })
?
?
然后,定义用来显示订阅消息的前台页面:
?
<html> <head> <script src="F:\nodejs\node_modules\socket.io\node_modules\socket.io-client\socket.io.js"></script> <script src="jquery-1.7.2.min.js"></script> </head> <body> <div style="width:100px; height:200px; border:1px solid red" id="show"></div> </script>hello world <script type="text/javascript"> var div = $("#show"); console.log("hello"); var socket = io(‘http://localhost:1379‘); socket.on(‘connection‘, function() { console.log(‘connection setup for socket.io‘) }); socket.on(‘msgReceived‘, function(msg) { //alert(div.html()); div.append(msg + "<br/>"); //alert(msg); }) </script> </body> </html>
?页面中var socket = io(‘http://localhost:1379‘);一行是关键,它和pubsub.js中?listen(1379)里对应。表明位于本地的Nodejs服务器的1379端口,在pubsub.js里listen(1379)被监听了。
?
用任一浏览器(我这里是谷歌的),打开刚定义的前台页面。
?页面此时没内容,因为还未开启pubsub.js发起订阅请求。
OK,我们开始运行pubsub.js发起请求
?
接下来要用redis发布请求咯:
打开redis服务:
?
?再打开一客户端,向testchannel发布消息:
?
运行pubsub.js: node pubsub.js
可以看到:
?
开启一redis客户端,可看到订阅内容:
?
重点来了,页面也可显示订阅的内容:
?
同理,你在java中写发布到渠道testchannel的的代码,前台页面一样可以显示。
而你开启另外一个浏览器(比如搜狗),也是可以看到实时订阅的消息的:
?这说明,已经做到了redis主动向页面push数据,实时推送的效果。
总结:nodejs作为页面订阅的pubsub.js的运行服务端,socket.io则是类似websocket的功能,实现nodejs和浏览器之间交流的桥梁,是redis向页面推送数据的关键。另外注意nodejs用到redis和socket.io这2个module,必须要先用npm命令安装扩展。注意redis发布订阅思想在实时推送中的模型——前台页面订阅某一渠道的消息,而发布的话则是由redis来发布的。
?
以上是关于如何将服务端log实时在页面输出展示的主要内容,如果未能解决你的问题,请参考以下文章