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

        
            com.fasterxml.jackson.core
            jackson-annotations
            2.3.0
        
        
            com.fasterxml.jackson.core
            jackson-core
            2.3.1
        
        
            com.fasterxml.jackson.core
            jackson-databind
            2.3.3
        
        
            org.springframework
            spring-messaging
            4.0.5.RELEASE
        
        
            org.springframework
            spring-websocket
            4.0.5.RELEASE
        
        
            org.springframework
            spring-webmvc
            4.0.5.RELEASE
        
        
            com.google.code.gson
            gson
            2.3.1
        
        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        

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 userSocketSessionMap;
    static {
        userSocketSessionMap = new HashMap();
    }
    /**
     * 建立连接后
     */
    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> it = userSocketSessionMap
                .entrySet().iterator();
        LOG.warn("======消息传输错误======");
        // 移除Socket会话
        while (it.hasNext()) {
            Entry 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> it = userSocketSessionMap
                .entrySet().iterator();
        // 移除Socket会话
        LOG.warn("======关闭连接======");
        while (it.hasNext()) {
            Entry 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> it = userSocketSessionMap
                .entrySet().iterator();
        LOG.warn("======群发======");
        // 多线程群发
        while (it.hasNext()) {
            final Entry 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共存。


        contextConfigLocation
        
            /WEB-INF/classes/applicationContext.xml
        
    
    
        SpringMVC
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            /WEB-INF/classes/applicationContext.xml
        
        1
    
    
        SpringMVC
        *.do
    

注意web.xml的头部:




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


    
    
        
        
            
        
    

    
    
    

注意spring的xml的头部:




5)日志接收页面:





tail log




    
此页面打开时发送了一个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,本文的所有效果和引用都有。或者发邮件到[email protected]提醒我。 本例中为方便消息的处理用到如下两个实体类(我也是沿用了别人教程代码): 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页面)