代码提交

This commit is contained in:
2025-07-18 11:18:45 +08:00
parent 515fb83408
commit 3334aa23d9
9 changed files with 176 additions and 25 deletions

View File

@@ -2,9 +2,48 @@ package cn.somkit.fmt.entity;
import java.time.LocalDateTime;
public record LoggerMessage(
String level,
String loggerName,
String message,
LocalDateTime timestamp
) {}
public class LoggerMessage {
String level;
String loggerName;
String message;
String timestamp;
public LoggerMessage(String level, String loggerName, String message, String timestamp) {
this.level = level;
this.loggerName = loggerName;
this.message = message;
this.timestamp = timestamp;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getLoggerName() {
return loggerName;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}

View File

@@ -4,6 +4,7 @@ import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
import cn.hutool.core.date.DateUtil;
import cn.somkit.fmt.entity.LoggerMessage;
import cn.somkit.fmt.utils.LoggerQueue;
@@ -19,18 +20,15 @@ public class LogStashFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent e) {
if (!this.isStarted()) {
return FilterReply.NEUTRAL;
} else {
LoggerMessage msg = new LoggerMessage(
e.getLevel().toString(),
e.getLoggerName(),
e.getFormattedMessage(),
Instant.ofEpochMilli(e.getTimeStamp()).atZone(ZoneId.systemDefault()).toLocalDateTime()
);
LoggerQueue.getInstance().push(msg); // 单例阻塞队列
return e.getLevel().isGreaterOrEqual(this.level) ? FilterReply.NEUTRAL : FilterReply.DENY;
}
LoggerMessage msg = new LoggerMessage(
e.getLevel().toString(),
e.getLoggerName(),
e.getFormattedMessage(),
DateUtil.format(Instant.ofEpochMilli(e.getTimeStamp()).atZone(ZoneId.systemDefault()).toLocalDateTime(),
"yyyy-MM-dd HH:mm:ss.SSS")
);
LoggerQueue.getInstance().push(msg); // 单例阻塞队列
return FilterReply.NEUTRAL;
}
public void setLevel(String level) {

View File

@@ -0,0 +1,23 @@
package cn.somkit.fmt.socket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketAutoConfig implements WebSocketConfigurer {
@Bean
public WebSocketServerHandler webSocketServerHandler() {
return new WebSocketServerHandler();
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketServerHandler(), "/socket/ws")//设置连接路径和处理
.setAllowedOrigins("*");//允许跨域访问
}
}

View File

@@ -0,0 +1,57 @@
package cn.somkit.fmt.socket;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.metona.cache.Cache;
import cn.somkit.fmt.entity.LoggerMessage;
import cn.somkit.fmt.utils.LoggerQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import java.io.IOException;
@Component
public class WebSocketServerHandler implements WebSocketHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Cache<String, Object> cache;
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
push(session);
}
private void push(WebSocketSession session) throws IOException {
while (StrUtil.isBlankIfStr(cache.get("closed")) || !Boolean.parseBoolean(String.valueOf(cache.get("closed")))) {
LoggerMessage log = LoggerQueue.getInstance().poll();
if(log != null){
session.sendMessage(new TextMessage(JSONUtil.toJsonStr(log)));
}
}
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
push(session);
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}

View File

@@ -6,7 +6,7 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
public class AppContext implements ApplicationContextAware {
private static ApplicationContext context;

View File

@@ -7,7 +7,7 @@ import java.util.concurrent.LinkedBlockingQueue;
public final class LoggerQueue {
private static final LoggerQueue INSTANCE = new LoggerQueue();
private final BlockingQueue<LoggerMessage> queue = new LinkedBlockingQueue<>();
private final BlockingQueue<LoggerMessage> queue = new LinkedBlockingQueue<>(100000);
public static LoggerQueue getInstance() {
return INSTANCE;

View File

@@ -6,7 +6,7 @@
<!-- 日志输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="cn.somkit.fmt.filter.LogStashFilter">
<level>INFO</level>
<level>DEBUG</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
@@ -15,7 +15,7 @@
</appender>
<!-- 指定日志输出级别以及启动的Appender -->
<root level="INFO">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@@ -19,9 +19,12 @@
<script th:src="@{/common/js/LogMonitorAdaptive.js}" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" th:inline="javascript" charset="utf-8">
window.onload = () => {
let ws = null;
const logger = new LogMonitorAdaptive('#logContainer', {
theme: 'dark',
maxLines: 5000,
maxLines: 10000,
fontSize: 14,
enableFilter: true,
enableSearch: true,
@@ -34,19 +37,44 @@
enableWordWrap: true,
showTimestamp: true, // 是否显示时间戳
showLevel: true, // 是否显示日志级别标签
wordWrap: true, // 日志内容是否自动换行true=换行false=横向滚动)
//暂停/继续 回调函数
onTogglePause: (isPaused) => {
onTogglePause: async (isPaused) => {
const options = {
url: Fmt.ctx() + '/logging/close',
data: {closed: isPaused},
method: 'post'
};
Fmt.axios(options).then((result) => console.log(result)).catch((err) => console.error(err));
await Fmt.axios(options).then((result) => console.log(result)).catch((err) => console.error(err));
ws.send('发送日志');
},
onCreated: () => {
console.log('日志容器已创建');
}
});
try {
if ('WebSocket' in window) {
ws = new WebSocket((Fmt.ctx() + '/socket/ws').replace("http", "ws").replace("https", "wss"));
} else {
alert('浏览器不支持WebSocket通讯');
return false;
}
ws.onmessage = function (event) {
if(event.data){
let data = JSON.parse(event.data);
logger.log(data.loggerName + ' : ' + data.message, data.level, data.timestamp);
}
}
//监听窗口关闭事件当窗口关闭时主动去关闭websocket连接防止连接还没断开就关闭窗口server端会抛异常。
window.onbeforeunload = ()=> {
ws.close();
};
} catch (e) {
alert('WebSocket:链接失败');
}
}
</script>
</body>