15 Commits

Author SHA1 Message Date
4b8b90af04 Merge remote-tracking branch 'origin/develop' 2025-07-26 00:43:18 +08:00
690e9288b8 v2.1.3 更新:
1、使用ServletContextListener方式获取实时日志
    2、页面日志要素添加进程id和线程名称,优化样式
    3、日志打印级别调整为INFO
2025-07-26 00:42:38 +08:00
2d93f70704 Merge remote-tracking branch 'origin/develop' 2025-07-25 17:09:34 +08:00
9f74a467ba v2.1.2 更新:
1、升级metona-mq-mini-pro到2.0.1
2025-07-25 17:08:56 +08:00
4b36e35ace v2.1.2 更新:
1、升级metona-mq-mini-pro到2.0.1
2025-07-25 17:08:31 +08:00
40f07bdd6e Merge remote-tracking branch 'origin/develop' 2025-07-25 14:35:11 +08:00
71ca306336 v2.1.1 更新:
1、升级metona-mq-mini-pro到2.0.0,重构实时日志获取方式
2025-07-25 14:34:42 +08:00
5274830cc6 v2.1.1 更新:
1、升级metona-mq-mini-pro到2.0.0,重构实时日志获取方式
2025-07-25 14:32:21 +08:00
3b6c8bedf6 Merge remote-tracking branch 'origin/develop'
# Conflicts:
#	README.md
2025-07-25 09:30:36 +08:00
b37da657fa 修改版本记录和描述 2025-07-25 09:29:34 +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
e17e31edfd v2.1.0 更新:
1、引入metona-mq-mini-pro消息队列,重构实时日志获取方式
2025-07-23 23:26:56 +08:00
bb99e48275 v2.1.0 更新:
1、引入metona-mq-mini-pro消息队列,重构实时日志获取方式
2025-07-23 18:27:16 +08:00
7d8cda234e v2.0.1 更新:
1、常规BUG修复,参数名称修改
2025-07-19 16:43:22 +08:00
18 changed files with 319 additions and 180 deletions

View File

@@ -1,31 +1,77 @@
# fmt # fmt
#### 介绍 ## 介绍
1. 简易文件传输管理系统
2. 文件下载、文件上传、在线日志
#### 软件架构 `fmt` 是一个基于 Spring Boot 的文件管理工具,旨在提供便捷的文件上传、下载、打包、删除等操作。它结合了简洁的前端界面与强大的后端功能,适用于需要进行文件管理的各类应用场景。
1. Jdk 21
2. Maven 3.6.3
3. SpringBoot 3.5.3
4. SpringBoot Starter Thymeleaf 3.5.3
5. SpringBoot Starter WebSocket 3.5.3
7. Hutool 5.8.25
7. AXUI 2.1.1
## 软件架构
#### 安装教程 本项目采用前后端一体化架构,后端基于 Spring Boot 框架,使用 Java 语言开发,前端采用 HTML、CSS 和 JavaScript 技术,结合 `axui` 框架进行界面渲染。主要模块包括:
1. 项目导入IDEA配置Jdk、Maven根据自己实际环境修改配置文件然后启动项目 - **文件上传模块**:支持文件上传并保存至服务器。
2. 下载发行版本压缩包解压后修改bin目录下的启动文件配置然后执行即可 - **文件下载模块**:支持文件下载及 ZIP 打包下载。
- **文件管理模块**:支持文件删除、批量删除等操作。
- **日志模块**:实时推送日志信息。
- **WebSocket 模块**:用于前后端实时通信。
- **工具类模块**:包括 MD5 工具、操作系统工具等。
#### 使用说明 ## 安装教程
1. 启动项目访问http://127.0.0.1:8098/fmt 1. **克隆项目**
```bash
git clone https://gitee.com/thzxx/fmt.git
```
#### 参与贡献 2. **构建项目**
使用 Maven 构建项目:
```bash
cd fmt
mvn clean package
```
1. Fork 本仓库 3. **运行项目**
2. 新建 Feat_xxx 分支 启动 Spring Boot 应用:
3. 提交代码 ```bash
4. 新建 Pull Request java -jar target/fmt.jar
```
4. **访问项目**
打开浏览器访问:
```
http://localhost:8080
```
## 使用说明
- **上传文件**
访问 `/upload/index` 页面,选择文件进行上传。
- **下载文件**
访问 `/download/index` 页面,点击文件进行下载,或使用 `/download/file` 接口下载指定路径文件。
- **打包下载**
使用 `/download/packZip` 接口,传入文件名列表进行 ZIP 打包下载。
- **删除文件**
使用 `/download/delete` 或 `/download/batchDel` 接口进行单个或批量删除。
- **查看日志**
访问 `/logging/index` 页面,实时查看日志信息。
- **WebSocket 实时通信**
前端通过 WebSocket 连接 `/ws`,接收服务器推送的消息。
## 参与贡献
欢迎贡献代码和改进项目。请遵循以下步骤:
1. Fork 项目。
2. 创建新分支。
3. 提交代码。
4. 创建 Pull Request。
如发现 Bug 或有改进建议,请提交 Issue 或直接联系项目维护者。
## 许可证
本项目采用 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.0.0</version> <version>2.1.3</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>
@@ -51,6 +51,12 @@
<artifactId>metona-cache-spring-boot-starter</artifactId> <artifactId>metona-cache-spring-boot-starter</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
</dependency> </dependency>
<dependency>
<groupId>cn.metona</groupId>
<artifactId>metona-mq-mini-pro</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -1,11 +1,15 @@
package cn.somkit.fmt; package cn.somkit.fmt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @SpringBootApplication
public class FmtApplication { public class FmtApplication {
private static final Logger logger = LoggerFactory.getLogger(FmtApplication.class);
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(FmtApplication.class, args); SpringApplication.run(FmtApplication.class, args);
} }

View File

@@ -1,6 +1,9 @@
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;
@@ -31,6 +34,8 @@ 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;
@@ -41,6 +46,7 @@ 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();
@@ -59,6 +65,7 @@ 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);
@@ -66,7 +73,7 @@ public class DownloadAction {
return mv; return mv;
} }
private static Map<String, Object> getObjectMap(File file) { private 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();
@@ -77,6 +84,7 @@ 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;
} }
@@ -102,6 +110,7 @@ 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 {
// 尝试在流关闭后删除文件 // 尝试在流关闭后删除文件
@@ -111,7 +120,7 @@ public class DownloadAction {
} }
} catch (IOException e) { } catch (IOException e) {
// 记录日志或采取其他措施 // 记录日志或采取其他措施
System.err.println("删除文件失败: " + e.getMessage()); logger.error("删除文件失败{}", e.getMessage());
} }
} }
}; };

View File

@@ -1,28 +1,15 @@
package cn.somkit.fmt.action; package cn.somkit.fmt.action;
import cn.metona.cache.Cache;
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 {
@Autowired
private Cache<String, Object> 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", closed);
}
} }

View File

@@ -2,6 +2,8 @@ package cn.somkit.fmt.action;
import cn.somkit.fmt.utils.OsInfoUtil; import cn.somkit.fmt.utils.OsInfoUtil;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
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.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -18,6 +20,8 @@ import java.util.*;
@RequestMapping("/upload") @RequestMapping("/upload")
public class UploadAction { public class UploadAction {
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;
@@ -40,7 +44,7 @@ public class UploadAction {
try { try {
this.saveFile(file); // 保存上传信息 this.saveFile(file); // 保存上传信息
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.error("文件保存失败:{}", e.getMessage());
} }
} }
} }
@@ -56,6 +60,7 @@ public class UploadAction {
* @throws Exception 上传异常 * @throws Exception 上传异常
*/ */
public void saveFile(MultipartFile file) throws Exception { public void saveFile(MultipartFile file) throws Exception {
try {
String path = OsInfoUtil.isWindows() ? windows_path : String path = OsInfoUtil.isWindows() ? windows_path :
OsInfoUtil.isLinux() ? linux_path : null; OsInfoUtil.isLinux() ? linux_path : null;
assert path != null; assert path != null;
@@ -64,6 +69,10 @@ public class UploadAction {
filePath = path + File.separator + file.getOriginalFilename(); filePath = path + File.separator + file.getOriginalFilename();
File saveFile = new File(filePath) ; File saveFile = new File(filePath) ;
file.transferTo(saveFile); // 文件保存 file.transferTo(saveFile); // 文件保存
logger.info("文件保存成功:{}", filePath);
}
} catch (Exception e) {
throw new RuntimeException(e);
} }
} }
} }

View File

@@ -0,0 +1,35 @@
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

@@ -0,0 +1,50 @@
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

@@ -0,0 +1,25 @@
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

@@ -1,18 +1,20 @@
package cn.somkit.fmt.entity; package cn.somkit.fmt.entity;
import java.time.LocalDateTime;
public class LoggerMessage { public class LoggerMessage {
String level; String level;
String loggerName; String threadName;
String name;
String message; String message;
String timestamp; String timestamp;
long number;
public LoggerMessage(String level, String loggerName, String message, String timestamp) { public LoggerMessage(String level, String threadName, String loggerName, String message, String timestamp, long number) {
this.level = level; this.level = level;
this.loggerName = loggerName; this.threadName = threadName;
this.name = loggerName;
this.message = message; this.message = message;
this.timestamp = timestamp; this.timestamp = timestamp;
this.number = number;
} }
public String getLevel() { public String getLevel() {
@@ -23,12 +25,20 @@ public class LoggerMessage {
this.level = level; this.level = level;
} }
public String getLoggerName() { public String getThreadName() {
return loggerName; return threadName;
} }
public void setLoggerName(String loggerName) { public void setThreadName(String threadName) {
this.loggerName = loggerName; this.threadName = threadName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} }
public String getMessage() { public String getMessage() {
@@ -46,4 +56,12 @@ 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

@@ -1,44 +0,0 @@
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.somkit.fmt.entity.LoggerMessage;
import cn.somkit.fmt.utils.LoggerQueue;
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")
);
LoggerQueue.getInstance().push(msg); // 单例阻塞队列
return FilterReply.NEUTRAL;
}
public void setLevel(String level) {
this.level = Level.toLevel(level);
}
public void start() {
if (this.level != null) {
super.start();
}
}
}

View File

@@ -0,0 +1,31 @@
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,53 +1,29 @@
package cn.somkit.fmt.socket; 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.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, Object> cache;
@Override @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { public void afterConnectionEstablished(WebSocketSession session) throws Exception {
push(session); SocketManage.put(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 @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

@@ -1,28 +0,0 @@
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="cn.somkit.fmt.filter.LogStashFilter"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level> <level>INFO</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="DEBUG"> <root level="INFO">
<appender-ref ref="CONSOLE"/> <appender-ref ref="CONSOLE"/>
</root> </root>
</configuration> </configuration>

View File

@@ -26,7 +26,7 @@ class LogMonitorAdaptive {
timeFormat: 'HH:mm:ss.SSS', // 时间戳格式,可自定义 timeFormat: 'HH:mm:ss.SSS', // 时间戳格式,可自定义
/* --- 日志级别 & 过滤 --- */ /* --- 日志级别 & 过滤 --- */
levels: ['debug', 'info', 'warn', 'error', 'success', 'system'], // 可用日志级别 levels: ['DEBUG', 'INFO', 'WARN', 'ERROR', 'SUCCESS', 'SYSTEM'], // 可用日志级别
// 过滤功能基于 levels如只想显示 info/warn可在这里删减 // 过滤功能基于 levels如只想显示 info/warn可在这里删减
/* --- 功能开关 --- */ /* --- 功能开关 --- */
@@ -51,7 +51,7 @@ class LogMonitorAdaptive {
/* 强制确保 levels 是数组,防止 forEach 报错 */ /* 强制确保 levels 是数组,防止 forEach 报错 */
if (!Array.isArray(this.cfg.levels)) { if (!Array.isArray(this.cfg.levels)) {
this.cfg.levels = ['info', 'warn', 'error']; this.cfg.levels = ['INFO', 'WARN', 'ERROR'];
} }
this.isPaused = false; this.isPaused = false;
@@ -309,7 +309,7 @@ class LogMonitorAdaptive {
debug: '#9c27b0', info: '#2196f3', warn: '#ff9800', debug: '#9c27b0', info: '#2196f3', warn: '#ff9800',
error: '#f44336', success: '#4caf50', system: '#00bcd4' error: '#f44336', success: '#4caf50', system: '#00bcd4'
}; };
lvlSpan.style.color = colors[entry.level] || colors.info; lvlSpan.style.color = colors[entry.level.toLowerCase()] || colors.info;
lvlSpan.style.minWidth = '70px'; lvlSpan.style.minWidth = '70px';
lvlSpan.style.fontWeight = 'bold'; lvlSpan.style.fontWeight = 'bold';
line.appendChild(lvlSpan); line.appendChild(lvlSpan);

View File

@@ -31,14 +31,8 @@
showLevel: true, // 是否显示日志级别标签 showLevel: true, // 是否显示日志级别标签
wordWrap: true, // 日志内容是否自动换行true=换行false=横向滚动) wordWrap: true, // 日志内容是否自动换行true=换行false=横向滚动)
//暂停/继续 回调函数 //暂停/继续 回调函数
onTogglePause: async (isPaused) => { onTogglePause: (isPaused) => {
const options = {
url: Fmt.ctx() + '/logging/close',
data: {closed: isPaused},
method: 'post'
};
await Fmt.axios(options).then((result) => console.log(result)).catch((err) => console.error(err));
ws.send('发送日志');
}, },
onCreated: () => { onCreated: () => {
console.log('日志容器已创建'); console.log('日志容器已创建');
@@ -56,7 +50,8 @@
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);
logger.log(data.loggerName + ' : ' + data.message, data.level, data.timestamp); let message = `${data.number} --- [ ${data.threadName} ] <span style="color: #198cff;">${data.name}</span> : ${data.message}`;
logger.log(message, data.level, data.timestamp);
} }
} }

View File

@@ -27,3 +27,23 @@
重构在线日志页面及实现方式,不再使用读取日志文件方式,自定义日志拦截器实时获取日志 重构在线日志页面及实现方式,不再使用读取日志文件方式,自定义日志拦截器实时获取日志
不再生成自定义日志文件日志打印从INFO改为DEBUG打印更详细的内容 不再生成自定义日志文件日志打印从INFO改为DEBUG打印更详细的内容
``` ```
> v2.0.1
常规BUG修复参数名称修改
> v2.1.0
```
引入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
```