原文:http://blog.csdn.net/smile326/article/details/52218264

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中包的引入:

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

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

  1. package com.he.websocket;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.socket.config.annotation.EnableWebSocket;
  5. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
  6. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
  7. import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
  8. /**
  9. * WebScoket配置处理器
  10. * @author he
  11. * @Date 2016年03月15日 下午1:15:09
  12. */
  13. @Configuration
  14. @EnableWebSocket
  15. public class WebsocketConfig implements WebSocketConfigurer {
  16. public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  17. registry.addHandler(myHandler(), "/ws").addInterceptors(new HttpSessionHandshakeInterceptor());
  18. registry.addHandler(myHandler(), "/ws/sockjs").addInterceptors(new HttpSessionHandshakeInterceptor()).withSockJS();
  19. }
  20.  
  21. @Bean
  22. public WebsocketHandler myHandler() {
  23. return new WebsocketHandler();
  24. }
  25. }

handle:

  1. package com.he.websocket;
  2.  
  3. import java.io.IOException;
  4. import java.util.Date;
  5. import java.util.HashMap;
  6. import java.util.Iterator;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import org.apache.log4j.Logger;
  10. import org.springframework.stereotype.Component;
  11. import org.springframework.web.socket.CloseStatus;
  12. import org.springframework.web.socket.TextMessage;
  13. import org.springframework.web.socket.WebSocketMessage;
  14. import org.springframework.web.socket.WebSocketSession;
  15. import org.springframework.web.socket.handler.TextWebSocketHandler;
  16. import com.google.gson.Gson;
  17. import com.google.gson.GsonBuilder;
  18. import com.he.entity.Message;
  19.  
  20. @Component
  21. public class WebsocketHandler extends TextWebSocketHandler {
  22. protected static final Logger LOG = Logger.getLogger(WebsocketHandler.class);
  23.  
  24. public static final Map<Object, WebSocketSession> userSocketSessionMap;
  25. static {
  26. userSocketSessionMap = new HashMap<Object, WebSocketSession>();
  27. }
  28. /**
  29. * 建立连接后
  30. */
  31. public void afterConnectionEstablished(WebSocketSession session)
  32. throws Exception {
  33. String uid = (String) session.getAttributes().get("uid");
  34. if (userSocketSessionMap.get(uid) == null) {
  35. userSocketSessionMap.put(uid, session);
  36. }
  37. LOG.warn("======建立连接完成======");
  38. }
  39. /**
  40. * 消息处理,在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理
  41. */
  42. @Override
  43. public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
  44. if(message.getPayloadLength()==0)return;
  45. Message msg=new Gson().fromJson(message.getPayload().toString(),Message.class);
  46. String msgString = message.getPayload().toString();
  47. LOG.warn("收到的消息是:" + msgString);
  48. LOG.warn("发送的对象是:" + msg.getTo());
  49. msg.setDate(new Date());
  50. sendMessageToUser(msg.getTo(), new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
  51.  
  52. LOG.warn("======消息处理结束======");
  53. }
  54. /**
  55. * 消息传输错误处理
  56. */
  57. public void handleTransportError(WebSocketSession session,
  58. Throwable exception) throws Exception {
  59. if (session.isOpen()) {
  60. session.close();
  61. }
  62. Iterator<Entry<Object, WebSocketSession>> it = userSocketSessionMap
  63. .entrySet().iterator();
  64. LOG.warn("======消息传输错误======");
  65. // 移除Socket会话
  66. while (it.hasNext()) {
  67. Entry<Object, WebSocketSession> entry = it.next();
  68. if (entry.getValue().getId().equals(session.getId())) {
  69. userSocketSessionMap.remove(entry.getKey());
  70. System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
  71. break;
  72. }
  73. }
  74. }
  75. /**
  76. * 关闭连接后
  77. */
  78. public void afterConnectionClosed(WebSocketSession session,
  79. CloseStatus closeStatus) throws Exception {
  80. LOG.warn("Websocket:" + session.getId() + "已经关闭");
  81. Iterator<Entry<Object, WebSocketSession>> it = userSocketSessionMap
  82. .entrySet().iterator();
  83. // 移除Socket会话
  84. LOG.warn("======关闭连接======");
  85. while (it.hasNext()) {
  86. Entry<Object, WebSocketSession> entry = it.next();
  87. if (entry.getValue().getId().equals(session.getId())) {
  88. userSocketSessionMap.remove(entry.getKey());
  89. LOG.warn("Socket会话已经移除:用户ID" + entry.getKey());
  90. break;
  91. }
  92. }
  93. }
  94. public boolean supportsPartialMessages() {
  95. return false;
  96. }
  97. /**
  98. * 给所有在线用户发送消息
  99. *
  100. * @param message
  101. * @throws IOException
  102. */
  103. public void broadcast(final TextMessage message) throws IOException {
  104. Iterator<Entry<Object, WebSocketSession>> it = userSocketSessionMap
  105. .entrySet().iterator();
  106. LOG.warn("======群发======");
  107. // 多线程群发
  108. while (it.hasNext()) {
  109. final Entry<Object, WebSocketSession> entry = it.next();
  110. if (entry.getValue().isOpen()) {
  111. // entry.getValue().sendMessage(message);
  112. new Thread(new Runnable() {
  113. public void run() {
  114. try {
  115. if (entry.getValue().isOpen()) {
  116. entry.getValue().sendMessage(message);
  117. }
  118. } catch (IOException e) {
  119. e.printStackTrace();
  120. }
  121. }
  122. }).start();
  123. }
  124. }
  125. }
  126. /**
  127. * 给某个用户发送消息
  128. *
  129. * @param userName
  130. * @param message
  131. * @throws IOException
  132. */
  133. public void sendMessageToUser(String uid, TextMessage message)
  134. throws IOException {
  135. WebSocketSession session = userSocketSessionMap.get(uid);
  136. LOG.warn("======给某个用户发送消息======");
  137. if (session != null && session.isOpen()) {
  138. session.sendMessage(message);
  139. }
  140. }
  141. }

3)web.xml中配置spring和springMVC,因为借助与springMVC来拦截处理websocket请求,如果用其他MVC框架,请自行替换。不想换也可以用springMVC,侵入性很低,并且本来就可以多种MVC共存。

  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>
  4. /WEB-INF/classes/applicationContext.xml
  5. </param-value>
  6. </context-param>
  7. <servlet>
  8. <servlet-name>SpringMVC</servlet-name>
  9. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  10. <init-param>
  11. <param-name>contextConfigLocation</param-name>
  12. <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
  13. </init-param>
  14. <load-on-startup>1</load-on-startup>
  15. </servlet>
  16. <servlet-mapping>
  17. <servlet-name>SpringMVC</servlet-name>
  18. <url-pattern>*.do</url-pattern>
  19. </servlet-mapping>

注意web.xml的头部:

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

4)spring的xml文件中对websocket的配置:

  1. <!-- websocket配置 -->
  2. <bean id="websocket" class="com.he.websocket.WebsocketHandler" />
  3. <websocket:handlers>
  4. <websocket:mapping path="/ws.do" handler="websocket" />
  5. <websocket:handshake-interceptors>
  6. <bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor" />
  7. </websocket:handshake-interceptors>
  8. </websocket:handlers>
  9.  
  10. <!-- 需要扫描的包-->
  11. <context:component-scan base-package="com.he.websocket.WebsocketConfig" />
  12. <context:component-scan base-package="com.he.websocket.WebsocketHandler" />

注意spring的xml的头部:

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

5)日志接收页面:

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

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

6)控制日志的实时打印示例:

  1. PrintWriter out = response.getWriter();
  2. MyWebSocketHandler.broadcast(new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson("访问test接口")));
  3. out.println("访问test接口");

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

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

4.备注:

需要完整代码留邮箱,看到就发送,发送的工程是一个基本的web工程,只集成spring websocket,本文的所有效果和引用都有。或者发邮件到smile326@qq.com提醒我。 
本例中为方便消息的处理用到如下两个实体类(我也是沿用了别人教程代码): 
Message.Java

  1. public class Message {
  2.  
  3. //发送者
  4. public Long from;
  5. //发送者名称
  6. public String fromName;
  7. //接收者
  8. public Long to;
  9. //发送的文本
  10. public String text;
  11. //发送日期
  12. public Date date;
  13.  
  14. public Long getFrom() {
  15. return from;
  16. }
  17.  
  18. public void setFrom(Long from) {
  19. this.from = from;
  20. }
  21.  
  22. public Long getTo() {
  23. return to;
  24. }
  25.  
  26. public void setTo(Long to) {
  27. this.to = to;
  28. }
  29.  
  30. public String getText() {
  31. return text;
  32. }
  33.  
  34. public void setText(String text) {
  35. this.text = text;
  36. }
  37.  
  38. public String getFromName() {
  39. return fromName;
  40. }
  41.  
  42. public void setFromName(String fromName) {
  43. this.fromName = fromName;
  44. }
  45.  
  46. public Date getDate() {
  47. return date;
  48. }
  49.  
  50. public void setDate(Date date) {
  51. this.date = date;
  52. }
  53.  
  54. }

user.java:

  1. public class User {
  2.  
  3. private Long id;
  4.  
  5. private String name;
  6.  
  7. private String password;
  8.  
  9. public Long getId() {
  10. return id;
  11. }
  12.  
  13. public void setId(Long id) {
  14. this.id = id;
  15. }
  16.  
  17. public String getName() {
  18. return name;
  19. }
  20.  
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24.  
  25. public String getPassword() {
  26. return password;
  27. }
  28.  
  29. public void setPassword(String password) {
  30. this.password = password;
  31. }
  32.  
  33. }

应大家需求,完成工程的代码放在了github上面啦!传上去的代码进一步完善了一下,把建立连接的时候上传参数的问题处理了一下;把存放session的容器换成了线程安全型的!欢迎大家在线讨论! 
https://github.com/smile326/springWebsockeForTomcatLog

Java用webSocket实现tomcat的日志实时输出到web页面的更多相关文章

  1. Java后端WebSocket的Tomcat实现(转载)

    一.WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通 ...

  2. SpringBoot系列——Logback日志,输出到文件以及实时输出到web页面

    前言 SpringBoot对所有内部日志使用通用日志记录,但保留底层日志实现.为Java Util Logging.Log4J2和Logback提供了默认配置.在不同的情况下,日志记录器都预先配置为使 ...

  3. Java后端WebSocket的Tomcat实现

    转自:http://blog.chenzuhuang.com/archive/28.html 文章摘要随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5 ...

  4. python+mitmproxy抓包过滤+redis消息订阅+websocket实时消息发送,日志实时输出到web界面

    本实例实现需求 在游戏SDK测试中,经常需要测试游戏中SDK的埋点日志是否接入正确.本实例通过抓包(客户端http/https 请求)来判定埋点日志是是否接入正确. 实现细节:使用django项目,后 ...

  5. Java后端WebSocket的Tomcat实现(转)

    文章摘要随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端 ...

  6. Java后端WebSocket的Tomcat实现 html5 WebSocket 实时聊天

    WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据.Tomcat7.0.47上才能运行. 需要添加Tomcat里lib目 ...

  7. JAVA识别字符串是数字(英文)还是汉字,web页面进行字符截断的帮助类

    public static void main(String[] args) { //长度是2说明是英文/数字/英文状态下的字符,长度为4说明是汉字/中文状态下的字符 String str = &qu ...

  8. WebSocket的Tomcat实现

    一.WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通 ...

  9. java 实现websocket

    最近了解了下websocket和socket这个东西,说不得不来说下为何要使用 WebSocket ,和为何不用http. 为何需要WebSocket ? HTTP 协议是一种无状态的.无连接的.单向 ...

随机推荐

  1. mini-treeselect 不允许选父节点的写法

    html里的控件事件:onbeforenodeselect = beforenodeselect   js里: beforenodeselect : function(e){   //禁止选中父节点 ...

  2. c# 回调委托

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  3. Gold Coins 分类: POJ 2015-06-10 15:04 16人阅读 评论(0) 收藏

    Gold Coins Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 21767   Accepted: 13641 Desc ...

  4. Linux内存模型

    http://blog.csdn.net/sunyubo458/article/details/6090946 了解linux的内存模型,或许不能让你大幅度提高编程能力,但是作为一个基本知识点应该熟悉 ...

  5. [Python] 使用有道翻译API

    Python 使用youdao (有道翻译)API 想写一个给自己记录背单词状况的软件,需要获取英文单词的中文释义(基本功能).考虑使用有道翻译的API实现获取英文单词的中文释义的方法. 获取API_ ...

  6. Android中直播视频技术探究之---采集摄像头Camera视频源数据进行推流(采用金山云SDK)

    一.前言 在之前已经详细介绍了Android中的一种视频数据源:Camera,不了解的同学可以点击进入:Android中Camera使用详解 ,在这篇文章中我们介绍了如何采集摄像头的每一帧数据,然后进 ...

  7. Android的消息处理机制Looper,Handler,Message

    android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...

  8. MVC4 经典增删改查详情demo

    MVC4 经典增删改查详情demo 源码 不解释 Mvc4增删改查详情Demo.7z public ActionResult Detail(int? id)  {    ViewData.Model ...

  9. What Your Computer Does While You Wait

    转: CPU的等待有多久? 原文标题:What Your Computer Does While You Wait 原文地址:http://duartes.org/gustavo/blog/ [注:本 ...

  10. 有关Duilib的博客(持续更新)

    1.转载:http://blog.csdn.net/LostSpeed/article/category/1896505 2.支持多线程和动画 转载:http://blog.csdn.net/Skil ...