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<String> entries = Collections.synchronizedList(new ArrayList<>(MAX_ENTRIES));
|
private final List<SseEmitter> 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<String> 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;
|
}
|
}
|