精华内容
下载资源
问答
  • 一、环境介绍二、RabbitMQ代码三、websocket代码wsclient.gowshub.go四、GIN路由五、HTML代码六、测试代码 一、环境介绍 ...AI算法训练过程中的日志实时在Web UI界面显示     &.

    一、环境介绍

    Go版本:1.13.1
    开发工具:IntelliJ IDEA 2019.2.3 x64
    开发环境:windows10 64位
    部署环境:centos7

    业务场景:
           AI算法训练过程中的日志,实时在Web UI界面显示
           每次训练的日志可以在多个Web UI展示,同一个训练的同一条消息可以被多个消费端消费
           不同训练的消息不能混合显示,使用RabbitMQ的路由匹配模式
    RountingKey=rtk.train.id,queue=que.logque.id,每个训练根据id匹配自己的日志。Id=训练id
    Exchange持久化, queue不持久化
           消息流转过程、RabbitMQ队列设计如下
    在这里插入图片描述
    代码已上传github:https://github.com/reachyu/realtimelog

    二、RabbitMQ代码

    路由模式生产者和消费者代码如下

    package msgmq
    
    import (
    	"aisvc/common/vo"
    	"encoding/json"
    	"fmt"
    	"github.com/streadway/amqp"
    	"log"
    	"strconv"
    	"unsafe"
    )
    
    // rabbitmq 路由routing模式
    
    // 发送消息(生产者)
    func PublishMsgRout(exName string,rtKey string, msg string) {
    
    	ch, err := MQInstance().GetMQChannel()
    	failOnError(err, "Failed to open a channel")
    	defer ch.Close()
    
    	err = ch.ExchangeDeclare(
    		exName, // name
    		"direct",      // type
    		true,         // durable
    		false,        // auto-deleted
    		false,        // internal
    		false,        // no-wait
    		nil,          // arguments
    	)
    	failOnError(err, "Failed to declare an exchange")
    	err = ch.Publish(
    		exName,          // exchange
    		rtKey, // routing key
    		//如果为true,根据exchange类型和routekey类型,如果无法找到符合条件的队列,name会把发送的信息返回给发送者
    		true, // mandatory
    		false, // immediate
    		amqp.Publishing{
    			// 消息持久化
    			DeliveryMode: amqp.Persistent,
    			ContentType: "text/plain",
    			Body:        []byte(msg),
    		})
    	failOnError(err, "Failed to publish a message")
    }
    
    
    type Callback func(trainId string,msg string)
    
    // 消费消息(消费者)
    func ConsumeMsgRout(exName string,queName string,rtKey string,callback Callback) {
    	ch, err := MQInstance().GetMQChannel()
    	failOnError(err, "Failed to receive a message")
    	defer ch.Close()
    
    	err = ch.ExchangeDeclare(
    		exName,   // name
    		"direct", // type
    		true,     // durable
    		false,    // auto-deleted
    		false,    // internal
    		false,    // no-wait
    		nil,      // arguments
    	)
    	failOnError(err, "Failed to receive a message")
    
    	_, err = ch.QueueDeclare(
    		queName,    // name
    		false, // durable
    		false, // delete when usused
    		false,  // exclusive
    		false, // no-wait
    		nil,   // arguments
    	)
    	failOnError(err, "Failed to receive a message")
    
    	err = ch.QueueBind(
    		queName, // queue name
    		rtKey,     // routing key
    		exName, // exchange
    		false,
    		nil,
    	)
    	failOnError(err, "Failed to receive a message")
    
    	msgs, err := ch.Consume(
    		queName, // queue
    		"",     // consumer
    		true,   // auto-ack
    		false,  // exclusive
    		false,  // no-local
    		false,  // no-wait
    		nil,    // args
    	)
    
    	forever := make(chan bool)
    	go func() {
    		for d := range msgs {
    			var msgTrainLog vo.TrainLog
    			_ = json.Unmarshal(d.Body, &msgTrainLog)
    			trainId := msgTrainLog.TrainId
    			id64 := strconv.FormatInt(trainId,10)
    
    			// 避免循环引用  callback = ws.SendLogsToWeb(trainId string,msg string)
    			strAddress := &callback
    			strPointer := fmt.Sprintf("%d", unsafe.Pointer(strAddress))
    			intPointer, _ := strconv.ParseInt(strPointer, 10, 0)
    			var pointer *Callback
    			pointer = *(**Callback)(unsafe.Pointer(&intPointer))
    			(Callback)(*pointer)(id64,msgTrainLog.TrainLog)
    
    			log.Printf("路由队列接收到消息======== [x] %s", d.Body)
    		}
    	}()
    	<-forever
    }
    
    func failOnError(err error, msg string) {
    	if err != nil {
    		log.Fatalf("%s: %s", msg, err)
    	}
    }
    

    三、websocket代码

    websocket代码参考了
    https://github.com/516134941/websocket-gin-demo/tree/master/message-chat

    wsclient.go

    package ws
    
    import (
    	_const "aisvc/common/const"
    	mq "aisvc/msgmq"
    	"bytes"
    	"fmt"
    	"log"
    	"net/http"
    	"sync"
    	"time"
    
    	"github.com/gin-gonic/gin"
    
    	"github.com/gorilla/websocket"
    )
    
    const (
    	// Time allowed to write a message to the peer.
    	writeWait = 10 * time.Second
    	// Time allowed to read the next pong message from the peer.
    	pongWait = 60 * time.Second
    	// Send pings to peer with this period. Must be less than pongWait.
    	pingPeriod = (pongWait * 9) / 10
    	// Maximum message size allowed from peer.
    	maxMessageSize = 512
    )
    
    var clientMaps map[string]*WSClient
    var once sync.Once
    
    func GetClientMaps() map[string]*WSClient {
    	once.Do(func() {
    		clientMaps = make(map[string]*WSClient)
    	})
    	return clientMaps
    }
    
    var (
    	newline = []byte{'\n'}
    	space   = []byte{' '}
    )
    
    var upgrader = websocket.Upgrader{
    	ReadBufferSize:  1024,
    	WriteBufferSize: 1024,
    }
    
    // Client is a middleman between the websocket connection and the hub.
    type WSClient struct {
    	hub *WSHub
    	// The websocket connection.
    	conn *websocket.Conn
    	// Buffered channel of outbound messages.
    	send chan []byte
    	trainId []byte
    }
    
    func SendLogsToWeb(trainId string,msg string) {
    	clientMaps := GetClientMaps()
    	c := clientMaps[trainId]
    
    	if c == nil{
    		return
    	}
    	c.conn.SetReadLimit(maxMessageSize)
    	c.conn.SetReadDeadline(time.Now().Add(pongWait))
    	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
    
    	message := bytes.TrimSpace(bytes.Replace([]byte(msg), newline, space, -1))
    	message = []byte(trainId + "&" + msg)
    	fmt.Println("websocket读取到的消息====="+string(message))
    	c.hub.broadcast <- []byte(message)
    
    }
    
    func (c *WSClient) readPump() {
    	defer func() {
    		c.hub.unregister <- c
    		c.conn.Close()
    	}()
    	c.conn.SetReadLimit(maxMessageSize)
    	c.conn.SetReadDeadline(time.Now().Add(pongWait))
    	c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
    	for {
    		_, message, err := c.conn.ReadMessage()
    		if err != nil {
    			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
    				log.Printf("error: %v", err)
    			}
    			break
    		}
    		message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
    		message = []byte(string(c.trainId) + "&" + string(message))
    		fmt.Println("websocket读取到的消息====="+string(message))
    		c.hub.broadcast <- []byte(message)
    	}
    }
    
    func (c *WSClient) writePump() {
    	ticker := time.NewTicker(pingPeriod)
    	defer func() {
    		ticker.Stop()
    		c.conn.Close()
    	}()
    	for {
    		select {
    		case message, ok := <-c.send:
    			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
    			if !ok {
    				// The hub closed the channel.
    				c.conn.WriteMessage(websocket.CloseMessage, []byte{})
    				return
    			}
    
    			w, err := c.conn.NextWriter(websocket.TextMessage)
    			if err != nil {
    				return
    			}
    			w.Write(message)
    			if err := w.Close(); err != nil {
    				log.Printf("error: %v", err)
    				return
    			}
    		case <-ticker.C:
    			c.conn.SetWriteDeadline(time.Now().Add(writeWait))
    			if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
    				return
    			}
    		}
    	}
    }
    
    // ServeWs handles websocket requests from the peer.
    func ServeWs(hub *WSHub, c *gin.Context) {
    	GetClientMaps()
    	trainId := c.Param("trainId")
    	// 将网络请求变为websocket
    	var upgrader = websocket.Upgrader{
    		// 解决跨域问题
    		CheckOrigin: func(r *http.Request) bool {
    			return true
    		},
    	}
    	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    	if err != nil {
    		log.Println(err)
    		return
    	}
    	fmt.Println("websocket接收到前端的trainId======"+trainId)
    	client := &WSClient{hub: hub, conn: conn, send: make(chan []byte, 256), trainId: []byte(trainId)}
    	client.hub.register <- client
    
    	clientMaps[trainId] = client
    
    	// 监听日志消息
    	go mq.ConsumeMsgRout(_const.RABBITMQ_ROUT_EXCHANGE_NAME,_const.RABBITMQ_ROUT_QUEUE + trainId,_const.RABBITMQ_ROUT_ROUTING_KEY + trainId,SendLogsToWeb)
    
    	// Allow collection of memory referenced by the caller by doing all work in
    	// new goroutines.
    	go client.writePump()
    	//go client.readPump()
    }
    

    wshub.go

    package ws
    
    import "strings"
    
    // Hub maintains the set of active clients and broadcasts messages to the
    // clients.
    type WSHub struct {
    	// Registered clients.
    	clients map[*WSClient]bool
    	// Inbound messages from the clients.
    	broadcast chan []byte
    	// Register requests from the clients.
    	register chan *WSClient
    	// Unregister requests from clients.
    	unregister chan *WSClient
    	// 唯一id key:client value:唯一id
    	trainId map[*WSClient]string
    }
    
    // NewHub .
    func NewHub() *WSHub {
    	return &WSHub{
    		broadcast:  make(chan []byte),
    		register:   make(chan *WSClient),
    		unregister: make(chan *WSClient),
    		clients:    make(map[*WSClient]bool),
    		trainId:    make(map[*WSClient]string),
    	}
    }
    
    // Run .
    func (h *WSHub) Run() {
    	for {
    		select {
    		case client := <-h.register:
    			h.clients[client] = true                 // 注册client端
    			h.trainId[client] = string(client.trainId) // 给client端添加唯一id
    		case client := <-h.unregister:
    			if _, ok := h.clients[client]; ok {
    				delete(h.clients, client)
    				delete(h.trainId, client)
    				close(client.send)
    			}
    		case message := <-h.broadcast:
    			for client := range h.clients {
    				// 使用“&”对message进行message切割 获取唯一id
    				// 向信息所属的训练内的所有client 内添加send
    				// msg[0]为唯一id msg[1]为打印内容
    				msg := strings.Split(string(message), "&")
    				if string(client.trainId) == msg[0] {
    					select {
    					case client.send <- []byte(msg[1]):
    					default:
    						close(client.send)
    						delete(h.clients, client)
    						delete(h.trainId, client)
    					}
    				}
    			}
    		}
    	}
    }
    

    四、GIN路由

    // 设置跨域
    	router.Use(cors.New(cors.Config{
    		AllowAllOrigins:  true,  // 这是允许访问所有域
    		AllowMethods:     []string{"GET", "PUT", "POST", "DELETE", "OPTIONS"},   //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
    		AllowHeaders:     []string{"x-xq5-jwt", "Content-Type", "Origin", "Content-Length"},  // 允许跨域设置
    		ExposeHeaders:    []string{"x-xq5-jwt"},  // 跨域关键设置 让浏览器可以解析
    		AllowCredentials: true,  //  跨域请求是否需要带cookie信息 默认设置为true
    		MaxAge:           12 * time.Hour,
    	}))
    
    hub := ws.NewHub()
    	go hub.Run()
    	router.GET("/ws/logs/:trainId", func(c *gin.Context) { ws.ServeWs(hub, c) })
    

    五、HTML代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <title>logs</title>
    <script type="text/javascript">
    window.onload = function () {
        var conn;
        var msg = document.getElementById("msg");
        var log = document.getElementById("log");
        function appendLog(item) {
            var doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1;
            log.appendChild(item);
            if (doScroll) {
                log.scrollTop = log.scrollHeight - log.clientHeight;
            }
        }
        document.getElementById("form").onsubmit = function () {
            if (!conn) {
                return false;
            }
            if (!msg.value) {
                return false;
            }
            conn.send(msg.value);
            msg.value = "";
            return false;
        };
        if (window["WebSocket"]) {
            // 123456为trainId
            conn = new WebSocket("ws://" + "localhost:9090" + "/ws/logs/123456");
            conn.onclose = function (evt) {
                var item = document.createElement("div");
                item.innerHTML = "<b>Connection closed.</b>";
                appendLog(item);
            };
            conn.onmessage = function (evt) {
                var messages = evt.data.split('\n');
                for (var i = 0; i < messages.length; i++) {
                    var item = document.createElement("div");
                    item.innerText = messages[i];
                    appendLog(item);
                }
            };
        } else {
            var item = document.createElement("div");
            item.innerHTML = "<b>Your browser does not support WebSockets.</b>";
            appendLog(item);
        }
    };
    </script>
    <style type="text/css">
    html {
        overflow: hidden;
    }
    body {
        overflow: hidden;
        padding: 0;
        margin: 0;
        width: 100%;
        height: 100%;
        background: gray;
    }
    #log {
        background: white;
        margin: 0;
        padding: 0.5em 0.5em 0.5em 0.5em;
        position: absolute;
        top: 0.5em;
        left: 0.5em;
        right: 0.5em;
        bottom: 3em;
        overflow: auto;
    }
    #form {
        padding: 0 0.5em 0 0.5em;
        margin: 0;
        position: absolute;
        bottom: 1em;
        left: 0px;
        width: 100%;
        overflow: hidden;
    }
    </style>
    </head>
    <body>
    <div id="log"></div>
    <form id="form">
        <input type="submit" value="Send" />
        <input type="text" id="msg" size="64"/>
    </form>
    </body>
    </html>
    

    六、测试代码

    func main() {
    	// 不调用的话,glog会报错----ERROR: logging before flag.Parse:
    	flag.Parse()
    	initenv.InitEnv()
    
    	go httpserver.InitHttpServer()
    
    msg := map[string]interface{}{
    			"trainId":    123456,
    			"trainLog":   "测试测试测试测试",
    		}
    msgJson, _ := json.Marshal(msg)
    
    	forever := make(chan bool)
    	go func() {
    		for {
    
    	mq.PublishMsgRout(_const.RABBITMQ_ROUT_EXCHANGE_NAME,_const.RABBITMQ_ROUT_ROUTING_KEY+"123456",string(msgJson))
    		time.Sleep(1 * time.Second)
    		}
    	}()
    	<-forever
    
    }
    

    实时日志效果
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • WebSocket页面显示日志的代码

    千次阅读 2019-06-21 12:54:49
    WebSocket页面显示日志的代码在前端html页面显示后台的服务器日志信息页面效果前端页面代码后台代码WebSocketInterceptor拦截器WebSocketHandler握手动作WebSocketConfig配置类TailLogThread实时读取日志的线程...

    在前端html页面显示后台的服务器日志信息

    主要是为了方便,前端也不会ssh去连接服务器tail -f日志。为了方便前端和测试调试,将日志 染色 并显示在页面上。

    页面效果

    在这里插入图片描述

    前端页面代码

    前面引用的easyui库这些是为了页面布局和原系统支持。其实对读者没啥用,不需要的去掉就行。

    <!--showServiceLog.html-->
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>WebSocket查看服务日志页面</title>
        <!-- easyUI样式 -->
        <link rel="stylesheet" type="text/css" href="../../Font-Awesome-master/css/font-awesome.min.css" />
        <link rel="stylesheet" type="text/css" href="../../css/common/main.css" />
        <link rel="stylesheet" type="text/css" href="../../css/common/my.css" />
        <link rel="stylesheet" type="text/css" href="../../css/themes/default/easyui.css" />
        <link rel="stylesheet" type="text/css" href="../../css/themes/icon.css" />
        <script type="text/javascript" src="../../js/jquery.min.js"></script>
        <script type="text/javascript" src="../../js/jquery.easyui.min.js"></script>
        <script type="text/javascript" src="../../js/easyui-lang-zh_CN.js"></script>
        <script type="text/javascript" src="../../js/common.js"></script>
        <script type="text/javascript" src="../../js/common/Cookie.js"></script>
        <script type="text/javascript" src="../../js/sockjs.js"></script>
        <style type="text/css">
            .datagrid-cell, .datagrid-cell-group, .datagrid-header-rownumber, .datagrid-cell-rownumber
            {
                text-overflow: ellipsis;
            }
        </style>
    
    </head>
    <body>
    <!-- 标题和操作菜单 -->
    <table class="title" style="width:100%;padding:5px;">
        <tbody>
        <tr>
            <td style="width:25%;color: #407EAC">
                <img src="../../images/common/title.gif" style="vertical-align:middle;">查看服务日志</td>
            <td style="text-align: right;">
                <!--按钮区域-->
                <a class="easyui-linkbutton l-btn l-btn-small" data-options="iconCls:'icon-search'"  onclick="loadSocket2()">&nbsp;查看日志&nbsp;</a>
                <a class="easyui-linkbutton l-btn l-btn-small" data-options="iconCls:'icon-search'"  onclick="looknearly()">&nbsp;查看最近&nbsp;</a>
                <a class="easyui-linkbutton l-btn l-btn-small" data-options="iconCls:'icon-search'"  onclick="cleanText()">&nbsp;清屏&nbsp;</a>
            </td>
        </tr>
        </tbody>
    </table>
    <!-- 模糊查询 -->
    <table class="btbForm">
        <tbody>
        <tr>
            <!--<td>-->
                <!--小组发送<input id="groupId" class="easyui-combobox" data-options="editable:false,panelHeight:'auto',limitToList:'true'" style="width: 110px;">-->
                <!--&nbsp;&nbsp;&nbsp;组员&nbsp;<input id="userId" class="easyui-combobox" data-options="editable:false,panelHeight:'auto',limitToList:'true'" style="width: 110px;">-->
            <!--</td>-->
        </tr>
        </tbody>
    </table>
    <!-- 表格间距 -->
    <div style="margin-top:5px"></div>
    <!-- 数据表格 -->
    <div>
        <div id="text" style=" overflow-y:scroll; width:100%; height:400px;word-break:break-word;">初始化完毕...</div>
    </div>
    <!--隐藏的昵称信息-->
    <input id="nickName" style="display: none">
    <script type="text/javascript">
        //定义WebSocket接口
        var ws;
        /**
         * jQuery初始化完毕执行的代码
         */
        $(function(){
            loadSocket2();
            setInterval(function(){
                $('#text').css("height",$(document.body).height()*0.8);
            },300);
        });
    
        /**
         * 下滑到底部
         */
        function looknearly() {
            try {
                $("#text").scrollTop($("#text")[0].scrollHeight);
            } catch (e) {
                console.error(e);
            }
        }
    
        /**
         * 获取WebSocket根地址
         */
        function wsPath() {
            //定义ws接口开头协议
            var wsPath = "ws://"+basePath.split("://")[1];
            if(basePath.split("://")[0].trim().toLowerCase().indexOf("https")>-1){
                wsPath = "wss://"+basePath.split("://")[1];
            }
            return wsPath;
        }
    
        function testOhter() {
            $.messager.prompt('提示信息', '请输入ws地址:', function(r){
                if (r){
                    loadSocket2(r);
                }else{
                    loadSocket2();
                }
            });
        }
    
    
        /**
         * 加载websocket数据
         */
        function loadSocket2(file){
            //console.log(ws);
            if(ws!=null&&ws!=undefined&&ws.readyState == 1){
                $('#text').append("连接已存在..<br/>");
                return;
            }
            if(file!=null&&file!=undefined&&file!=''){
                if ("WebSocket" in window){
                    ws=new WebSocket(wsPath()+"/websocket/"+file+".json");
                }else{
                    ws=new SockJS(wsPath()+"/sockjs/"+file+".ws");
                }
            }else{
                if ("WebSocket" in window){
                    ws=new WebSocket(wsPath()+"/ws_showLog.json");
                }else{
                    ws=new SockJS(wsPath()+"/sockjs/ws_showLog.ws");
                }
            }
    
            $('#text').append("<br/>开始建立连接...<br/>");
            ws.onopen=function(event){
                $('#text').append("连接已建立..<br/>");
                $('#start').linkbutton('disable');
            }
            /**接收消息*/
            ws.onmessage=function(event){
                //console.log(event);
                var msg=event.data;
                $('#text').append(msg);
                // 滚动条滚动到最低部
                try {
                    $("#text").scrollTop($("#text")[0].scrollHeight);
                } catch (e) {
                    console.error(e);
                }
            }
            ws.onclose=function(){
                $('#text').append("连接已关闭..<br/>");
                $('#start').linkbutton('enable');
    
            }
            ws.onerror = function(){
                $('#text').append("发生了错误..<br/>");
                $('#start').linkbutton('enable');
            }
    
        }
    
        /**
         * 手动加载websocket数据
         */
        function loadSocket(file){
    
            if(file!=null&&file!=undefined&&file!=''){
                if ("WebSocket" in window){
                    ws=new WebSocket(wsPath()+"/websocket/"+file+".json");
                }else{
                    ws=new SockJS(wsPath()+"/sockjs/"+file+".ws");
                }
            }else{
                if ("WebSocket" in window){
                    ws=new WebSocket(wsPath()+"/ws_showLog.json");
                }else{
                    ws=new SockJS(wsPath()+"/sockjs/ws_showLog.ws");
                }
            }
    
            $('#text').append("<br/>开始建立连接...<br/>");
            ws.onopen=function(event){
                $('#text').append("连接已建立..<br/>");
                $('#start').linkbutton('disable');
            }
            /**接收消息*/
            ws.onmessage=function(event){
                //console.log(event);
                var msg=event.data;
                $('#text').append(msg);
                // 滚动条滚动到最低部
                try {
                    $("#text").scrollTop($("#text")[0].scrollHeight);
                } catch (e) {
                    console.error(e);
                }
            }
            ws.onclose=function(){
                $('#text').append("连接已关闭..<br/>");
                $('#start').linkbutton('enable');
    
            }
            ws.onerror = function(){
                $('#text').append("发生了错误..<br/>");
                $('#start').linkbutton('enable');
            }
    
        }
    
        /**
         * 清除屏幕
         */
        function cleanText() {
            $('#text').html("屏幕已清除...<br/>");
        }
    
    </script>
    
    </body>
    </html>
    

    后台代码

    WebSocketInterceptor拦截器

    package com.faker.filter;
    
    import com.faker.app.util.ToolsUtil;
    import org.apache.log4j.Logger;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.http.server.ServletServerHttpRequest;
    import org.springframework.web.socket.WebSocketHandler;
    import org.springframework.web.socket.server.HandshakeInterceptor;
    
    import javax.servlet.http.HttpSession;
    import java.util.Map;
    
    /**
     * <p>Title: WebSocketInterceptor拦截器 - WebSocketInterceptor_ShowLog</p>
     *
     * <p>Description:WebSocket 适配器 拦截器 显示tomcat日志的拦截器</p>
     *
     * <p>Copyright: Copyright Faker(c) 2018</p>
     *
     * <p>Company: 无</p>
     *
     * @author Anlinxi
     *
     * @version 1.0
     */
    public class WebSocketInterceptor_ShowLog implements HandshakeInterceptor {
    
        /**
         * 日志
         */
        private final static Logger log= Logger.getLogger(WebSocketInterceptor_ShowLog.class);
    
        /**
         * 握手前
         * @param request 请求
         * @param response 响应
         * @param handler ws握手
         * @param attributes 属性
         * @return 是否握手成功
         * @throws Exception 异常
         */
        @Override
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
                                       Map<String, Object> attributes) throws Exception {
            log.info("执行握手: "+handler+"map: "+attributes.values());
            if(request != null){
                ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
                HttpSession session = servletRequest.getServletRequest().getSession();
                if(session!=null){
                    String userId = (String) session.getAttribute("WEBSOCKET_SL_ID");
                    if(userId!=null){
                        session.setAttribute("SESSION_SL_ID", userId);
                        attributes.put("WEBSOCKET_SL_ID", userId);
                    }else{
                        session.setAttribute("SESSION_SL_ID", session.getId());
                        attributes.put("WEBSOCKET_SL_ID", session.getId());
                    }
                }
            }
            return true;
        }
    
        /**
         * 将属性设置到session里
         * @param session session
         * @param attributes Map
         * @param attributeName 待设置的属性名
         * @param attribute 待设置的属性
         */
        private void setAttributes(HttpSession session,Map<String, Object> attributes,String attributeName,String attribute){
            if (ToolsUtil.isNotNull(attribute)) {
                String attributeNameUp = attributeName.toUpperCase();
                session.setAttribute("SESSION_"+attributeNameUp, attribute);
                attributes.put("WEBSOCKET_"+attributeNameUp, attribute);
            }
        }
    
        /**
         * 握手后
         * @param request 请求
         * @param response 响应
         * @param handler ws握手
         * @param exceptions 错误
         */
        @Override
        public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Exception exceptions) {
            log.info("握手后: "+handler+"exceptions: "+exceptions);
        }
    
    }
    
    
    

    WebSocketHandler握手动作

    package com.faker.filter;
    import com.faker.app.util.CpdetectorUtils;
    import com.faker.app.util.TailLogThread;
    import com.faker.app.util.ToolsUtil;
    import net.sf.json.JSONObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.socket.CloseStatus;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketSession;
    import org.springframework.web.socket.handler.TextWebSocketHandler;
    import org.springframework.web.util.HtmlUtils;
    
    import java.io.*;
    import java.util.*;
    
    import static java.lang.Thread.sleep;
    
    /**
     * <p>Title: Websocket处理器 - WebSocketHandler_ShowLog</p>
     *
     * <p>Description:统一发送信息等操作的</p>
     *
     * <p>Copyright: Copyright Faker(c) 2018</p>
     *
     * <p>Company: 无</p>
     *
     * @author Anlinxi
     *
     * @version 1.0
     */
    public class WebSocketHandler_ShowLog extends TextWebSocketHandler {
    
        private final static Logger logger = LoggerFactory.getLogger(WebSocketHandler_ShowLog.class);
    
        /**储存分组信息 分组名:用户分组list*/
        public static final Map<Object, WebSocketSession> userSocketSessionMap;
        static {
            userSocketSessionMap = new HashMap<Object, WebSocketSession>();
        }
    
        /**实例化的类*/
        public static WebSocketHandler_ShowLog webSocketHandler;
        /**运行的日志程序cmd*/
        private static Process process;
        /**输入流*/
        private static InputStream inputStream;
    
        /**
         * 实例化方法
         * @return webSocketHandler
         */
        public static WebSocketHandler_ShowLog getBeans(){
            if(webSocketHandler == null){
                webSocketHandler = new WebSocketHandler_ShowLog();
            }
            return webSocketHandler;
        }
    
        /**
         * 处理前端发送的文本信息
         * js调用websocket.send时候,会调用该方法
         * @param session session
         * @param message 发送信息
         * @throws Exception 异常
         */
        @Override
        protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
            if(message.getPayloadLength()==0){
                return;
            }
            // message.getPayload().toString() 获取消息具体内容
            Map<String, Object> msg = JSONObject.fromObject(message);
            logger.info("handleMessage......."+message.getPayload()+"..........."+msg);
            session.sendMessage(message);
            // 调用方法(发送消息给所有人)
            this.sendMessageToAllUsers(msg.get("msgContent").toString());
        }
    
        /**
         * 当新连接建立的时候,被调用
         * 连接成功时候,会触发页面上onOpen方法
         * @param session session
         * @throws Exception 异常
         */
        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            String uid = (String) session.getAttributes().get("WEBSOCKET_SL_ID");
            if (userSocketSessionMap.get(uid) == null) {//获取用户id
                uid = session.getId();
                userSocketSessionMap.put(uid, session);
            }
            logger.info("======"+uid+" 建立连接完成======");
            //读取配置文件里日志文件地址
            String fileAddr = ToolsUtil.getInstence().getProperties("myConfig","log.tomcat.catalina");
            if(ToolsUtil.isNotNull(fileAddr)){
                //读取现有日志信息
                this.readCatalinaFile(fileAddr,session);
                //进程为空或者未激活时执行cmd命令
                // 执行tail -f命令
                String cmd = "tail -f "+fileAddr;
                try {
                    process = Runtime.getRuntime().exec(cmd);
                    inputStream = process.getInputStream();
                    if(process.isAlive()){
                        // 一定要启动新的线程,防止InputStream阻塞处理WebSocket的线程
                        TailLogThread thread = new TailLogThread(inputStream, session );
                        thread.start();
                    }
                } catch (IOException e) {
                    //logger.error(cmd);
                    System.err.println(cmd);
                    e.printStackTrace();
                    this.close();
                }
    
            }else{
                logger.error("======未配置myConfig文件中log.tomcat.catalina属性(tomcat日志路径)======");
            }
        }
    
        /**
         * 读取现有日志信息
         * @param fileAddr 文件地址
         * @param session WebSocketSession
         */
        private void readCatalinaFile(String fileAddr, WebSocketSession session) {
            Map<String,Object> result = new HashMap<>(4);
            FileInputStream inStream  = null;
            try {
                File file = new File(fileAddr);
                //如果文件夹不存在,先创建文件夹
                if(!file.exists()){
                    System.err.println("文件不存在");
                    return;
                }
                inStream = new FileInputStream(file);
                String bm =  CpdetectorUtils.getInstence().getFileOrIOEncode(fileAddr,"file");
                InputStreamReader isr = new InputStreamReader(inStream, bm);
                BufferedReader reader = new BufferedReader(isr);
                String pythonText;
                //读取的一行
                String readline;
                List<String> sbList = new ArrayList();
                while((readline = reader.readLine())!=null){
                    sbList.add(this.setColorToMsg(readline));
                }
                reader.close();
                int rowsNum  = sbList.size();
                int sendNum  = 0;
                StringBuffer sb = new StringBuffer();
                for(String l:sbList){
                    if(1<=rowsNum&&rowsNum<=400){
                        sb.append(l);
                    }
                    if(sb.length()>4096){
                        session.sendMessage(new TextMessage(sb.toString()));
                        sb.setLength(0);
                        sendNum++;
                        try {
                            //发送一次的时间间隔,给浏览器反映的时间
                            sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else if(rowsNum<=1&&sb.length()>0){
                        session.sendMessage(new TextMessage(sb.toString()));
                        sb.setLength(0);
                        sendNum++;
                    }
                    rowsNum--;
                }
    
                logger.info("原始日志共计"+sbList.size()+"行。发送信息"+sendNum+"次。");
                session.sendMessage(new TextMessage(System.lineSeparator()+System.lineSeparator()+"最近日志加载完毕......"+System.lineSeparator()+System.lineSeparator()));
                inStream.close();
    
            } catch (IllegalStateException | IOException e) {
                e.printStackTrace();
            }
            return;
        }
    
        /**
         * 给日志上色!
         * @param msg 日志消息
         * @return html
         */
        private String setColorToMsg(String msg){
            String color = "";
            if(msg.toLowerCase().contains("error")){
                color = "#ff0000";
            }else if(msg.toLowerCase().contains("warn")){
                color = "#ff9900";
            }else if(msg.toLowerCase().contains("debug")){
                color = "#336600";
            }else if(msg.toLowerCase().contains("info")){
                color = "#0033cc";
            }else if(msg.contains("定时清除游离文件中(时间间隔5分钟)")){
                color = "#C1CDCD";
            }else if(msg.contains("Exception")){
                color = "#FFD700";
            }
            //对文本中含有的html代码进行转义
            msg = HtmlUtils.htmlEscape(msg);
            String html = "<p class='log_msg' style='color:"+color+"'>"+msg+"</p>";
            return html;
        }
    
        /**
         * 当连接关闭时被调用
         * @param session session
         * @param status 连接状态
         * @throws Exception 异常
         */
        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
            logger.info("Websocket:" + session.getId() + "已经关闭");
            Iterator<Map.Entry<Object, WebSocketSession>> it = userSocketSessionMap
                    .entrySet().iterator();
            // 移除Socket会话
            logger.info("======关闭连接======");
            while (it.hasNext()) {
                Map.Entry<Object, WebSocketSession> entry = it.next();
                if (entry.getValue().getId().equals(session.getId())) {
                    userSocketSessionMap.remove(entry.getKey());
                    logger.warn("Socket会话已经移除:用户ID" + entry.getKey());
                    break;
                }
            }
            this.close();
        }
    
        /**
         * 关闭程序和流
         */
        private void close(){
    //        logger.info("关闭ws的程序和流!");
    //        try {
    //            if(inputStream != null)
    //                inputStream.close();
    //        } catch (Exception e) {
    //            e.printStackTrace();
    //        }
    //        if(process != null){
    //            process.destroy();
    //        }
        }
    
        /**
         * 传输错误时调用
         * @param session session
         * @param exception 异常
         * @throws Exception 异常
         */
        @Override
        public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
            if (session.isOpen()) {
                session.close();
            }
            Iterator<Map.Entry<Object, WebSocketSession>> it = userSocketSessionMap
                    .entrySet().iterator();
            logger.info("======消息传输错误======");
            // 移除Socket会话
            while (it.hasNext()) {
                Map.Entry<Object, WebSocketSession> entry = it.next();
                if (entry.getValue().getId().equals(session.getId())) {
                    userSocketSessionMap.remove(entry.getKey());
                    logger.error("Socket会话已经移除:用户ID" + entry.getKey());
                    break;
                }
            }
            this.close();
        }
    
        /**
         * 给所有在线用户发送消息
         * @param message 发送消息
         */
        public void sendMessageToAllUsers(String message) {
            Iterator<Map.Entry<Object, WebSocketSession>> it = userSocketSessionMap
                    .entrySet().iterator();
            logger.info("======群发======");
            // 多线程群发
            while (it.hasNext()) {
                final Map.Entry<Object, WebSocketSession> entry = it.next();
                if (entry.getValue().isOpen()) {
                    new Thread(new Runnable() {
                        public void run() {
                            try {
                                if (entry.getValue().isOpen()) {
                                    entry.getValue().sendMessage(new TextMessage(message));
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }).start();
                }
            }
        }
    
    
        /**
         * 给某个用户发送消息
         * @param userId 用户id
         * @param message 发送信息
         */
        public void sendMessageToUser(String userId,String message) throws IOException {
            WebSocketSession session = userSocketSessionMap.get(userId);
            logger.info("======给某个用户发送消息======");
            if (session != null && session.isOpen()) {
                session.sendMessage(new TextMessage(message));
            }
        }
    
    }
    

    WebSocketConfig配置类

    package com.faker.filter;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    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.standard.ServletServerContainerFactoryBean;
    import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
    
    /**
     * <p>Title: websocket配置类 - WebSocketConfig_ShowLog</p>
     *
     * <p>Description:websocket日志显示的配置信息</p>
     *
     * <p>Copyright: Copyright Faker(c) 2018</p>
     *
     * <p>Company: 无</p>
     *
     * @author Anlinxi
     *
     * @version 1.0
     */
    @Configuration
    //这个标注可以不加,如果有加,要extends WebMvcConfigurerAdapter
    @EnableWebMvc
    @EnableWebSocket
    public class WebSocketConfig_ShowLog extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
    
        /**
         * 注册websocket的信息
         * @param registry registry
         */
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            //设置websocket的地址,注册Handler,注册Interceptor
            registry.addHandler(SocketHandler(), "/ws_showLog.json").setAllowedOrigins("*").addInterceptors(new WebSocketInterceptor_ShowLog());
            //注册SockJS,提供SockJS支持(主要是兼容ie8)
            registry.addHandler(SocketHandler(), "/sockjs/ws_showLog.ws")
                    //允许跨域
                    .setAllowedOrigins("*")
                    //注册Interceptor
                    .addInterceptors(new WebSocketInterceptor_ShowLog())
                    //支持sockjs协议
                    .withSockJS();
        }
    
        @Bean
        public org.springframework.web.socket.WebSocketHandler SocketHandler(){
            return new com.faker.filter.WebSocketHandler_ShowLog ();
        }
    
    
        /**
         * 配置webSocket引擎
         * 用于tomcat 可以不配置
         * @return Bean
         */
        @Bean
        public ServletServerContainerFactoryBean createWebSocketContainer() {
            ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
            container.setMaxTextMessageBufferSize(8192);
            container.setMaxBinaryMessageBufferSize(8192);
            return container;
        }
    
    }
    
    

    TailLogThread实时读取日志的线程

    package com.faker.app.util;
    import org.apache.commons.codec.StringEncoder;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketSession;
    import org.springframework.web.util.HtmlUtils;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.List;
    
    public class TailLogThread extends Thread {
    
        /**读取缓冲*/
        private BufferedReader reader;
        /**WebSocketSession*/
        private WebSocketSession session;
        /**消息队列*/
        private List<String> msgs = new ArrayList<>();
        /**cmd的文本编码*/
        private String encoding;
    
        public TailLogThread(InputStream in, WebSocketSession session) {
            this.reader = new BufferedReader(new InputStreamReader(in));
            this.session = session;
    
        }
    
        /**
         * 读取日志的方法
         */
        @Override
        public void run() {
            this.method1();
        }
    
        /**
         * 原始读取日志的方法
         */
        private void method1(){
            String line;
            try {
                if(session.isOpen()){
                    //如果消息队列有数据,则先输出消息队列消息
                    if(msgs.size()>0){
                        List<String> errMsgs = new ArrayList<>();
                        for(String msg:msgs){
                            try {
                                //输出消息队列消息
                                this.sendMsg(msg,session);
                            } catch (IOException e) {
                                //未发送的信息储存在错误消息队列中
                                System.err.println(e.getMessage());
                                errMsgs.add(msg);
                            }
                        }
                        //清空消息队列
                        msgs.clear();
                        //如果有错误的消息队列,储存给消息队列中
                        if(errMsgs.size()>0){
                            msgs = errMsgs;
                        }
                    }
                }
                while((line = reader.readLine()) != null) {
                    if(encoding==null||encoding==""){
                        encoding = this.StringEncoding(line);
                    }
                    //String text = new String(line.getBytes(encoding),"UTF-8");
                    // 将实时日志通过WebSocket发送给客户端,给每一行添加一个HTML换行
                    if(session.isOpen()){
                        this.sendMsg(line,session);
                    }else{
                        msgs.add(line);
                    }
                }
            } catch (IOException e) {
                System.err.println(e.getMessage());
                e.printStackTrace();
            }
        }
    
        /**
         * 发送消息
         * @param msg
         * @param session
         * @throws IOException
         */
        private void sendMsg(String msg,WebSocketSession session) throws IOException {
            String str = msg;
            if(1==1){
                str = this.setColorToMsg(msg);
            }else{
                //文本域
                str = str + System.lineSeparator();
            }
            session.sendMessage(new TextMessage(str));
        }
    
        /**
         * 判断字符串编码
         * @param sb 字符串
         * @return 编码类型
         */
        private String StringEncoding(String sb){
            String backEncoding = "";
            String iso8859 = this.testEncoding(sb,"iso8859-1");
            String gbk = this.testEncoding(sb,"gbk");
            String utf8 = this.testEncoding(sb,"utf-8");
            if(iso8859.equals(sb.toString())){
                backEncoding = "iso8859";
            }else  if(gbk.equals(sb.toString())){
                backEncoding = "gbk";
            }else  if(utf8.equals(sb.toString())){
                backEncoding = "utf8";
            }
            return backEncoding;
        }
    
        /**
         * 给日志上色!
         * @param msg 日志消息
         * @return html
         */
        private String setColorToMsg(String msg){
            String color = "";
            if(msg.toLowerCase().contains("error")){
                color = "#ff0000";
            }else if(msg.toLowerCase().contains("warn")){
                color = "#ff9900";
            }else if(msg.toLowerCase().contains("debug")){
                color = "#336600";
            }else if(msg.toLowerCase().contains("info")){
                color = "#0033cc";
            }else if(msg.contains("定时清除游离文件中(时间间隔5分钟)")){
                color = "#C1CDCD";
            }else if(msg.contains("Exception")){
                color = "#FFD700";
            }
            //对文本中含有的html代码进行转义
            msg = HtmlUtils.htmlEscape(msg);
            String html = "<p class='log_msg' style='color:"+color+"'>"+msg+"</p>";
            return html;
        }
    
        /**
         * 测试编码
         * @param sb 文本
         * @param encoding 编码
         * @return
         */
        private String testEncoding(String sb,String encoding){
            String tmpStrin = null;
            try {
                tmpStrin = new String(sb.toString().getBytes(encoding));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return tmpStrin;
        }
    }
    
    

    ToolsUtil工具类

    package com.faker.app.util;
    
    import com.sun.istack.internal.NotNull;
    import net.sf.json.JSONArray;
    import net.sf.json.JSONObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.*;
    
    /**
     * <p>Title: 常用的工具类 - ToolsUtil</p>
     *
     * <p>Description:常用工具放这儿</p>
     *
     * <p>Copyright: Copyright Faker(c) 2018</p>
     *
     * <p>Company: 无</p>
     *
     * @author Anlinxi
     *
     * @version 1.0
     */
    public class ToolsUtil {
    
        /**
         * 工具类实例化对象
         */
        private static ToolsUtil toolsUtil = null;
    
        /**
         * 私有化构造函数防止new对象消耗内存
         */
        private ToolsUtil() {
    
        }
        /**
         * 静态实例化方法获取实例化对象
         * 使用同一个lCsinginUtil,减少内存占用
         * @return LCsinginUtil
         */
        public static ToolsUtil getInstence(){
            if(toolsUtil==null){
                toolsUtil = new ToolsUtil();
            }
            return toolsUtil;
        }
    
        /**
         * 日志
         */
        private final static Logger logger = LoggerFactory.getLogger(ToolsUtil.class);
    
        /**
         * 发送json数据
         * @param response 响应
         * @param object 发送的对象
         */
        public static void WriterJson(@NotNull HttpServletResponse response, Object object){
            try {
                String backJson = null;
                if(object instanceof List){
                    JSONArray jsonArray = JSONArray.fromObject(object);
                    backJson = jsonArray.toString();
                }else{
                    JSONObject jsonObject = JSONObject.fromObject(object);
                    backJson = jsonObject.toString();
                }
                response.setHeader("Cache-Control", "no-cache");
                response.setHeader("Access-Control-Allow-Origin", "*");
                response.setContentType("text/json;charset=utf-8");
                PrintWriter printWriter = response.getWriter();
                logger.info("发送json数据>>>>>>>>>>>>>>>>"+backJson);
                printWriter.write(backJson);
                printWriter.flush();
                printWriter.close();
            } catch (IOException e) {
                logger.error("发送json数据失败");
                e.printStackTrace();
            }
        }
    
    
        /**
         * 判断一个字符串是否为null或空字符,是则返回false,否为true
         * @param str 字符串对象
         * @return 是否为null或空字符
         */
        public static boolean isNotNull(Object str){
            return !isNullOrEmpty(str);
        }
    
        /**
         * 判断一个字符串是否为null或空字符,是则返回true,否为false
         * @param str 字符串对象
         * @return 是否为null或空字符
         */
        public static boolean isNullOrEmpty(Object str){
            String t = trim(str);
            if(t.equals("") || t==null){
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * 字符串对象去空
         * @param str 字符串对象
         * @return 替换空
         */
        public static String trim(Object str){
            return trim(str, "");
        }
    
        /**
         * 将对象转换为字符串类型
         * @param obj 对象
         * @param nullToString 如果为空就返回该默认值
         * @return 字符串类型
         */
        public static String trim(Object obj, String nullToString){
            return obj == null ? nullToString : obj.toString().trim();
        }
    
        /**
         * 读取python文件代码
         * @param fileName 文件名(含后缀)
         * @param path 文件夹路径
         * @return 读取的字符串
         */
        public Boolean download( HttpServletResponse response,String fileName, String path){
            FileInputStream inStream  = null;
            try {
                inStream = new FileInputStream(path+fileName);
                //通过available方法取得流的最大字符数
                byte[] inOutb = new byte[inStream.available()];
                //读入流,保存在byte数组
                inStream.read(inOutb);
                //设置文件类型
                response.setContentType(Files.probeContentType(Paths.get(path+fileName)));
                //将response提取流
                OutputStream out = response.getOutputStream();
                //将byte数组写入response中
                out.write(inOutb);
                //刷新流
                out.flush();
                out.close();
                inStream.close();
            } catch (Exception e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
                return false;
            }
            return true;
        }
    
        /**
         * 获取系统信息
         */
        Properties prop = System.getProperties();
    
        /**
         * 获取当前服务器所在系统的路径
         * @return 服务器的路径
         */
        public String classRootPath() {
            //确定jython目录的路径
            String classRootPath = this.getClass().getResource("/").toString();
            String os = prop.getProperty("os.name");
            if (os != null && os.toLowerCase().indexOf("linux") > -1) {
                return classRootPath.replace("file:", "");
            } else {
                return classRootPath.replace("file:/", "");
            }
        }
    
        /**
         * 获取properties文件配置信息
         * @param fileName 文件名(不含后缀)
         * @param key 取值对应的键名
         * @return 值
         * @throws Exception 异常
         */
        public String getProperties (String fileName,String key) throws Exception {
            //文件名不为空
            if(fileName!=null&&!"".equals(fileName)){
                //不需要后缀
                if(fileName.contains(".properties")){
                    fileName.replace(".properties","");
                }
                if(key!=null&&!"".equals(key)){
                    //properties文件放置的位置
                    String fileAddr = "config/"+fileName;
                    ResourceBundle resource = ResourceBundle.getBundle(fileAddr);
                    String text = new String(resource.getString(key).getBytes("ISO-8859-1"), "UTF8");
                    return text;
                }else{
                    throw new Exception("取值键名为空!");
                }
            }else{
                throw new Exception("文件名为空!");
            }
        }
    }
    
    

    给日志上色的方法

    这个方法是去匹配字符串,不是原本的错误就是红色,所以可能有误差。配色也不会配,颜色区分这些还是前端要敏感一些。

    	/**
         * 给日志上色!
         * @param msg 日志消息
         * @return html
         */
        private String setColorToMsg(String msg){
            String color = "";
            if(msg.toLowerCase().contains("error")){
                color = "#ff0000";
            }else if(msg.toLowerCase().contains("warn")){
                color = "#ff9900";
            }else if(msg.toLowerCase().contains("debug")){
                color = "#336600";
            }else if(msg.toLowerCase().contains("info")){
                color = "#0033cc";
            }else if(msg.contains("定时清除游离文件中(时间间隔5分钟)")){
                color = "#C1CDCD";
            }else if(msg.contains("Exception")){
                color = "#FFD700";
            }
            //对文本中含有的html代码进行转义
            msg = HtmlUtils.htmlEscape(msg);
            String html = "<p class='log_msg' style='color:"+color+"'>"+msg+"</p>";
            return html;
        }
        ```
    
    展开全文
  • 功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段...

    功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息

    一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段时间去获取服务器日志?out了,试试用websocket方式吧

    我用bottle框架,写了个websocket服务端,浏览器连接到websocket server,再用python subprocess获取远程服务器的日志信息,subprocess,就是用Popen调用shell的shell命令而已,这样可以获取到实时的日志了,然后再send到websocket server中,那连接到websocket server的浏览器,就会实时展现出来了

    用二台服务器来实现这个场景,A服务器是websocket服务端,B服务器是日志端

    A服务器是我浏览器本机,websocket服务端也是这台机,IP是:192.168.2.222

    B服务器是要远程查看日志的服务器,我这里用:192.168.2.224

    以下是在A服务器的操作(Python2)

    安装:

    pip install bottle

    pip install websocket-client

    pip install bottle-websocket

    websocket servet的python代码:

    1 #!/usr/bin/env python

    2 #-*- coding:utf-8 -*-

    3 from bottle importget, run4 from bottle.ext.websocket importGeventWebSocketServer5 from bottle.ext.websocket importwebsocket6 users = set() #连接进来的websocket客户端集合

    7 @get('/websocket/', apply=[websocket])8 defchat(ws):9 users.add(ws)10 whileTrue:11 msg = ws.receive() #接客户端的消息

    12 ifmsg:13 for u inusers:14 u.send(msg) #发送信息给所有的客户端

    15 else:16 break

    17 #如果有客户端断开连接,则踢出users集合

    18 users.remove(ws)19 run(host='0.0.0.0', port=8000, server=GeventWebSocketServer)

    记得安装bottle、websocket-client 、bottle-websocket 模块,服务端允许所有的IP访问其8000端口

    websocket服务端除了用以上的方法外,还可以用这下面的方法实现:

    http://www.linuxyw.com/831.html

    在电脑桌面,写一个简单的HTML5  javascripts页面,随便命名了,如test.html,这个页面使用了websocket连接到websocket服务端:

    1

    2

    3

    4

    5

    6 #msg{

    7 width:400px; height:400px; overflow:auto; border:2px solid #000000;background-color:#000000;color:#ffffff;

    8 }9

    10

    11

    12

    实时日志

    13

    14

    15

    16 $(document).ready(function() {17 /* !window.WebSocket、window.MozWebSocket检测浏览器对websocket的支持*/

    18 if(!window.WebSocket) {19 if(window.MozWebSocket) {20 window.WebSocket =window.MozWebSocket;21 } else{22 $('#msg').prepend("

    你的浏览器不支持websocket

    ");23 }24 }25 /* ws = new WebSocket 创建WebSocket的实例 注意设置对以下的websocket的地址哦*/

    26 ws = new WebSocket('ws://192.168.2.222:8000/websocket/');27 /*

    28 ws.onopen 握手完成并创建TCP/IP通道,当浏览器和WebSocketServer连接成功后,会触发onopen消息29 ws.onmessage 接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含server传输过来的数据;30 */

    31 ws.onopen =function(evt) {32 $('#msg').append('

    websocket连接成功');33 }34 ws.onmessage =function(evt) {35 $('#msg').prepend('' + evt.data + '');36 }37 });38

    39

    40

    展开全文
  • 功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息 一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔...

    功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息

    一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段时间去获取服务器日志?out了,试试用websocket方式吧

    我用bottle框架,写了个websocket服务端,浏览器连接到websocket server,再用python subprocess获取远程服务器的日志信息,subprocess,就是用Popen调用shell的shell命令而已,这样可以获取到实时的日志了,然后再send到websocket server中,那连接到websocket server的浏览器,就会实时展现出来了

    用二台服务器来实现这个场景,A服务器是websocket服务端,B服务器是日志端

    A服务器是我浏览器本机,websocket服务端也是这台机,IP是:192.168.2.222

    B服务器是要远程查看日志的服务器,我这里用:192.168.2.224

    以下是A服务器的操作(Python2)

    安装

        pip install bottle

        pip install websocket-client

        pip install bottle-websocket

    websocket servet的python代码:

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 from bottle import get, run
     4 from bottle.ext.websocket import GeventWebSocketServer
     5 from bottle.ext.websocket import websocket
     6 users = set()   # 连接进来的websocket客户端集合
     7 @get('/websocket/', apply=[websocket])
     8 def chat(ws):
     9     users.add(ws)
    10     while True:
    11         msg = ws.receive()  # 接客户端的消息
    12         if msg:
    13             for u in users:
    14                 u.send(msg) # 发送信息给所有的客户端
    15         else:
    16             break
    17     # 如果有客户端断开连接,则踢出users集合
    18     users.remove(ws)
    19 run(host='0.0.0.0', port=8000, server=GeventWebSocketServer)

     

    记得安装bottle、websocket-client 、bottle-websocket 模块,服务端允许所有的IP访问其8000端口

     

    websocket服务端除了用以上的方法外,还可以用这下面的方法实现:

     http://www.linuxyw.com/831.html

    在电脑桌面,写一个简单的HTML5  javascripts页面,随便命名了,如test.html,这个页面使用了websocket连接到websocket服务端:

     1  <!DOCTYPE html>
     2 <html>
     3 <head>
     4 </head>
     5     <style>
     6         #msg{
     7             width:400px; height:400px; overflow:auto; border:2px solid #000000;background-color:#000000;color:#ffffff;
     8     }
     9     </style>
    10 </head>
    11 <body>
    12     <p>实时日志</p>
    13     <div id="msg"></div>
    14     <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
    15     <script>
    16     $(document).ready(function() {
    17         /* !window.WebSocket、window.MozWebSocket检测浏览器对websocket的支持*/
    18         if (!window.WebSocket) {
    19             if (window.MozWebSocket) {
    20                 window.WebSocket = window.MozWebSocket;
    21             } else {
    22                 $('#msg').prepend("<p>你的浏览器不支持websocket</p>");
    23             }
    24         }
    25         /* ws = new WebSocket 创建WebSocket的实例  注意设置对以下的websocket的地址哦*/
    26         ws = new WebSocket('ws://192.168.2.222:8000/websocket/');
    27         /*
    28             ws.onopen  握手完成并创建TCP/IP通道,当浏览器和WebSocketServer连接成功后,会触发onopen消息
    29             ws.onmessage 接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含server传输过来的数据;
    30         */
    31         ws.onopen = function(evt) {
    32             $('#msg').append('<li>websocket连接成功</li>');
    33         }
    34         ws.onmessage = function(evt) {
    35             $('#msg').prepend('<li>' + evt.data + '</li>');
    36         }
    37     });
    38 </script>
    39 </body>
    40 </html>

     

    到这里,就搞定浏览器连接到websocket服务端的场景了,现在要A服务器里‘远程查看日志.py’,去采集B服务器的实时信息了,其实采集原理很简单,就是使用shell中的tailf命令,实时显示最新的信息而已,我们在这段脚本中,使用subprocess.Popen()来远程查看日志信息:

    python代码如下:

     1 #!/usr/bin/python
     2 # encoding=utf-8
     3 import subprocess
     4 import time
     5 from websocket import create_connection
     6 # 配置远程服务器的IP,帐号,密码,端口等,因我做了双机密钥信任,所以不需要密码
     7 r_user = "root"
     8 r_passwd='jason_zhang'
     9 r_ip = "192.168.2.224"
    10 r_port = 22
    11 r_log = "/tmp/test.log"   # 远程服务器要被采集的日志路径
    12 # websocket服务端地址
    13 ws_server = "ws://192.168.2.222:8000/websocket/"
    14 # 执行的shell命令(使用ssh远程执行)
    15 cmd = "/usr/bin/ssh -p {port} {user}@{ip} /usr/bin/tailf {log_path}".format(user=r_user,ip=r_ip,port=r_port,log_path=r_log)
    16 def tailfLog():
    17     """获取远程服务器实时日志,并发送到websocket服务端"""
    18     popen = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
    19     print('连接成功')
    20     ws = create_connection(ws_server)   # 创建websocket连接
    21     while True:
    22         line = popen.stdout.readline().strip()  #获取内容
    23         if line:
    24             ws.send(line)   #把内容发送到websocket服务端
    25         print (time.time())
    26 if __name__ == '__main__':
    27     tailfLog()

     

     在服务器B的日志文件随便输入点东西,再运行服务器A的获取日志脚本

     

    获取到的结果

     

       

    tailfLog()文章最后再解析subprocess.Popen的原理和功能

    执行websocket服务端脚本和上面这个websocket客户端采集脚本,再打开用浏览器打开上面的html5页面后,环境就基本部署好了,双websocket客户端连接到websocket服务端中

    上面脚本指定的r_log = "/tmp/web_socket.log"日志路径,我们需要生成这个日志文件,并不停地往里面写入日志,这样才能在浏览器中实时显示效果(真实场景中,可以指定服务器某日志,如apache,nginx日志等)

     

    刚才提到subprocess.Popen的原理和功能,请看以下资料:

    http://www.cnblogs.com/fengbeihong/articles/3374132.html

    bottle websocket参考资料:

    http://rfyiamcool.blog.51cto.com/1030776/1269232/

     

    转载于:https://www.cnblogs.com/xiaoyou2018/p/9328950.html

    展开全文
  • 功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段...
  • 功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段...
  • 功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段...
  • websocket 配置 package com.sitech.cmap.comp.wsg.cntr.oracle.websocket; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org....
  • 摘要用websocket技术,在运维工具部署环境的时候,实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看,可以不使用ajax加载服务器日志的形式了。本次使用到的模块有:bottle,bottle-websocket,...
  • webSocket实现Web实时服务器日志输出

    千次阅读 2019-04-12 10:03:23
    由于项目要求,需要在web页面上实时显示服务器上某日志文件的内容,针对该需求,经查找一些大神的实现方式,主要可由两种方式实现: 前台定时向后台发送请求,后台连接linux获取日志信息 使用webSocket建立通信,...
  • 前言最近需要做一个新功能,要求在浏览器可以看到服务器上的日志文件的内容,并且实时显示,也就是相当于要在浏览器实现Linux下的tail -f 的功能。 最开始的思路是使用Ajax定时向后端请求数据并进行展示,但是这样...
  • 本文介绍一个基于websocket实现的远程实时日志系统,可以通过浏览器查看远程移动设备的实时运行日志。...2. 浏览器日志查看页面:与服务器建立websocket连接,通过websocket接收指定设备的实时运行日志显示
  • 一、开始的话使用python简单的实现websocket服务器,可以在浏览器上实时显示远程服务器的日志信息。之前做了一个web版的发布系统,但没实现在线看日志,每次发布版本后,都需要登录到服务器上查看日志,非常麻烦,...
  •  使用python实现websocket服务器,可以在浏览器上实时显示远程服务器的日志。  之前写了一个发布系统,每次发布版本后,为了了解发布情况(进度、是否有错误)都会登录到服务器上查看日志,有点麻烦,如果发布的...

空空如也

空空如也

1 2 3 4
收藏数 79
精华内容 31
热门标签
关键字:

websocket实时显示日志