3 Commits

Author SHA1 Message Date
3b6c8bedf6 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	README.md
2025-07-25 09:30:36 +08:00
1931697a87 !1 Update README.md
Merge pull request !1 from gitee-agent/N/A
2025-07-24 15:39:24 +00:00
gitee-bot
b882e85134 Update README.md 2025-07-24 15:35:16 +00:00
16 changed files with 257 additions and 276 deletions

152
README.md
View File

@@ -1,77 +1,77 @@
# fmt # fmt
## 介绍 ## 介绍
`fmt` 是一个基于 Spring Boot 的文件管理工具,旨在提供便捷的文件上传、下载、打包、删除等操作。它结合了简洁的前端界面与强大的后端功能,适用于需要进行文件管理的各类应用场景。 `fmt` 是一个基于 Spring Boot 的文件管理工具,旨在提供便捷的文件上传、下载、打包、删除等操作。它结合了简洁的前端界面与强大的后端功能,适用于需要进行文件管理的各类应用场景。
## 软件架构 ## 软件架构
本项目采用前后端一体化架构,后端基于 Spring Boot 框架,使用 Java 语言开发,前端采用 HTML、CSS 和 JavaScript 技术,结合 `axui` 框架进行界面渲染。主要模块包括: 本项目采用前后端一体化架构,后端基于 Spring Boot 框架,使用 Java 语言开发,前端采用 HTML、CSS 和 JavaScript 技术,结合 `axui` 框架进行界面渲染。主要模块包括:
- **文件上传模块**:支持文件上传并保存至服务器。 - **文件上传模块**:支持文件上传并保存至服务器。
- **文件下载模块**:支持文件下载及 ZIP 打包下载。 - **文件下载模块**:支持文件下载及 ZIP 打包下载。
- **文件管理模块**:支持文件删除、批量删除等操作。 - **文件管理模块**:支持文件删除、批量删除等操作。
- **日志模块**:实时推送日志信息。 - **日志模块**:实时推送日志信息。
- **WebSocket 模块**:用于前后端实时通信。 - **WebSocket 模块**:用于前后端实时通信。
- **工具类模块**:包括 MD5 工具、操作系统工具等。 - **工具类模块**:包括 MD5 工具、操作系统工具等。
## 安装教程 ## 安装教程
1. **克隆项目** 1. **克隆项目**
```bash ```bash
git clone https://gitee.com/thzxx/fmt.git git clone https://gitee.com/thzxx/fmt.git
``` ```
2. **构建项目** 2. **构建项目**
使用 Maven 构建项目: 使用 Maven 构建项目:
```bash ```bash
cd fmt cd fmt
mvn clean package mvn clean package
``` ```
3. **运行项目** 3. **运行项目**
启动 Spring Boot 应用: 启动 Spring Boot 应用:
```bash ```bash
java -jar target/fmt.jar java -jar target/fmt.jar
``` ```
4. **访问项目** 4. **访问项目**
打开浏览器访问: 打开浏览器访问:
``` ```
http://localhost:8080 http://localhost:8080
``` ```
## 使用说明 ## 使用说明
- **上传文件** - **上传文件**
访问 `/upload/index` 页面,选择文件进行上传。 访问 `/upload/index` 页面,选择文件进行上传。
- **下载文件** - **下载文件**
访问 `/download/index` 页面,点击文件进行下载,或使用 `/download/file` 接口下载指定路径文件。 访问 `/download/index` 页面,点击文件进行下载,或使用 `/download/file` 接口下载指定路径文件。
- **打包下载** - **打包下载**
使用 `/download/packZip` 接口,传入文件名列表进行 ZIP 打包下载。 使用 `/download/packZip` 接口,传入文件名列表进行 ZIP 打包下载。
- **删除文件** - **删除文件**
使用 `/download/delete` 或 `/download/batchDel` 接口进行单个或批量删除。 使用 `/download/delete` 或 `/download/batchDel` 接口进行单个或批量删除。
- **查看日志** - **查看日志**
访问 `/logging/index` 页面,实时查看日志信息。 访问 `/logging/index` 页面,实时查看日志信息。
- **WebSocket 实时通信** - **WebSocket 实时通信**
前端通过 WebSocket 连接 `/ws`,接收服务器推送的消息。 前端通过 WebSocket 连接 `/ws`,接收服务器推送的消息。
## 参与贡献 ## 参与贡献
欢迎贡献代码和改进项目。请遵循以下步骤: 欢迎贡献代码和改进项目。请遵循以下步骤:
1. Fork 项目。 1. Fork 项目。
2. 创建新分支。 2. 创建新分支。
3. 提交代码。 3. 提交代码。
4. 创建 Pull Request。 4. 创建 Pull Request。
如发现 Bug 或有改进建议,请提交 Issue 或直接联系项目维护者。 如发现 Bug 或有改进建议,请提交 Issue 或直接联系项目维护者。
## 许可证 ## 许可证
本项目采用 MIT 许可证。详情请查看项目根目录下的 `LICENSE` 文件。 本项目采用 MIT 许可证。详情请查看项目根目录下的 `LICENSE` 文件。

View File

@@ -10,7 +10,7 @@
</parent> </parent>
<groupId>cn.somkit</groupId> <groupId>cn.somkit</groupId>
<artifactId>fmt</artifactId> <artifactId>fmt</artifactId>
<version>2.1.3</version> <version>2.1.0</version>
<name>fmt</name> <name>fmt</name>
<description>File Manage System for by SpringBoot</description> <description>File Manage System for by SpringBoot</description>
<properties> <properties>
@@ -55,7 +55,7 @@
<dependency> <dependency>
<groupId>cn.metona</groupId> <groupId>cn.metona</groupId>
<artifactId>metona-mq-mini-pro</artifactId> <artifactId>metona-mq-mini-pro</artifactId>
<version>2.0.1</version> <version>1.0.2</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -1,5 +1,6 @@
package cn.somkit.fmt; package cn.somkit.fmt;
import cn.metona.mq.util.MetonaMQUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
@@ -12,6 +13,11 @@ public class FmtApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(FmtApplication.class, args); SpringApplication.run(FmtApplication.class, args);
if(!MetonaMQUtil.isInitialized()){
logger.info("Metona MQ Mini Pro 初始化...");
MetonaMQUtil.init();
}
} }
} }

View File

@@ -1,9 +1,6 @@
package cn.somkit.fmt.action; package cn.somkit.fmt.action;
import cn.hutool.json.JSONUtil;
import cn.somkit.fmt.utils.OsInfoUtil; import cn.somkit.fmt.utils.OsInfoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -34,8 +31,6 @@ import java.util.zip.ZipOutputStream;
@RequestMapping("/download") @RequestMapping("/download")
public class DownloadAction { public class DownloadAction {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${somkit.upload.path.windows}") @Value("${somkit.upload.path.windows}")
private String windows_path; private String windows_path;
@@ -46,7 +41,6 @@ public class DownloadAction {
public ModelAndView index(String keyboard) throws Exception{ public ModelAndView index(String keyboard) throws Exception{
String path = OsInfoUtil.isWindows() ? windows_path : String path = OsInfoUtil.isWindows() ? windows_path :
OsInfoUtil.isLinux() ? linux_path : null; OsInfoUtil.isLinux() ? linux_path : null;
logger.info("获取文件存储路径:{}", path);
assert path != null; assert path != null;
File folder = new File(path); File folder = new File(path);
File[] listOfFiles = folder.listFiles(); File[] listOfFiles = folder.listFiles();
@@ -65,7 +59,6 @@ public class DownloadAction {
list = list.stream().filter(map -> String.valueOf(map.get("filename")) list = list.stream().filter(map -> String.valueOf(map.get("filename"))
.contains(keyboard)).collect(Collectors.toList()); .contains(keyboard)).collect(Collectors.toList());
} }
logger.info("关键词:{},文件数量:{}", keyboard, list.size());
ModelAndView mv = new ModelAndView(); ModelAndView mv = new ModelAndView();
mv.setViewName("download"); mv.setViewName("download");
mv.addObject("files", list); mv.addObject("files", list);
@@ -73,7 +66,7 @@ public class DownloadAction {
return mv; return mv;
} }
private Map<String, Object> getObjectMap(File file) { private static Map<String, Object> getObjectMap(File file) {
BigDecimal filesize = BigDecimal.valueOf(file.length()) BigDecimal filesize = BigDecimal.valueOf(file.length())
.divide(BigDecimal.valueOf(1024 * 1024), 2, RoundingMode.HALF_UP); .divide(BigDecimal.valueOf(1024 * 1024), 2, RoundingMode.HALF_UP);
Long time = file.lastModified(); Long time = file.lastModified();
@@ -84,7 +77,6 @@ public class DownloadAction {
map.put("filepath", file.getAbsolutePath()); map.put("filepath", file.getAbsolutePath());
map.put("filesize", filesize); map.put("filesize", filesize);
map.put("filetime", filetime); map.put("filetime", filetime);
logger.info("文件信息:{}", JSONUtil.toJsonStr(map));
return map; return map;
} }
@@ -110,7 +102,6 @@ public class DownloadAction {
} }
outputStream.flush(); outputStream.flush();
} catch (IOException e) { } catch (IOException e) {
logger.error("IO错误", e);
throw new RuntimeException("IO错误: " + e.getMessage()); throw new RuntimeException("IO错误: " + e.getMessage());
} finally { } finally {
// 尝试在流关闭后删除文件 // 尝试在流关闭后删除文件
@@ -120,7 +111,7 @@ public class DownloadAction {
} }
} catch (IOException e) { } catch (IOException e) {
// 记录日志或采取其他措施 // 记录日志或采取其他措施
logger.error("删除文件失败{}", e.getMessage()); System.err.println("删除文件失败: " + e.getMessage());
} }
} }
}; };

View File

@@ -1,15 +1,38 @@
package cn.somkit.fmt.action; package cn.somkit.fmt.action;
import cn.metona.cache.Cache;
import cn.metona.mq.util.MetonaMQUtil;
import cn.somkit.fmt.socket.WebSocketServerHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller @Controller
@RequestMapping("/logging") @RequestMapping("/logging")
public class LoggingAction { public class LoggingAction {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Cache<String, String> cache;
@GetMapping("/index") @GetMapping("/index")
public String index() throws Exception{ public String index() throws Exception{
return "logging"; return "logging";
} }
@ResponseBody
@PostMapping("/close")
public void close(Boolean closed) throws Exception {
cache.put("closed", String.valueOf(closed));
logger.info("Metona MQ Mini Pro 停止消费者(顺序消息)...");
if(closed){
MetonaMQUtil.stopOrderedConsuming();
}
}
} }

View File

@@ -1,35 +0,0 @@
package cn.somkit.fmt.config;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import cn.metona.mq.MQToolkit;
import cn.metona.mq.core.MessageQueue;
import cn.somkit.fmt.entity.LoggerMessage;
import java.time.Instant;
import java.time.ZoneId;
public class LogMonitorAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
private static final String Queue_Name = "log-monitor";
@Override
protected void append(ILoggingEvent event) {
LoggerMessage msg = new LoggerMessage(
event.getLevel().toString(),
event.getThreadName(),
event.getLoggerName(),
event.getFormattedMessage(),
DateUtil.format(Instant.ofEpochMilli(event.getTimeStamp()).atZone(ZoneId.systemDefault()).toLocalDateTime(),
"yyyy-MM-dd HH:mm:ss.SSS"),
ProcessHandle.current().pid()
);
//发送日志信息到日志监控队列
MessageQueue.QueueStats queueStats = MQToolkit.getQueueStats(Queue_Name);
if(queueStats != null && queueStats.isRunning() && !queueStats.isPaused()){
MQToolkit.sendMessage(Queue_Name, "log.monitor", JSONUtil.toJsonStr(msg));
}
}
}

View File

@@ -1,50 +0,0 @@
package cn.somkit.fmt.config;
import cn.metona.mq.MQToolkit;
import cn.metona.mq.consumer.MessageConsumer;
import cn.metona.mq.utils.MonitorUtils;
import cn.somkit.fmt.socket.SocketManage;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.List;
@Component
public class LogMonitorConfig {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final String Queue_Name = "log-monitor";
private static final String Consumer_Id = "log-monitor-consumer";
@PostConstruct
public void run(){
//创建日志监控队列
MQToolkit.createQueue(Queue_Name, Boolean.FALSE);
//创建日志监控消费者
MessageConsumer consumer = MQToolkit.createConsumer(Consumer_Id, Queue_Name, message -> {
List<WebSocketSession> list = SocketManage.all();
if(list != null && !list.isEmpty()){
list.forEach(session -> {
if(session.isOpen()){
try {
session.sendMessage(new TextMessage(message.body()));
} catch (IOException e) {
logger.error("发送消息失败:", e);
}
}
});
}
});
//启动消费者
consumer.start();
// 显示队列统计
MonitorUtils.printQueueStats(Queue_Name);
}
}

View File

@@ -1,25 +0,0 @@
package cn.somkit.fmt.config;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class LogbackConfigListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// 配置logback
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
// 添加自定义appender
LogMonitorAppender appender = new LogMonitorAppender();
appender.setContext(loggerContext);
appender.start();
rootLogger.addAppender(appender);
}
}

View File

@@ -2,19 +2,15 @@ package cn.somkit.fmt.entity;
public class LoggerMessage { public class LoggerMessage {
String level; String level;
String threadName;
String name; String name;
String message; String message;
String timestamp; String timestamp;
long number;
public LoggerMessage(String level, String threadName, String loggerName, String message, String timestamp, long number) { public LoggerMessage(String level, String loggerName, String message, String timestamp) {
this.level = level; this.level = level;
this.threadName = threadName;
this.name = loggerName; this.name = loggerName;
this.message = message; this.message = message;
this.timestamp = timestamp; this.timestamp = timestamp;
this.number = number;
} }
public String getLevel() { public String getLevel() {
@@ -25,14 +21,6 @@ public class LoggerMessage {
this.level = level; this.level = level;
} }
public String getThreadName() {
return threadName;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
public String getName() { public String getName() {
return name; return name;
} }
@@ -56,12 +44,4 @@ public class LoggerMessage {
public void setTimestamp(String timestamp) { public void setTimestamp(String timestamp) {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public long getNumber() {
return number;
}
public void setNumber(long number) {
this.number = number;
}
} }

View File

@@ -0,0 +1,52 @@
package cn.somkit.fmt.filter;
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.hutool.json.JSONUtil;
import cn.metona.mq.exception.MessageSendException;
import cn.metona.mq.util.MetonaMQUtil;
import cn.somkit.fmt.entity.LoggerMessage;
import java.time.Instant;
import java.time.ZoneId;
public class LogStashFilter extends Filter<ILoggingEvent> {
Level level;
public LogStashFilter() {
}
@Override
public FilterReply decide(ILoggingEvent e) {
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")
);
try {
if(MetonaMQUtil.isInitialized() && MetonaMQUtil.isOrderedConsumerRunning()){
MetonaMQUtil.send("log-topic", "log-monitor", JSONUtil.toJsonStr(msg));
}
} catch (MessageSendException ex) {
System.out.println("发送消息队列失败");
}
return FilterReply.NEUTRAL;
}
public void setLevel(String level) {
this.level = Level.toLevel(level);
}
public void start() {
if (this.level != null) {
super.start();
}
}
}

View File

@@ -1,31 +0,0 @@
package cn.somkit.fmt.socket;
import org.springframework.web.socket.WebSocketSession;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SocketManage {
private SocketManage() {
}
private static final Map<String, WebSocketSession> onlineMap = new ConcurrentHashMap<>();
public static void put(WebSocketSession session){
onlineMap.put(session.getId(), session);
}
public static WebSocketSession get(String sessionId){
return onlineMap.get(sessionId);
}
public static void remove(String sessionId){
onlineMap.remove(sessionId);
}
public static List<WebSocketSession> all(){
return onlineMap.values().stream().toList();
}
}

View File

@@ -1,29 +1,70 @@
package cn.somkit.fmt.socket; package cn.somkit.fmt.socket;
import cn.hutool.core.util.StrUtil;
import cn.metona.cache.Cache;
import cn.metona.mq.consumer.MessageListener;
import cn.metona.mq.core.Message;
import cn.metona.mq.util.MetonaMQUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.socket.*; import org.springframework.web.socket.*;
import java.io.IOException;
@Component @Component
public class WebSocketServerHandler implements WebSocketHandler { public class WebSocketServerHandler implements WebSocketHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Cache<String, String> cache;
@Override @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { public void afterConnectionEstablished(WebSocketSession session) throws Exception {
SocketManage.put(session); push(session);
}
private void push(WebSocketSession session) throws IOException {
boolean closed = StrUtil.isNotBlank(cache.get("closed")) && Boolean.parseBoolean(cache.get("closed"));
if(!closed){
try {
if(MetonaMQUtil.isInitialized() && !MetonaMQUtil.isOrderedConsumerRunning()){
logger.info("Metona MQ Mini Pro 订阅主题log-topic顺序消息...");
MetonaMQUtil.subscribeOrdered("log-topic", new MessageListener() {
@Override
public void onMessage(Message message) {
try {
session.sendMessage(new TextMessage(message.getBody()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
logger.info("Metona MQ Mini Pro 启动消费者(顺序消息)...");
MetonaMQUtil.startOrderedConsuming();
}
} catch (Exception e) {
logger.error("Metona MQ Mini Pro 异常", e);
}
}
} }
@Override @Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
push(session);
} }
@Override @Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
SocketManage.remove(session.getId());
} }
@Override @Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
SocketManage.remove(session.getId());
} }
@Override @Override

View File

@@ -0,0 +1,28 @@
package cn.somkit.fmt.utils;
import cn.somkit.fmt.entity.LoggerMessage;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public final class LoggerQueue {
private static final LoggerQueue INSTANCE = new LoggerQueue();
private final BlockingQueue<LoggerMessage> queue = new LinkedBlockingQueue<>(100000);
public static LoggerQueue getInstance() {
return INSTANCE;
}
public void push(LoggerMessage msg) {
queue.offer(msg); // 非阻塞插入
}
public LoggerMessage poll() {
try {
return queue.take(); // 阻塞直到有数据
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
}

View File

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

View File

@@ -14,6 +14,18 @@
let ws = null; let ws = null;
let logMonitor = async (closed = false) => {
const options = {
url: Fmt.ctx() + '/logging/close',
data: {closed: closed},
method: 'post'
};
await Fmt.axios(options).then(() => {}).catch((err) => console.error(err));
if(ws){
ws.send('发送日志');
}
}
const logger = new LogMonitorAdaptive('#logContainer', { const logger = new LogMonitorAdaptive('#logContainer', {
theme: 'dark', theme: 'dark',
maxLines: 10000, maxLines: 10000,
@@ -31,8 +43,8 @@
showLevel: true, // 是否显示日志级别标签 showLevel: true, // 是否显示日志级别标签
wordWrap: true, // 日志内容是否自动换行true=换行false=横向滚动) wordWrap: true, // 日志内容是否自动换行true=换行false=横向滚动)
//暂停/继续 回调函数 //暂停/继续 回调函数
onTogglePause: (isPaused) => { onTogglePause: async (isPaused) => {
await logMonitor(isPaused);
}, },
onCreated: () => { onCreated: () => {
console.log('日志容器已创建'); console.log('日志容器已创建');
@@ -47,11 +59,14 @@
return false; return false;
} }
ws.onopen = function () {
}
ws.onmessage = function (event) { ws.onmessage = function (event) {
if(event.data){ if(event.data){
let data = JSON.parse(event.data); let data = JSON.parse(event.data);
let message = `${data.number} --- [ ${data.threadName} ] <span style="color: #198cff;">${data.name}</span> : ${data.message}`; logger.log(data.name + ' : ' + data.message, data.level, data.timestamp);
logger.log(message, data.level, data.timestamp);
} }
} }

View File

@@ -32,18 +32,4 @@
> v2.1.0 > v2.1.0
``` ```
引入metona-mq-mini-pro消息队列重构实时日志获取方式 引入metona-mq-mini-pro消息队列重构实时日志获取方式
```
> v2.1.1
```
升级metona-mq-mini-pro到2.0.0,重构实时日志获取方式
```
> v2.1.2
```
升级metona-mq-mini-pro到2.0.1
```
> v2.1.3
```
使用ServletContextListener方式获取实时日志
页面日志要素添加进程id和线程名称优化样式
日志打印级别调整为INFO
``` ```