diff --git a/pom.xml b/pom.xml index 31841aa..88f1555 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ cn.somkit fmt - 2.0.0 + 2.1.0 fmt File Manage System for by SpringBoot @@ -51,6 +51,12 @@ metona-cache-spring-boot-starter 1.0.0 + + + cn.metona + metona-mq-mini-pro + 1.0.2 + diff --git a/src/main/java/cn/somkit/fmt/FmtApplication.java b/src/main/java/cn/somkit/fmt/FmtApplication.java index bb94f44..513204a 100644 --- a/src/main/java/cn/somkit/fmt/FmtApplication.java +++ b/src/main/java/cn/somkit/fmt/FmtApplication.java @@ -1,13 +1,23 @@ package cn.somkit.fmt; +import cn.metona.mq.util.MetonaMQUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class FmtApplication { + private static final Logger logger = LoggerFactory.getLogger(FmtApplication.class); + public static void main(String[] args) { SpringApplication.run(FmtApplication.class, args); + + if(!MetonaMQUtil.isInitialized()){ + logger.info("Metona MQ Mini Pro 初始化..."); + MetonaMQUtil.init(); + } } } diff --git a/src/main/java/cn/somkit/fmt/action/LoggingAction.java b/src/main/java/cn/somkit/fmt/action/LoggingAction.java index 68d173f..f1e2a6f 100644 --- a/src/main/java/cn/somkit/fmt/action/LoggingAction.java +++ b/src/main/java/cn/somkit/fmt/action/LoggingAction.java @@ -1,6 +1,10 @@ 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.web.bind.annotation.GetMapping; @@ -12,8 +16,10 @@ import org.springframework.web.bind.annotation.ResponseBody; @RequestMapping("/logging") public class LoggingAction { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + @Autowired - private Cache cache; + private Cache cache; @GetMapping("/index") public String index() throws Exception{ @@ -23,6 +29,10 @@ public class LoggingAction { @ResponseBody @PostMapping("/close") public void close(Boolean closed) throws Exception { - cache.put("closed", closed); + cache.put("closed", String.valueOf(closed)); + logger.info("Metona MQ Mini Pro 停止消费者(顺序消息)..."); + if(closed){ + MetonaMQUtil.stopOrderedConsuming(); + } } } diff --git a/src/main/java/cn/somkit/fmt/action/UploadAction.java b/src/main/java/cn/somkit/fmt/action/UploadAction.java index 2014d39..96b5a78 100644 --- a/src/main/java/cn/somkit/fmt/action/UploadAction.java +++ b/src/main/java/cn/somkit/fmt/action/UploadAction.java @@ -2,6 +2,8 @@ package cn.somkit.fmt.action; import cn.somkit.fmt.utils.OsInfoUtil; import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @@ -18,6 +20,8 @@ import java.util.*; @RequestMapping("/upload") public class UploadAction { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + @Value("${somkit.upload.path.windows}") private String windows_path; @@ -40,7 +44,7 @@ public class UploadAction { try { this.saveFile(file); // 保存上传信息 } catch (Exception e) { - e.printStackTrace(); + logger.error("文件保存失败:{}", e.getMessage()); } } } @@ -56,14 +60,19 @@ public class UploadAction { * @throws Exception 上传异常 */ public void saveFile(MultipartFile file) throws Exception { - String path = OsInfoUtil.isWindows() ? windows_path : - OsInfoUtil.isLinux() ? linux_path : null; - assert path != null; - String filePath = ""; - if (file != null && file.getSize() > 0) { // 有文件上传 - filePath = path + File.separator + file.getOriginalFilename(); - File saveFile = new File(filePath) ; - file.transferTo(saveFile); // 文件保存 + try { + String path = OsInfoUtil.isWindows() ? windows_path : + OsInfoUtil.isLinux() ? linux_path : null; + assert path != null; + String filePath = ""; + if (file != null && file.getSize() > 0) { // 有文件上传 + filePath = path + File.separator + file.getOriginalFilename(); + File saveFile = new File(filePath) ; + file.transferTo(saveFile); // 文件保存 + logger.info("文件保存成功:{}", filePath); + } + } catch (Exception e) { + throw new RuntimeException(e); } } } diff --git a/src/main/java/cn/somkit/fmt/entity/LoggerMessage.java b/src/main/java/cn/somkit/fmt/entity/LoggerMessage.java index a25c154..e58551d 100644 --- a/src/main/java/cn/somkit/fmt/entity/LoggerMessage.java +++ b/src/main/java/cn/somkit/fmt/entity/LoggerMessage.java @@ -1,16 +1,14 @@ package cn.somkit.fmt.entity; -import java.time.LocalDateTime; - public class LoggerMessage { String level; - String loggerName; + String name; String message; String timestamp; public LoggerMessage(String level, String loggerName, String message, String timestamp) { this.level = level; - this.loggerName = loggerName; + this.name = loggerName; this.message = message; this.timestamp = timestamp; } @@ -23,12 +21,12 @@ public class LoggerMessage { this.level = level; } - public String getLoggerName() { - return loggerName; + public String getName() { + return name; } - public void setLoggerName(String loggerName) { - this.loggerName = loggerName; + public void setName(String name) { + this.name = name; } public String getMessage() { diff --git a/src/main/java/cn/somkit/fmt/filter/LogStashFilter.java b/src/main/java/cn/somkit/fmt/filter/LogStashFilter.java index 2c28cac..87fe46d 100644 --- a/src/main/java/cn/somkit/fmt/filter/LogStashFilter.java +++ b/src/main/java/cn/somkit/fmt/filter/LogStashFilter.java @@ -5,8 +5,10 @@ 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 cn.somkit.fmt.utils.LoggerQueue; import java.time.Instant; import java.time.ZoneId; @@ -27,7 +29,13 @@ public class LogStashFilter extends Filter { DateUtil.format(Instant.ofEpochMilli(e.getTimeStamp()).atZone(ZoneId.systemDefault()).toLocalDateTime(), "yyyy-MM-dd HH:mm:ss.SSS") ); - LoggerQueue.getInstance().push(msg); // 单例阻塞队列 + try { + if(MetonaMQUtil.isInitialized() && MetonaMQUtil.isOrderedConsumerRunning()){ + MetonaMQUtil.send("log-topic", "log-monitor", JSONUtil.toJsonStr(msg)); + } + } catch (MessageSendException ex) { + System.out.println("发送消息队列失败"); + } return FilterReply.NEUTRAL; } diff --git a/src/main/java/cn/somkit/fmt/socket/WebSocketServerHandler.java b/src/main/java/cn/somkit/fmt/socket/WebSocketServerHandler.java index 15d399c..23446d4 100644 --- a/src/main/java/cn/somkit/fmt/socket/WebSocketServerHandler.java +++ b/src/main/java/cn/somkit/fmt/socket/WebSocketServerHandler.java @@ -1,10 +1,10 @@ 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 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; @@ -19,7 +19,7 @@ public class WebSocketServerHandler implements WebSocketHandler { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired - private Cache cache; + private Cache cache; @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { @@ -27,10 +27,27 @@ public class WebSocketServerHandler implements WebSocketHandler { } 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))); + 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); } } } diff --git a/src/main/resources/static/common/js/LogMonitorAdaptive.js b/src/main/resources/static/common/js/LogMonitorAdaptive.js index b6f279a..34025a5 100644 --- a/src/main/resources/static/common/js/LogMonitorAdaptive.js +++ b/src/main/resources/static/common/js/LogMonitorAdaptive.js @@ -26,7 +26,7 @@ class LogMonitorAdaptive { timeFormat: 'HH:mm:ss.SSS', // 时间戳格式,可自定义 /* --- 日志级别 & 过滤 --- */ - levels: ['debug', 'info', 'warn', 'error', 'success', 'system'], // 可用日志级别 + levels: ['DEBUG', 'INFO', 'WARN', 'ERROR', 'SUCCESS', 'SYSTEM'], // 可用日志级别 // 过滤功能基于 levels;如只想显示 info/warn,可在这里删减 /* --- 功能开关 --- */ @@ -51,7 +51,7 @@ class LogMonitorAdaptive { /* 强制确保 levels 是数组,防止 forEach 报错 */ if (!Array.isArray(this.cfg.levels)) { - this.cfg.levels = ['info', 'warn', 'error']; + this.cfg.levels = ['INFO', 'WARN', 'ERROR']; } this.isPaused = false; @@ -309,7 +309,7 @@ class LogMonitorAdaptive { debug: '#9c27b0', info: '#2196f3', warn: '#ff9800', 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.fontWeight = 'bold'; line.appendChild(lvlSpan); diff --git a/src/main/resources/templates/logging.html b/src/main/resources/templates/logging.html index 3a31b6c..d3c0224 100644 --- a/src/main/resources/templates/logging.html +++ b/src/main/resources/templates/logging.html @@ -14,6 +14,18 @@ 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', { theme: 'dark', maxLines: 10000, @@ -32,13 +44,7 @@ wordWrap: true, // 日志内容是否自动换行(true=换行,false=横向滚动) //暂停/继续 回调函数 onTogglePause: async (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('发送日志'); + await logMonitor(isPaused); }, onCreated: () => { console.log('日志容器已创建'); @@ -53,10 +59,14 @@ return false; } + ws.onopen = function () { + + } + ws.onmessage = function (event) { if(event.data){ let data = JSON.parse(event.data); - logger.log(data.loggerName + ' : ' + data.message, data.level, data.timestamp); + logger.log(data.name + ' : ' + data.message, data.level, data.timestamp); } } diff --git a/版本记录/readme.md b/版本记录/readme.md index c18ccec..1fbf09e 100644 --- a/版本记录/readme.md +++ b/版本记录/readme.md @@ -26,4 +26,10 @@ 引入metona-cache-spring-boot-starter,使用此缓存方案 重构在线日志页面及实现方式,不再使用读取日志文件方式,自定义日志拦截器实时获取日志 不再生成自定义日志文件,日志打印从INFO改为DEBUG,打印更详细的内容 +``` +> v2.0.1 + 常规BUG修复,参数名称修改 +> v2.1.0 +``` + 引入metona-mq-mini-pro消息队列,重构实时日志获取方式 ``` \ No newline at end of file