前言
今天来做个有趣的东西,就是实时将系统日志输出的前端web页面,因为是实时输出,所有第一时间就想到了使用webSocket,而且在spring boot中,使用websocket超级方便,阅读本文,你会接触到以下关键词相关技术,WebSocket(stopmp服务端),stomp协议,sockjs.min.js,stomp.min.js(stomp客户端),本文使用到的其实就是使用spring boot自带的webSocket模块提供stomp的服务端,前端使用stomp.min.js做stomp的客户端,使用sockjs来链接,前端订阅后端日志端点的消息,后端实时推送,达到日志实时输出到web页面的目的,效果如下图
首先了解下stomp
STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。
STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。
STOMP是一个非常简单和容易实现的协议,其设计灵感源自于HTTP的简单性。尽管STOMP协议在服务器端的实现可能有一定的难度,但客户端的实现却很容易。例如,可以使用Telnet登录到任何的STOMP代理,并与STOMP代理进行交互。
下面是具体的步骤,主要是日志信息的获取和日志信息的推送,不多说,上代码
一.引入spring boot websocket依赖
1
2
3
4
|
< dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-websocket</ artifactId > </ dependency > |
二.新增日志消息实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/** * Created by kl on 2017/10/9. * Content :日志消息实体,注意,这里为了减少篇幅,省略了get,set代码 */ public class LoggerMessage{ private String body; private String timestamp; private String threadName; private String className; private String level; public LoggerMessage(String body, String timestamp, String threadName, String className, String level) { this .body = body; this .timestamp = timestamp; this .threadName = threadName; this .className = className; this .level = level; } public LoggerMessage() { } } |
三. 创建一个阻塞队列
作为日志系统输出的日志的一个临时载体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class LoggerQueue { //队列大小 public static final int QUEUE_MAX_SIZE = 10000 ; private static LoggerQueue alarmMessageQueue = new LoggerQueue(); //阻塞队列 private BlockingQueueblockingQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE); private LoggerQueue() { } public static LoggerQueue getInstance() { return alarmMessageQueue; } /** * 消息入队 * @param log * @return */ public boolean push(LoggerMessage log) { return this .blockingQueue.add(log); //队列满了就抛出异常,不阻塞 } /** * 消息出队 * @return */ public LoggerMessage poll() { LoggerMessage result = null ; try { result = this .blockingQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } return result; } } |
四.获取logback的日志,塞入日志队列中
1.定义Logfilter拦截输出日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class LogFilter extends Filter{ @Override public FilterReply decide(ILoggingEvent event) { LoggerMessage loggerMessage = new LoggerMessage( event.getMessage() , DateFormat.getDateTimeInstance().format( new Date(event.getTimeStamp())), event.getThreadName(), event.getLoggerName(), event.getLevel().levelStr ); LoggerQueue.getInstance().push(loggerMessage); return FilterReply.ACCEPT; } } |
2.配置logback.xml,添加我们自定义的filter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<? xml version = "1.0" encoding = "UTF-8" ?> < configuration scan = "true" > < include resource = "org/springframework/boot/logging/logback/defaults.xml" /> < property name = "LOG_FILE" value = "${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}" /> < include resource = "org/springframework/boot/logging/logback/file-appender.xml" /> < appender name = "CONSOLE" class = "ch.qos.logback.core.ConsoleAppender" > < encoder > < pattern >${CONSOLE_LOG_PATTERN}</ pattern > < charset >utf8</ charset > </ encoder > < filter class = "com.example.websocket.LogFilter" ></ filter > </ appender > < root level = "INFO" > < appender-ref ref = "FILE" /> < appender-ref ref = "CONSOLE" /> </ root > </ configuration > |
五.配置WebSocket消息代理端点,即stomp服务端
1
2
3
4
5
6
7
8
9
|
@Configuration public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint( "/websocket" ) .setAllowedOrigins( "http://localhost:8976" ) .withSockJS(); } } |
注意:为了连接安全,setAllowedOrigins设置的允许连接的源地址,如果在非这个配置的地址下发起连接会报403,进一步还可以使用addInterceptors设置拦截器,来做相关的鉴权操作
六.启动类,开启webSocket消息代理功能,并推送日志信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
@SpringBootApplication @EnableScheduling @EnableWebSocketMessageBroker public class WebsocketApplication { private Logger logger = LoggerFactory.getLogger(WebsocketApplication. class ); public static void main(String[] args) { SpringApplication.run(WebsocketApplication. class , args); } @Autowired private SimpMessagingTemplate messagingTemplate; int info= 1 ; @Scheduled (fixedRate = 1000 ) public void outputLogger(){ logger.info( "测试日志输出" +info++); } /** * 推送日志到/topic/pullLogger */ @PostConstruct public void pushLogger(){ ExecutorService executorService=Executors.newFixedThreadPool( 2 ); Runnable runnable= new Runnable() { @Override public void run() { while ( true ) { try { LoggerMessage log = LoggerQueue.getInstance().poll(); if (log!= null ){ if (messagingTemplate!= null ) messagingTemplate.convertAndSend( "/topic/pullLogger" ,log); } } catch (Exception e) { e.printStackTrace(); } } } }; executorService.submit(runnable); executorService.submit(runnable); } } |
七.html页面,连接stomp服务端,订阅/topic/pullLogger的消息,展示日志信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
<!DOCTYPE html> < html > < head > < meta charset = "utf-8" > < title >WebSocket Logger</ title > < script src = "https://cdn.bootcss.com/jquery/2.1.4/jquery.js" ></ script > < script src = "https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js" ></ script > < script src = "https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js" ></ script > </ head > < body > < button onclick = "openSocket()" >开启日志</ button >< button onclick = "closeSocket()" >关闭日志</ button > < div id = "log-container" style = "height: 450px; overflow-y: scroll; background: #333; color: #aaa; padding: 10px;" > < div ></ div > </ div > </ body > < script > var stompClient = null; $(document).ready(function() {openSocket();}); function openSocket() { if(stompClient==null){ var socket = new SockJS('http://localhost:8084/websocket?token=kl'); stompClient = Stomp.over(socket); stompClient.connect({token:"kl"}, function(frame) { stompClient.subscribe('/topic/pullLogger', function(event) { var content=JSON.parse(event.body); $("#log-container div").append(content.timestamp +" "+ content.level+" --- ["+ content.threadName+"] "+ content.className+" :"+content.body).append("< br />"); $("#log-container").scrollTop($("#log-container div").height() - $("#log-container").height()); },{ token:"kltoen" }); }); } } function closeSocket() { if (stompClient != null) { stompClient.disconnect(); stompClient=null; } } </ script > </ body > </ html > |
参考地址:
stomp.js客户端:http://jmesnil.net/stomp-websocket/doc/
scok.js客户端:https://github.com/sockjs/sockjs-client
spring webSocket:https://docs.spring.io/spring/docs/
以上就是spring boot集成WebSocket到web页面实时输出的详细内容,更多关于spring boot集成WebSocket实时输出到web的资料请关注服务器之家其它相关文章!
原文链接:http://www.kailing.pub/article/index/arcid/164.html