浏览器动态显示服务器日志,基于 websocket 实现远程实时日志 在浏览器中查看设备的运行日志...

本文介绍一个基于websocket实现的远程实时日志系统,可以通过浏览器查看远程移动设备的实时运行日志。

系统由三个部分组成:

1. 服务器:与移动设备和浏览器建立websocket连接,将移动设备websocket上读取的实时日志转发到对应的浏览器的websocket中

2. 浏览器日志查看页面:与服务器建立websocket连接,通过websocket接收指定设备的实时运行日志并显示

3. 移动设备:与服务器建立websocket连接,将运行日志通过websocket连接上传至服务器

服务器端实现

Tomcat 7.0.27 开始支持Websocket了。本文的服务器端Servlet程序是搭建在Tomcat上的。关于在Tomcat上面实现支持websocket的servlet,可以参考

服务器Servlet源码:

importjava.io.IOException;

importjava.nio.ByteBuffer;

importjava.nio.CharBuffer;

importjava.util.Map;

importjava.util.Set;

importjava.util.concurrent.ConcurrentHashMap;

importjava.util.concurrent.CopyOnWriteArraySet;

importjavax.servlet.annotation.WebServlet;

importjavax.servlet.http.HttpServletRequest;

importorg.apache.catalina.websocket.MessageInbound;

importorg.apache.catalina.websocket.StreamInbound;

importorg.apache.catalina.websocket.WebSocketServlet;

importorg.apache.catalina.websocket.WsOutbound;

/**

* Servlet implementation class WebLogcat

*/

@WebServlet("/WebLogcat")

publicclassWebLogcatextendsWebSocketServlet {

privatestaticfinallongserialVersionUID = 1L;

privatefinalSet connections =

newCopyOnWriteArraySet();

privatefinalMap devices =

newConcurrentHashMap();

privatefinalMap> browsers =

newConcurrentHashMap>();

@Override

protectedStreamInbound createWebSocketInbound(String arg0,

HttpServletRequest arg1) {

String id = arg1.getParameter("id");

String type = arg1.getParameter("type");

if( id !=null&& type !=null) {

if( type.equalsIgnoreCase("device") ) {

returnnewDeviceMessageInbound( id );

} elseif( type.equalsIgnoreCase("browser") ) {

returnnewBrowserMessageInbound( id );

}

}

// return NULL will lead to Exception

returnnewLogMessageInbound();

}

privatefinalclassDeviceMessageInboundextendsMessageInbound {

privateString _id;

DeviceMessageInbound(String id) {

_id = id;

}

@Override

protectedvoidonClose(intstatus) {

// remove me from device hash map

devices.remove(_id);

super.onClose(status);

}

@Override

protectedvoidonOpen(WsOutbound outbound) {

// add me to device hash map

devices.put(_id, this);

super.onOpen(outbound);

}

@Override

protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {

}

@Override

protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {

// broadcast to all browser with the same id as me

String message = newString(arg0.array());

Set list = browsers.get( _id );

if( list !=null) {

for(BrowserMessageInbound connection : list) {

try{

CharBuffer buffer = CharBuffer.wrap(message);

connection.getWsOutbound().writeTextMessage(buffer);

} catch(IOException ignore) {

// Ignore

}

}

}

}

}

privatefinalclassBrowserMessageInboundextendsMessageInbound {

privateString _id;

@Override

protectedvoidonClose(intstatus) {

synchronized( browsers ) {

Set list = browsers.get( _id );

if( list !=null) {

list.remove(this);

if( list.isEmpty() ) {

browsers.remove(_id);

}

}

}

super.onClose(status);

}

@Override

protectedvoidonOpen(WsOutbound outbound) {

synchronized( browsers ) {

if( browsers.containsKey(_id) ) {

browsers.get(_id).add(this);

} else{

Set list = newCopyOnWriteArraySet();

list.add(this);

browsers.put(_id, list);

}

}

super.onOpen(outbound);

}

BrowserMessageInbound(String id) {

_id = id;

}

@Override

protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {

}

@Override

protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {

}

}

privatefinalclassLogMessageInboundextendsMessageInbound {

@Override

protectedvoidonClose(intstatus) {

connections.remove(this);

super.onClose(status);

}

@Override

protectedvoidonOpen(WsOutbound outbound) {

super.onOpen(outbound);

connections.add(this);

}

@Override

protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {

}

@Override

protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {

}

}

}

要实现支持websocket的servlet,需要继承WebSocketServlet, 实现createWebSocketInbound函数,返回代表websocket连接的对象。

本文中,程序接收请求中传递的id和type参数,iid代表了设备的id,或者浏览器想要查看实时日志的设备的id。type代表了连接请求是来自设备还是浏览器。Servlet根据type的值创建对应类型的websocket连接。如果连接请求是来自设备device,则创建DeviceMessageInbound。如果设备请求是来自浏览器browser,则创建BrowserMessageInbound。

DeviceMessageInbound 和 BrowserMessageInbound 都继承自MessageInbound

对于DeviceMessageInbound,

在onOpen()中,将自己加入到设备Map表,

在onClose()中,将自己从设备Map表中移除。

onTextMessage()就是Servlet收到了来自设备的文本消息。Servlet根据设备id,找到所有正在监听此设备实时日志的browser,把文本消息转发给browser。

对于BrowserMessageInbound,

在onOpen()中,要将自己加入到监听对于id的browser列表中。由于针对同一个设备id,允许多个浏览器同时查看其实时日志。所以要先判断是否已经存在对应id的浏览器连接。如果不存在,则创建一个列表,并将此列表插入到browsers这个Map中。如果已经存在,则根据id找到列表,将自己加入到此列表中。

在onClose中,通过id找到列表,将自己从列表中移除。移除后如果列表为空,在将列表从browsers Map中移除。

浏览器端实现

html>

WebLogcat

input#chat {

width: 410px

}

#console-container {

width: 400px;

}

#console {

border: 1px solid #CCCCCC;

border-right-color: #999999;

border-bottom-color: #999999;

height: 170px;

overflow-y: scroll;

padding: 5px;

width: 100%;

}

#console p {

padding: 0;

margin: 0;

}

var Chat= {};

Chat.socket=null;

Chat.connect= (function(host) {

if ('WebSocket' in window) {

Chat.socket=newWebSocket(host);

} else if ('MozWebSocket' in window) {

Chat.socket=newMozWebSocket(host);

} else {

Console.log('Error: WebSocket is not supported by this browser.');

return;

}

Chat.socket.onopen=function() {

Console.log('Info: WebSocket connection opened.');

document.getElementById('chat').οnkeydοwn=function(event) {

if (event.keyCode== 13) {

Chat.sendMessage();

}

};

};

Chat.socket.onclose=function() {

document.getElementById('chat').οnkeydοwn=null;

Console.log('Info: WebSocket closed.');

};

Chat.socket.onmessage=function(message) {

Console.log(message.data);

};

});

Chat.initialize=function() {

if (window.location.protocol== 'http:') {

Chat.connect('ws://' + window.location.host + '/WebLogcat/WebLogcat?id=fv0557&type=browser');

} else {

Chat.connect('wss://' + window.location.host + '/WebLogcat/WebLogcat?id=fv0557&type=browser');

}

};

Chat.sendMessage= (function() {

var message=document.getElementById('chat').value;

if (message != '') {

Chat.socket.send(message);

document.getElementById('chat').value='';

}

});

var Console= {};

Console.log= (function(message) {

var console=document.getElementById('console');

var p=document.createElement('p');

p.style.wordWrap='break-word';

p.innerHTML=message;

console.appendChild(p);

while (console.childNodes.length >25) {

console.removeChild(console.firstChild);

}

console.scrollTop=console.scrollHeight;

});

Chat.initialize();

Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable

Javascript and reload this page!

你可能感兴趣的:(浏览器动态显示服务器日志)