支持服务端日志传入前端

pull/1682/head
648540858 2024-10-31 17:37:34 +08:00
parent 376f14733e
commit effb705f99
7 changed files with 244 additions and 2 deletions

18
pom.xml
View File

@ -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>

View File

@ -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);
}
});
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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
web_src/src/components/log.vue Executable file
View File

@ -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>

View File

@ -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,
},
]
},
{