package com.xcong.excoin.modules.gateApi; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * Gate 策略日志环形缓冲区,支持 SSE 推送到前端。 * 最多保留 500 条日志,新客户端连接时先推送历史再持续推新日志。 */ @Slf4j @Service public class GateLogBuffer { private static final int MAX_ENTRIES = 500; private static final DateTimeFormatter TF = DateTimeFormatter.ofPattern("HH:mm:ss"); private final List entries = Collections.synchronizedList(new ArrayList<>(MAX_ENTRIES)); private final List emitters = new CopyOnWriteArrayList<>(); /** * 推送一条日志到所有 SSE 客户端。 */ public void push(String level, String message) { String line = String.format("[%s] %-5s %s", TF.format(LocalTime.now()), level, message); synchronized (entries) { if (entries.size() >= MAX_ENTRIES) { entries.remove(0); } entries.add(line); } // 推送给所有 SSE 客户端 for (SseEmitter emitter : emitters) { try { emitter.send(SseEmitter.event().name("log").data(line)); } catch (IOException e) { emitters.remove(emitter); } } } /** info 级别日志 */ public void info(String message) { push("INFO", message); } /** warn 级别日志 */ public void warn(String message) { push("WARN", message); } /** error 级别日志 */ public void error(String message) { push("ERROR", message); } /** * 创建 SSE 连接,先推送历史再持续推送新日志。 */ public SseEmitter subscribe() { SseEmitter emitter = new SseEmitter(0L); // 无超时 emitters.add(emitter); // 推送历史 List history; synchronized (entries) { history = new ArrayList<>(entries); } for (String line : history) { try { emitter.send(SseEmitter.event().name("log").data(line)); } catch (IOException e) { emitters.remove(emitter); return emitter; } } emitter.onCompletion(() -> emitters.remove(emitter)); emitter.onTimeout(() -> emitters.remove(emitter)); emitter.onError(e -> emitters.remove(emitter)); return emitter; } }