您的位置:首页 > 运维架构 > Tomcat

java用webSocket实现tomcat的日志实时输出到web页面______软件开发-JAVA

2016-10-10 18:16 836 查看


java用webSocket实现tomcat的日志实时输出到web页面


1.场景需求

后台攻城狮和前端攻城狮一起开发时,经常受到前端攻城狮的骚扰,动不动就来一句,那谁,帮我看一下接口访问出什么错了。。。我刚刚上传的参数过来了吗。。。你返回的是什么。。。我请求过去了吗。。。 

好吧,就是这样的一种情况,然后我希望让他们自己去看后台日志,而又不想给他们登陆服务器的权限TAT。那就想办法把访问日志实时输出到web页面,这样他们打开页面就可以了。


2.特别鸣谢

1)特别感谢http://blog.csdn.net/xiao__gui/article/details/50041673的启发,该文章中利用的是linux中的tail日志,本文即是受此启发,基本思路一模一样,感谢感谢。 

2)感谢国产博客中各种websocket教程,获益颇多,本文其实也是一个websocket教程,只是稍作拓展。


3.进入正题


配置websocket,大部分web项目都会用到spring框架吧,所有这里贴spring websocket的配置,不用spring的请自行去掉spring部分。spring使用4.0以上,tomcat使用7.0.68版本。


1)pom.xml中包的引入:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.3.1</version>
</dependency>
<dependenc
1b94c
y>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

2)websocket逻辑代码,需要一个config和一个handle 

configuration:
package com.he.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
/**
* WebScoket配置处理器
* @author he
* @Date 2016年03月15日 下午1:15:09
*/
@Configuration
@EnableWebSocket
public class WebsocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/ws").addInterceptors(new HttpSessionHandshakeInterceptor());
registry.addHandler(myHandler(), "/ws/sockjs").addInterceptors(new HttpSessionHandshakeInterceptor()).withSockJS();
}

@Bean
public WebsocketHandler myHandler() {
return new WebsocketHandler();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

handle:
package com.he.websocket;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.he.entity.Message;

@Component
public class WebsocketHandler extends TextWebSocketHandler {
protected static final Logger LOG = Logger.getLogger(WebsocketHandler.class);

public static final Map<Object, WebSocketSession> userSocketSessionMap;
static {
userSocketSessionMap = new HashMap<Object, WebSocketSession>();
}
/**
* 建立连接后
*/
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
String uid = (String) session.getAttributes().get("uid");
if (userSocketSessionMap.get(uid) == null) {
userSocketSessionMap.put(uid, session);
}
LOG.warn("======建立连接完成======");
}
/**
* 消息处理,在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理
*/
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
if(message.getPayloadLength()==0)return;
Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);
String msgString = message.getPayload().toString();
LOG.warn("收到的消息是:" + msgString);
LOG.warn("发送的对象是:" + msg.getTo());
msg.setDate(new Date());
sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));

LOG.warn("======消息处理结束======");
}
/**
* 消息传输错误处理
*/
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
}
Iterator<Entry<Object, WebSocketSession>> it = userSocketSessionMap
.entrySet().iterator();
LOG.warn("======消息传输错误======");
// 移除Socket会话
while (it.hasNext()) {
Entry<Object, WebSocketSession> entry = it.next();
if (entry.getValue().getId().equals(session.getId())) {
userSocketSessionMap.remove(entry.getKey());
System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
break;
}
}
}
/**
* 关闭连接后
*/
public void afterConnectionClosed(WebSocketSession session,
CloseStatus closeStatus) throws Exception {
LOG.warn("Websocket:" + session.getId() + "已经关闭");
Iterator<Entry<Object, WebSocketSession>> it = userSocketSessionMap
.entrySet().iterator();
// 移除Socket会话
LOG.warn("======关闭连接======");
while (it.hasNext()) {
Entry<Object, WebSocketSession> entry = it.next();
if (entry.getValue().getId().equals(session.getId())) {
userSocketSessionMap.remove(entry.getKey());
LOG.warn("Socket会话已经移除:用户ID" + entry.getKey());
break;
}
}
}
public boolean supportsPartialMessages() {
return false;
}
/**
* 给所有在线用户发送消息
*
* @param message
* @throws IOException
*/
public void broadcast(final TextMessage message) throws IOException {
Iterator<Entry<Object, WebSocketSession>> it = userSocketSessionMap
.entrySet().iterator();
LOG.warn("======群发======");
// 多线程群发
while (it.hasNext()) {
final Entry<Object, WebSocketSession> entry = it.next();
if (entry.getValue().isOpen()) {
// entry.getValue().sendMessage(message);
new Thread(new Runnable() {
public void run() {
try {
if (entry.getValue().isOpen()) {
entry.getValue().sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
/**
* 给某个用户发送消息
*
* @param userName
* @param message
* @throws IOException
*/
public void sendMessageToUser(String uid, TextMessage message)
throws IOException {
WebSocketSession session = userSocketSessionMap.get(uid);
LOG.warn("======给某个用户发送消息======");
if (session != null && session.isOpen()) {
session.sendMessage(message);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

3)web.xml中配置spring和springMVC,因为借助与springMVC来拦截处理websocket请求,如果用其他MVC框架,请自行替换。不想换也可以用springMVC,侵入性很低,并且本来就可以多种MVC共存。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext.xml
</param-value>
</context-param>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

注意web.xml的头部:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">[/code]1 2
3
4
5

4)spring的xml文件中对websocket的配置:
<!-- websocket配置 -->
<bean id="websocket" class="com.he.websocket.WebsocketHandler" />
<websocket:handlers>
<websocket:mapping path="/ws.do" handler="websocket" />
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor" />
</websocket:handshake-interceptors>
</websocket:handlers>

<!-- 需要扫描的包-->
<context:component-scan base-package="com.he.websocket.WebsocketConfig" />
<context:component-scan base-package="com.he.websocket.WebsocketHandler" />
1
2
3
4
5
6
7
8
9
10
11
12

注意spring的xml的头部:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd ">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

5)日志接收页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>tail log</title>
<script src="//cdn.bootcss.com/jquery/2.1.4/jquery.js"></script>
<style>
html,body
{
height:100%;
width:100%;
}
</style>
</head>
<body>
<div id="log-container" style="height: 100%; overflow-y: scroll; background: #333; color: #aaa; padding: 10px;">
<div>
</div>
</div>
</body>
<script>
$(document).ready(function() {
// 指定websocket路径
var websocket = new WebSocket('ws://localhost:8080/websocket/ws.do');
websocket.onmessage = function(event) {
// 接收服务端的实时日志并添加到HTML页面中
$("#log-container div").append(event.data + "<p> </p>");
// 滚动条滚动到最低部
$("#log-container").scrollTop($("#log-container div").height() - $("#log-container").height());
};
});
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

此页面打开时发送了一个websocket请求,ws.do,会被springMVC拦截处理。连接建立之后每当收到新的消息会追加到当前文本的末尾并换行(p标签用来换行)。

6)控制日志的实时打印示例:
PrintWriter out = response.getWriter();
MyWebSocketHandler.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson("访问test接口")));
out.println("访问test接口");
1
2
3

每次返回给前端某些值的时候通过一个广播将该值传送到web页面。如果不想广播,只需要在程序中对log的session进行特别的标签,然后定向传送即可。

效果示意图:浏览器输入localhost:8080/projectName/log.html,然后访问localhost:8080/projectName/test,每次访问test接口,网页上实时显示tomcat输出的日志。 




4.备注:

需要完整代码留邮箱,看到就发送,发送的工程是一个基本的web工程,只集成spring websocket,本文的所有效果和引用都有。或者发邮件到smile326@qq.com提醒我。 

本例中为方便消息的处理用到如下两个实体类(我也是沿用了别人教程代码): 

Message.java:
public class Message {

//发送者
public Long from;
//发送者名称
public String fromName;
//接收者
public Long to;
//发送的文本
public String text;
//发送日期
public Date date;

public Long getFrom() {
return from;
}

public void setFrom(Long from) {
this.from = from;
}

public Long getTo() {
return to;
}

public void setTo(Long to) {
this.to = to;
}

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}

public String getFromName() {
return fromName;
}

public void setFromName(String fromName) {
this.fromName = fromName;
}

public Date getDate() {
return date;
}

public void setDate(Date date) {
this.date = date;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

user.java:
public class User {

private Long id;

private String name;

private String password;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

应大家需求,完成工程的代码放在了github上面啦!传上去的代码进一步完善了一下,把建立连接的时候上传参数的问题处理了一下;把存放session的容器换成了线程安全型的!欢迎大家在线讨论! 
https://github.com/smile326/springWebsockeForTomcatLog
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: