From dd3f70c32404e01d2db621150457d8c466400f23 Mon Sep 17 00:00:00 2001 From: lifangliang Date: Thu, 31 Jul 2025 14:24:36 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/config/LogbackConfig.java | 32 ++++++++ .../main/controller/LogController.java | 74 +++++++++++++++++++ .../main/utils/MemoryLogAppender.java | 61 +++++++++++++++ .../main/utils/MemoryLogStorage.java | 57 ++++++++++++++ 4 files changed, 224 insertions(+) create mode 100644 src/main/java/com/goeing/printserver/main/config/LogbackConfig.java create mode 100644 src/main/java/com/goeing/printserver/main/controller/LogController.java create mode 100644 src/main/java/com/goeing/printserver/main/utils/MemoryLogAppender.java create mode 100644 src/main/java/com/goeing/printserver/main/utils/MemoryLogStorage.java diff --git a/src/main/java/com/goeing/printserver/main/config/LogbackConfig.java b/src/main/java/com/goeing/printserver/main/config/LogbackConfig.java new file mode 100644 index 0000000..0eac398 --- /dev/null +++ b/src/main/java/com/goeing/printserver/main/config/LogbackConfig.java @@ -0,0 +1,32 @@ +// LogbackConfig.java +package com.goeing.printserver.main.config; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.Logger; +import com.goeing.printserver.main.utils.MemoryLogAppender; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import jakarta.annotation.PostConstruct; + +@Configuration +public class LogbackConfig { + + @Autowired + private MemoryLogAppender memoryLogAppender; + + @PostConstruct + public void registerAppender() { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + +// // 添加 appender 到 root logger + Logger rootLogger = context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(memoryLogAppender); + memoryLogAppender.start(); + + // 可选:也加到你的包 logger +// Logger appLogger = context.getLogger("com.goeing.printserver"); +// appLogger.addAppender(memoryLogAppender); + } +} \ No newline at end of file diff --git a/src/main/java/com/goeing/printserver/main/controller/LogController.java b/src/main/java/com/goeing/printserver/main/controller/LogController.java new file mode 100644 index 0000000..d964240 --- /dev/null +++ b/src/main/java/com/goeing/printserver/main/controller/LogController.java @@ -0,0 +1,74 @@ +package com.goeing.printserver.main.controller; + +import com.goeing.printserver.main.utils.MemoryLogAppender; +import com.goeing.printserver.main.utils.MemoryLogStorage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 日志控制器,提供日志相关的 API 接口 + */ +@RestController +@RequestMapping("/api/logs") +@Slf4j +public class LogController { + + @Autowired + private MemoryLogStorage memoryLogStorage; + + /** + * 获取系统日志 + * @param limit 限制数量,默认100 + * @param level 日志级别过滤,默认ALL + * @return 日志列表 + */ + @GetMapping + public Map getLogs( + @RequestParam(defaultValue = "100") int limit, + @RequestParam(defaultValue = "ALL") String level) { + + Map result = new HashMap<>(); + + try { + List logs = memoryLogStorage.getLogs(limit, level); + result.put("success", true); + result.put("logs", logs); + result.put("total", logs.size()); + } catch (Exception e) { + log.error("获取日志失败", e); + result.put("success", false); + result.put("message", "获取日志失败: " + e.getMessage()); + result.put("logs", List.of()); + result.put("total", 0); + } + + return result; + } + + /** + * 清空系统日志 + * @return 操作结果 + */ + @DeleteMapping + public Map clearLogs() { + Map result = new HashMap<>(); + + try { + memoryLogStorage.clearLogs(); + result.put("success", true); + result.put("message", "日志已清空"); + log.info("系统日志已被清空"); + } catch (Exception e) { + log.error("清空日志失败", e); + result.put("success", false); + result.put("message", "清空日志失败: " + e.getMessage()); + } + + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/goeing/printserver/main/utils/MemoryLogAppender.java b/src/main/java/com/goeing/printserver/main/utils/MemoryLogAppender.java new file mode 100644 index 0000000..c1e44ef --- /dev/null +++ b/src/main/java/com/goeing/printserver/main/utils/MemoryLogAppender.java @@ -0,0 +1,61 @@ +package com.goeing.printserver.main.utils; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import cn.hutool.extra.spring.SpringUtil; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 内存日志追加器,用于缓存日志到内存中 + */ +@Component +public class MemoryLogAppender extends AppenderBase { + @Autowired + private MemoryLogStorage memoryLogStorage; // ✅ Spring 注入,全局唯一 + + private static final DateTimeFormatter FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + @Override + public void start() { + System.out.println("✅ MemoryLogAppender started with context: " + context); + super.start(); // 必须调用 + } + @Override + protected void append(ILoggingEvent event) { + if (!isStarted()) return; + + LogEntry logEntry = new LogEntry( + LocalDateTime.now().format(FORMATTER), + event.getLevel().toString(), + event.getLoggerName(), + event.getFormattedMessage() + ); + memoryLogStorage.addLog(logEntry); // 写入共享存储 + } + + /** + * 日志条目类 + */ + @Getter + public static class LogEntry { + private final String timestamp; + private final String level; + private final String logger; + private final String message; + + public LogEntry(String timestamp, String level, String logger, String message) { + this.timestamp = timestamp; + this.level = level; + this.logger = logger; + this.message = message; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/goeing/printserver/main/utils/MemoryLogStorage.java b/src/main/java/com/goeing/printserver/main/utils/MemoryLogStorage.java new file mode 100644 index 0000000..dd76e71 --- /dev/null +++ b/src/main/java/com/goeing/printserver/main/utils/MemoryLogStorage.java @@ -0,0 +1,57 @@ +package com.goeing.printserver.main.utils; + +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; + +@Component +public class MemoryLogStorage { + + private final Queue logs = new ConcurrentLinkedQueue<>(); + private static final int MAX_LOGS = 2000; + + /** + * 添加日志条目,并控制最大数量 + */ + public void addLog(MemoryLogAppender.LogEntry logEntry) { + logs.offer(logEntry); + // 保持最多 MAX_LOGS 条 + while (logs.size() > MAX_LOGS) { + logs.poll(); + } + } + + /** + * 获取日志列表(支持过滤和分页) + */ + public List getLogs(int limit, String level) { + List result = new ArrayList<>(); + for (MemoryLogAppender.LogEntry log : logs) { + if (level == null || "ALL".equals(level) || level.equals(log.getLevel())) { + result.add(log); + } + } + // 最新的在前 + result.sort((a, b) -> b.getTimestamp().compareTo(a.getTimestamp())); + // 限制数量 + if (limit > 0 && result.size() > limit) { + return result.subList(0, limit); + } + return result; + } + + /** + * 清空所有日志 + */ + public void clearLogs() { + logs.clear(); + } + + /** + * 获取当前日志数量(用于监控) + */ + public int size() { + return logs.size(); + } +} \ No newline at end of file