Java用webSocket实现tomcat的日志实时输出到web页面
原文: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中包的引入:
- <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>
- <dependency>
- <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>
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();
- }
- }
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);
- }
- }
- }
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>
注意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">
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" />
注意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
- ">
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>
此页面打开时发送了一个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接口");
每次返回给前端某些值的时候通过一个广播将该值传送到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;
- }
- }
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;
- }
- }
应大家需求,完成工程的代码放在了github上面啦!传上去的代码进一步完善了一下,把建立连接的时候上传参数的问题处理了一下;把存放session的容器换成了线程安全型的!欢迎大家在线讨论!
https://github.com/smile326/springWebsockeForTomcatLog
Java用webSocket实现tomcat的日志实时输出到web页面的更多相关文章
- Java后端WebSocket的Tomcat实现(转载)
一.WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通 ...
- SpringBoot系列——Logback日志,输出到文件以及实时输出到web页面
前言 SpringBoot对所有内部日志使用通用日志记录,但保留底层日志实现.为Java Util Logging.Log4J2和Logback提供了默认配置.在不同的情况下,日志记录器都预先配置为使 ...
- Java后端WebSocket的Tomcat实现
转自:http://blog.chenzuhuang.com/archive/28.html 文章摘要随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5 ...
- python+mitmproxy抓包过滤+redis消息订阅+websocket实时消息发送,日志实时输出到web界面
本实例实现需求 在游戏SDK测试中,经常需要测试游戏中SDK的埋点日志是否接入正确.本实例通过抓包(客户端http/https 请求)来判定埋点日志是是否接入正确. 实现细节:使用django项目,后 ...
- Java后端WebSocket的Tomcat实现(转)
文章摘要随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端 ...
- Java后端WebSocket的Tomcat实现 html5 WebSocket 实时聊天
WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据.Tomcat7.0.47上才能运行. 需要添加Tomcat里lib目 ...
- JAVA识别字符串是数字(英文)还是汉字,web页面进行字符截断的帮助类
public static void main(String[] args) { //长度是2说明是英文/数字/英文状态下的字符,长度为4说明是汉字/中文状态下的字符 String str = &qu ...
- WebSocket的Tomcat实现
一.WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通 ...
- java 实现websocket
最近了解了下websocket和socket这个东西,说不得不来说下为何要使用 WebSocket ,和为何不用http. 为何需要WebSocket ? HTTP 协议是一种无状态的.无连接的.单向 ...
随机推荐
- mini-treeselect 不允许选父节点的写法
html里的控件事件:onbeforenodeselect = beforenodeselect js里: beforenodeselect : function(e){ //禁止选中父节点 ...
- c# 回调委托
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Gold Coins 分类: POJ 2015-06-10 15:04 16人阅读 评论(0) 收藏
Gold Coins Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 21767 Accepted: 13641 Desc ...
- Linux内存模型
http://blog.csdn.net/sunyubo458/article/details/6090946 了解linux的内存模型,或许不能让你大幅度提高编程能力,但是作为一个基本知识点应该熟悉 ...
- [Python] 使用有道翻译API
Python 使用youdao (有道翻译)API 想写一个给自己记录背单词状况的软件,需要获取英文单词的中文释义(基本功能).考虑使用有道翻译的API实现获取英文单词的中文释义的方法. 获取API_ ...
- Android中直播视频技术探究之---采集摄像头Camera视频源数据进行推流(采用金山云SDK)
一.前言 在之前已经详细介绍了Android中的一种视频数据源:Camera,不了解的同学可以点击进入:Android中Camera使用详解 ,在这篇文章中我们介绍了如何采集摄像头的每一帧数据,然后进 ...
- Android的消息处理机制Looper,Handler,Message
android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...
- MVC4 经典增删改查详情demo
MVC4 经典增删改查详情demo 源码 不解释 Mvc4增删改查详情Demo.7z public ActionResult Detail(int? id) { ViewData.Model ...
- What Your Computer Does While You Wait
转: CPU的等待有多久? 原文标题:What Your Computer Does While You Wait 原文地址:http://duartes.org/gustavo/blog/ [注:本 ...
- 有关Duilib的博客(持续更新)
1.转载:http://blog.csdn.net/LostSpeed/article/category/1896505 2.支持多线程和动画 转载:http://blog.csdn.net/Skil ...