之前面的博客爲基礎,最近一篇爲Spring Boot 入門(十):集成Redis哨兵模式,實現Mybatis二級緩存。本篇博客主要介紹了Spring Boot集成 Web Socket進行日誌的推送,並實時顯示在頁面上。html
第一個jar包是websocket的,第二個jar包是關於環形隊列的jar包,本案例是經過本地隊列存儲日誌。有條件的話,最好經過中間件存儲(eg:redis,mq……)。經過本地隊列存儲日誌會存在日誌丟失的狀況,且日誌量太大,會把頁面卡死。java
1 <!--begin web socket--> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-websocket</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>com.lmax</groupId> 8 <artifactId>disruptor</artifactId> 9 <version>3.4.2</version> 10 </dependency> 11 <!--end web socket-->
(1).在logback中增長監聽器jquery
並根據logback編寫相應的監聽器ProcessLogFilterweb
1 @Service 2 public class ProcessLogFilter extends Filter<ILoggingEvent> { 3 4 @Override 5 public FilterReply decide(ILoggingEvent event) { 6 LoggerMessage loggerMessage = new LoggerMessage( 7 event.getMessage() 8 , DateFormat.getDateTimeInstance().format(new Date(event.getTimeStamp())), 9 event.getThreadName(), 10 event.getLoggerName(), 11 event.getLevel().levelStr 12 ); 13 LoggerDisruptorQueue.publishEvent(loggerMessage); 14 return FilterReply.ACCEPT; 15 } 16 }
該監聽器將監聽的日誌消息推送到本地消息隊列中,而後頁面經過 Web Socket 去此隊列獲取日誌信息,從而在頁面顯示redis
(2).編寫日誌處理器spring
1 //進程日誌事件內容載體 2 @Data 3 @NoArgsConstructor 4 @AllArgsConstructor 5 public class LoggerEvent { 6 private LoggerMessage log; 7 }
1 /** 2 * Content :進程日誌事件工廠類 3 */ 4 public class LoggerEventFactory implements EventFactory<LoggerEvent> { 5 @Override 6 public LoggerEvent newInstance() { 7 return new LoggerEvent(); 8 } 9 }
1 /** 2 * Content :進程日誌事件處理器 3 */ 4 @Component 5 public class LoggerEventHandler implements EventHandler<LoggerEvent> { 6 7 @Autowired 8 private SimpMessagingTemplate messagingTemplate; 9 10 @Override 11 public void onEvent(LoggerEvent stringEvent, long l, boolean b) { 12 messagingTemplate.convertAndSend("/topic/pullLogger", stringEvent.getLog()); 13 } 14 }
日誌事件處理器的做用是監聽本地環形隊列中的消息,若是有消息,就會將這些消息推送到 Socket 管道中緩存
(3).編寫頁面服務器
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <title>歡迎頁</title> 7 <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> 8 <script src="plugins/jQuery/jquery-2.2.3.min.js"></script> 9 <script src="js/websocket/sockjs.min.js"></script> 10 <script src="js/websocket/stomp.min.js"></script> 11 </head> 12 <body> 13 <div class="panel panel-default"> 14 <h1>jvm進程內的日誌</h1> 15 <button onclick="openSocket()">開啓日誌</button> 16 <button onclick="closeSocket()">關閉日誌</button> 17 <div id="log-container" style="height: 600px; overflow-y: scroll; background: #333; color: #aaa; padding: 10px;"> 18 <div></div> 19 </div> 20 </div> 21 <script> 22 var stompClient = null; 23 $(document).ready(function () { 24 openSocket(); 25 }); 26 27 function openSocket() { 28 if (stompClient == null) { 29 var socket = new SockJS('http://localhost:8080/websocket?token=kl'); 30 stompClient = Stomp.over(socket); 31 stompClient.connect({token: "kl"}, function (frame) { 32 stompClient.subscribe('/topic/pullLogger', function (event) { 33 var content = JSON.parse(event.body); 34 $("#log-container div").append("<font color='red'>" + content.timestamp + "</font>|<font color='highlight'>" + content.level + "</font> |<font color='green'>" + content.threadName + "</font>| <font color='boldMagenta'>" + content.className + "</font>|<font color='cyan'>" + content.body + "</font>").append("<br/>"); 35 $("#log-container").scrollTop($("#log-container div").height() - $("#log-container").height()); 36 }, { 37 token: "kltoen" 38 }); 39 }); 40 } 41 } 42 43 function closeSocket() { 44 if (stompClient != null) { 45 stompClient.disconnect(); 46 stompClient = null; 47 } 48 } 49 </script> 50 </body> 51 </html>
頁面連接web Socket服務器,若是有消息,就能獲取websocket
(4).其餘輔助類app
環形本地隊列類
1 package com.learn.hello.system.common.queue; 2 3 import com.learn.hello.modules.entity.LoggerMessage; 4 import com.learn.hello.system.common.event.LoggerEvent; 5 import com.learn.hello.system.common.event.LoggerEventFactory; 6 import com.learn.hello.system.common.event.LoggerEventHandler; 7 import com.lmax.disruptor.RingBuffer; 8 import com.lmax.disruptor.dsl.Disruptor; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.stereotype.Component; 11 12 import java.util.concurrent.Executor; 13 import java.util.concurrent.Executors; 14 15 /** 16 * Content :Disruptor 環形隊列 17 */ 18 @Component 19 public class LoggerDisruptorQueue { 20 21 private Executor executor = Executors.newCachedThreadPool(); 22 23 // The factory for the event 24 private LoggerEventFactory factory = new LoggerEventFactory(); 25 26 27 // Specify the size of the ring buffer, must be power of 2. 28 private int bufferSize = 2 * 1024; 29 30 // Construct the Disruptor 31 private Disruptor<LoggerEvent> disruptor = new Disruptor<>(factory, bufferSize, executor); 32 ; 33 34 35 private static RingBuffer<LoggerEvent> ringBuffer; 36 37 38 @Autowired 39 LoggerDisruptorQueue(LoggerEventHandler eventHandler) { 40 disruptor.handleEventsWith(eventHandler); 41 this.ringBuffer = disruptor.getRingBuffer(); 42 disruptor.start(); 43 } 44 45 public static void publishEvent(LoggerMessage log) { 46 long sequence = ringBuffer.next(); // Grab the next sequence 47 try { 48 LoggerEvent event = ringBuffer.get(sequence); // Get the entry in the Disruptor 49 // for the sequence 50 event.setLog(log); // Fill with data 51 } finally { 52 ringBuffer.publish(sequence); 53 } 54 } 55 56 }
消息實體類
1 package com.learn.hello.modules.entity; 2 3 import lombok.AllArgsConstructor; 4 import lombok.Data; 5 import lombok.NoArgsConstructor; 6 7 // 日誌實體類 8 @Data 9 @AllArgsConstructor 10 @NoArgsConstructor 11 public class LoggerMessage { 12 private String body; 13 private String timestamp; 14 private String threadName; 15 private String className; 16 private String level; 17 }
頁面中的顏色能夠自行設置
.