Springboot + Websocket + Sockjs + Stomp + Vue + Iview 实现java后端日志显示在前端web页面上

话不多说,看代码。

一、pom.xml 引入spring boot websocket依赖

1 <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-websocket</artifactId>
4 </dependency>

二、WebSocketConfig 配置WebSocket消息代理端点,即stomp服务端

 1 @Slf4j
 2 @Configuration
 3 @EnableWebSocketMessageBroker // 注解开启STOMP协议来传输基于代理的消息,此时控制器支持使用
 4 @MessageMapping
 5 public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
 6 {
 7     @Autowired
 8     private SimpMessagingTemplate messagingTemplate;
 9 
10     // 测试
11     // int info = 1;
12     //
13     // @Scheduled(fixedRate = 4000)
14     // public void outputLogger()
15     // {
16     // log.info("测试日志输出" + info++);
17     // }
18 
19     @Override
20     public void registerStompEndpoints(StompEndpointRegistry registry)
21     {
22         // 将clientMessage注册为STOMP的一个端点
23         // 客户端在订阅或发布消息到目的路径前,要连接该端点
24         // setAllowedOrigins允许所有域连接,否则浏览器可能报403错误
25         registry.addEndpoint("/websocket").setAllowedOrigins("*").addInterceptors().withSockJS(); //
26     }
27 
28     
29 
30     /**
31      * 推送日志到/topic/pullLogger
32      */
33     @PostConstruct
34     public void pushLogger()
35     {
36         ExecutorService executorService = Executors.newFixedThreadPool(4);
37         Runnable fileLog = new Runnable()
38         {
39             @Override
40             public void run()
41             {
42                 while (true)
43                 {
44                     try
45                     {
46                         String log = LoggerQueue.getInstance().poll().toString();
47                         if (log != null)
48                         {
49                             if (messagingTemplate != null)
50                                 messagingTemplate.convertAndSend("/topic/pullFileLogger", log);
51                         }
52                     }
53                     catch (Exception e)
54                     {
55                         e.printStackTrace();
56                     }
57                 }
58             }
59         };
60         executorService.submit(fileLog);
61         executorService.submit(fileLog);
62     }
63 }

三、新增日志消息实体

 1 /*
 2  * 日志消息实体类,这里用了lombok插件
 3  * 没有安装该插件的话,就手动添加get、set方法、toString方法
 4  */
 5 @Getter
 6 @Setter
 7 @ToString
 8 @AllArgsConstructor
 9 public class LoggerMessage
10 {
11     private String body;
12     private String timestamp;
13     private String threadName;
14     private String className;
15     private String level;
16     private String exception;
17     private String cause;
18 }

四、创建一个阻塞队列,作为日志系统输出的日志的一个临时载体

 

 1 /*
 2  * 创建一个阻塞队列,作为日志系统输出的日志的一个临时载体
 3  */
 4 public class LoggerQueue
 5 {
 6     // 队列大小
 7     public static final int QUEUE_MAX_SIZE = 10000;
 8     private static LoggerQueue alarmMessageQueue = new LoggerQueue();
 9     // 阻塞队列
10     private BlockingQueue blockingQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);
11 
12     private LoggerQueue()
13     {
14     }
15 
16     public static LoggerQueue getInstance()
17     {
18         return alarmMessageQueue;
19     }
20 
21     /**
22      * 消息入队
23      *
24      * @param log
25      * @return
26      */
27     public boolean push(LoggerMessage log)
28     {
29         return this.blockingQueue.add(log);// 队列满了就抛出异常,不阻塞
30     }
31 
32     /**
33      * 消息出队
34      *
35      * @return
36      */
37     public LoggerMessage poll()
38     {
39         LoggerMessage result = null;
40         try
41         {
42             result = (LoggerMessage) this.blockingQueue.take();
43         }
44         catch (InterruptedException e)
45         {
46             e.printStackTrace();
47         }
48         return result;
49     }
50 }

 

五、获取logback的日志,塞入日志队列中

  1.定义Logfilter拦截输出日志

 

 1 @Service
 2 public class LogFilter extends Filter<ILoggingEvent>
 3 {
 4     @Override
 5     public FilterReply decide(ILoggingEvent event)
 6     {
 7         String exception = "";
 8         IThrowableProxy iThrowableProxy1 = event.getThrowableProxy();
 9         if (iThrowableProxy1 != null)
10         {
11             exception = "<span class='excehtext'>" + iThrowableProxy1.getClassName() + " " + iThrowableProxy1.getMessage() + "</span></br>";
12             for (int i = 0; i < iThrowableProxy1.getStackTraceElementProxyArray().length; i++)
13             {
14                 exception += "<span class='excetext'>" + iThrowableProxy1.getStackTraceElementProxyArray()[i].toString() + "</span></br>";
15             }
16         }
17         LoggerMessage loggerMessage = new LoggerMessage(event.getMessage(), DateFormat.getDateTimeInstance().format(new Date(event.getTimeStamp())), event.getThreadName(), event.getLoggerName(),
18                 event.getLevel().levelStr, exception, "");
19         LoggerQueue.getInstance().push(loggerMessage);
20         return FilterReply.ACCEPT;
21     }
22 }

 

  2.配置logback-spring.xml,添加我们自定义的filter

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
 3 <!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
 4 <!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
 5                  当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
 6 <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
 7 <configuration  scan="true" scanPeriod="10 seconds">
 8     <contextName>logback</contextName>
 9      <include resource="org/springframework/boot/logging/logback/defaults.xml" />
10      <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
11     <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
12     <property name="log.path" value="/var/log/" />
13  
14     <!--0. 日志格式和颜色渲染 -->
15     <!-- 彩色日志依赖的渲染类 -->
16     <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
17     <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
18     <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
19     <!-- 彩色日志格式 -->
20     <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
21  
22     <!--1. 输出到控制台-->
23     <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
24         <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
25         <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
26             <level>info</level>
27         </filter> -->
28         <encoder>
29             <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
30             <!-- 设置字符集 -->
31             <charset>UTF-8</charset>
32         </encoder>
33         <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
34         <filter class="com.hmkj.ddos.config.LogFilter">
35         </filter>
36     </appender>
37  
38     <!-- 2.2 level为 INFO 日志,时间滚动输出  -->
39     <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
40         <!-- 正在记录的日志文档的路径及文档名 -->
41         <file>${log.path}/hmddos.log</file>
42         <!--日志文档输出格式-->
43         <encoder>
44             <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
45             <charset>UTF-8</charset>
46         </encoder>
47         <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
48         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
49             <!-- 每天日志归档路径以及格式 -->
50             <fileNamePattern>${log.path}/web-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
51             <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
52                 <maxFileSize>10MB</maxFileSize>
53             </timeBasedFileNamingAndTriggeringPolicy>
54             <!--日志文档保留天数-->
55             <maxHistory>15</maxHistory>
56         </rollingPolicy>
57         <!-- 此日志文档只记录info级别的 -->
58         <filter class="ch.qos.logback.classic.filter.LevelFilter">
59             <level>info</level>
60             <onMatch>ACCEPT</onMatch>
61             <onMismatch>DENY</onMismatch>
62         </filter>
63     </appender>
64  
65     <root level="info">
66         <appender-ref ref="CONSOLE" />
67         <appender-ref ref="INFO_FILE" />
68     </root>
69 </configuration>

六、Vue中添加基于webSocket通信的库

  先安装 sockjs-client 和 stompjs

  注意: 在安装stompjs出现一个问题,net 模块不存在,则应该在项目根目录再执行npm install net 即可。

1 npm install sockjs-client
2 npm install stompjs

  .vue页面

 1 <template>
 2   <div>
 3     <Card dis-hover>
 4       <p slot="title" style="padding-bottom: 20px; padding-top: 5px;">
 5             监听程序日志
 6       </p>
 7               <Button type="primary" @click="openSocket">开启日志</Button>&nbsp;&nbsp;
 8               <Button type="error" @click="closeSocket">关闭日志</Button><br><br>
 9               <div id="filelog-container" style="height: 400px; overflow-y: scroll; background: #333; color: #aaa; padding: 10px;">
10                 <div id="aa">{{pullFileLogger}}</div>
11               </div>
12       </Card>
13     </div>
14 </template>
15 
16 <script>
17 // 安装并引入相关模块
18 import SockJS from 'sockjs-client';
19 import Stomp from 'stompjs';
20 export default {
21     data(){
22         return {
23             pullLogger: '',
24             pullFileLogger: '正在连接websocket,请稍后~',
25             stompClient: null
26         }
27     },
28     methods:{
29         openSocket () {
30             if (this.stompClient === null) {
31                 console.log('打开websocket连接')
32                 this.pullFileLogger = '正在连接websocket,请稍后~'
33                 let _that = this
34                 var socket = new SockJS('http://localhost:7788/websocket?token=kl');
35                 _that.stompClient = Stomp.over(socket);
36                 _that.stompClient.connect({token:"kl"}, function(frame) {
37                     _that.stompClient.subscribe('/topic/pullFileLogger', function(event) {
38                         _that.pullFileLogger = ''
39                             var content = event.body
40                             var aa = document.getElementById('aa')
41                             var p = document.createElement('p')
42                             p.style.wordWrap = 'break-word'
43                             p.style.fontSize = 18 + 'px'
44                             p.appendChild(document.createTextNode(event.body))
45                             aa.appendChild(p)
46                             var div1 = document.getElementById('filelog-container')
47                             div1.scrollTop = div1.scrollHeight
48                     }, {
49                             token:"kltoen"
50                     })
51                 })
52             }
53         },
54         closeSocket () {
55             if (this.stompClient != null) {
56                 this.stompClient.disconnect()
57                 this.stompClient = null
58                 console.log("关闭websocket连接")
59                 this.pullFileLogger = 'websocket连接已关闭'
60             }
61         }
62     },
63     //销毁页面之前,断开连接
64     beforeDestroy: function () {
65         //页面离开时断开连接,清除定时器
66         this.closeSocket()
67     },
68     mounted(){
69         //调用初始化websocket方法
70         this.openSocket()
71     }
72 }
73 </script>
74 <style>
75 </style>

 七、效果图,字体颜色背景颜色可以自行修改样式。

 

有什么不对的地方欢迎大家评论指点。

posted @ 2019-05-07 10:22  我今生何求惟你  阅读(2762)  评论(1编辑  收藏  举报