瀏覽代碼

支持服务端日志传入前端

648540858 1 年之前
父節點
當前提交
effb705f99

+ 18 - 0
pom.xml

@@ -107,6 +107,12 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-configuration-processor</artifactId>
@@ -353,6 +359,18 @@
             <version>1.18.30</version>
             <scope>provided</scope>
         </dependency>
+        <!--LogViewer-->
+<!--        <dependency>-->
+<!--            <groupId>io.github.sevdokimov.logviewer</groupId>-->
+<!--            <artifactId>log-generator</artifactId>-->
+<!--            <version>1.0.10</version>-->
+<!--        </dependency>-->
+
+        <dependency>
+            <groupId>io.github.sevdokimov.logviewer</groupId>
+            <artifactId>log-viewer-spring-boot</artifactId>
+            <version>1.0.10</version>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>

+ 66 - 0
src/main/java/com/genersoft/iot/vmp/conf/webLog/LogChannel.java

@@ -0,0 +1,66 @@
+package com.genersoft.iot.vmp.conf.webLog;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.websocket.*;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+@ServerEndpoint(value = "/channel/log")
+@Slf4j
+public class LogChannel {
+
+    public static final ConcurrentMap<String, LogChannel> CHANNELS = new ConcurrentHashMap<>();
+
+    private Session session;
+
+    @OnMessage(maxMessageSize = 1) // MaxMessage 1 byte
+    public void onMessage(String message) {
+
+        log.debug("Recv Message: {}", message);
+
+        try {
+            this.session.close(new CloseReason(CloseReason.CloseCodes.TOO_BIG, "此节点不接收任何客户端信息"));
+        } catch (IOException e) {
+            log.error("[Web-Log] 连接关闭失败: id={}, err={}", this.session.getId(), e.getMessage());
+        }
+    }
+
+    @OnOpen
+    public void onOpen(Session session, EndpointConfig endpointConfig) {
+        this.session = session;
+        this.session.setMaxIdleTimeout(0);
+        CHANNELS.put(this.session.getId(), this);
+
+        log.info("[Web-Log] 连接已建立: id={}", this.session.getId());
+    }
+
+    @OnClose
+    public void onClose(CloseReason closeReason) {
+
+        log.info("[Web-Log] 连接已断开: id={}, err={}", this.session.getId(), closeReason);
+
+        CHANNELS.remove(this.session.getId());
+    }
+
+    @OnError
+    public void onError(Throwable throwable) throws IOException {
+        log.info("[Web-Log] 连接错误: id={}, err= ", this.session.getId(), throwable);
+        this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
+    }
+
+    /**
+     * Push messages to all clients
+     *
+     * @param message
+     */
+    public static void push(String message) {
+        CHANNELS.values().stream().forEach(endpoint -> {
+            if (endpoint.session.isOpen()) {
+                endpoint.session.getAsyncRemote().sendText(message);
+            }
+        });
+    }
+}

+ 22 - 0
src/main/java/com/genersoft/iot/vmp/conf/webLog/WebSocketAppender.java

@@ -0,0 +1,22 @@
+package com.genersoft.iot.vmp.conf.webLog;
+
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+import com.genersoft.iot.vmp.utils.DateUtil;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WebSocketAppender  extends AppenderBase<ILoggingEvent> {
+
+    private PatternLayoutEncoder encoder;
+
+    @Override
+    protected void append(ILoggingEvent loggingEvent) {
+        byte[] data = this.encoder.encode(loggingEvent);
+        // Push to client.
+        LogChannel.push(DateUtil.timestampMsTo_yyyy_MM_dd_HH_mm_ss(loggingEvent.getTimeStamp()) + " " +  loggingEvent.getFormattedMessage());
+    }
+}

+ 19 - 0
src/main/java/com/genersoft/iot/vmp/conf/websocket/WebSocketConfig.java

@@ -0,0 +1,19 @@
+package com.genersoft.iot.vmp.conf.websocket;
+
+import com.genersoft.iot.vmp.conf.webLog.LogChannel;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+@Configuration
+public class WebSocketConfig {
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter(){
+        ServerEndpointExporter endpointExporter = new ServerEndpointExporter();
+
+        endpointExporter.setAnnotatedEndpointClasses(LogChannel.class);
+
+        return endpointExporter;
+    }
+}

+ 9 - 0
src/main/resources/logback-spring.xml

@@ -24,6 +24,14 @@
 		</filter>
 	</appender>
 
+	<!-- WebSocket -->
+	<appender name="websocket" class="com.genersoft.iot.vmp.conf.webLog.WebSocketAppender">
+		<encoder>
+			<pattern>${FILE_LOG_PATTERN}</pattern>
+			<charset>UTF-8</charset>
+		</encoder>
+	</appender>
+
 	<!-- 按照每天生成日志文件 DEBUG以上级别的日志,仅用于测试环境,正式环境为info级别以上的日志-->
 	<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
 
@@ -88,6 +96,7 @@
 	<!-- 日志输出级别 -->
 	<root level="INFO">
 		<appender-ref ref="STDOUT" />
+		<appender-ref ref="websocket" />
 	</root>
 
 	<logger name="com.genersoft.iot.vmp" level="info" additivity="true">

+ 104 - 0
web_src/src/components/log.vue

@@ -0,0 +1,104 @@
+<template>
+  <div id="log" style="width: 100%">
+    <el-container v-loading="loading" >
+      <el-aside width="400px" >
+      </el-aside>
+      <el-main style="padding: 5px;">
+      </el-main>
+    </el-container>
+
+  </div>
+</template>
+
+<script>
+// import uiHeader from '../layout/UiHeader.vue'
+
+export default {
+  name: 'log',
+  components: {},
+  data() {
+    return {
+      loading: false,
+    };
+  },
+
+  created() {
+    console.log('created');
+    this.initData();
+  },
+  destroyed() {},
+  methods: {
+    initData: function () {
+      console.log('initData');
+      const websocket = new WebSocket("ws://localhost:18080/channel/log");
+      websocket.onclose = e => {
+        console.log(`conn closed: code=${e.code}, reason=${e.reason}, wasClean=${e.wasClean}`)
+      }
+      websocket.onmessage = e => {
+        console.log(e.data);
+      }
+      websocket.onerror = e => {
+        console.log(`conn err`)
+        console.error(e)
+      }
+      websocket.onopen = e => {
+        console.log(`conn open: ${e}`);
+      }
+    },
+  }
+};
+</script>
+
+<style>
+.videoList {
+  display: flex;
+  flex-wrap: wrap;
+  align-content: flex-start;
+}
+
+.video-item {
+  position: relative;
+  width: 15rem;
+  height: 10rem;
+  margin-right: 1rem;
+  background-color: #000000;
+}
+
+.video-item-img {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  margin: auto;
+  width: 100%;
+  height: 100%;
+}
+
+.video-item-img:after {
+  content: "";
+  display: inline-block;
+  position: absolute;
+  z-index: 2;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  margin: auto;
+  width: 3rem;
+  height: 3rem;
+  background-image: url("../assets/loading.png");
+  background-size: cover;
+  background-color: #000000;
+}
+
+.video-item-title {
+  position: absolute;
+  bottom: 0;
+  color: #000000;
+  background-color: #ffffff;
+  line-height: 1.5rem;
+  padding: 0.3rem;
+  width: 14.4rem;
+}
+</style>

+ 6 - 2
web_src/src/router/index.js

@@ -25,6 +25,7 @@ import wasmPlayer from '../components/common/jessibuca.vue'
 import rtcPlayer from '../components/dialog/rtcPlayer.vue'
 import region from '../components/region.vue'
 import group from '../components/group.vue'
+import log from '../components/log.vue'
 
 const originalPush = VueRouter.prototype.push
 VueRouter.prototype.push = function push(location) {
@@ -142,8 +143,11 @@ export default new VueRouter({
           path: '/channel/group',
           name: 'group',
           component: group,
-        }
-        ,
+        },
+        {
+          path: '/log',
+          component: log,
+        },
         ]
     },
     {