package utils import ( "fmt" "io" "os" "sync" "time" ) // LogEntry 日志条目 type LogEntry struct { Time time.Time `json:"time"` Level string `json:"level"` Message string `json:"message"` } // LogBuffer 环形日志缓冲区 type LogBuffer struct { mu sync.RWMutex entries []LogEntry maxSize int writer io.Writer // 原始输出(控制台) } var LogBuf = NewLogBuffer(2000) func NewLogBuffer(maxSize int) *LogBuffer { return &LogBuffer{ entries: make([]LogEntry, 0, maxSize), maxSize: maxSize, writer: os.Stderr, } } // Write 实现 io.Writer 接口, 用于 log.SetOutput func (b *LogBuffer) Write(p []byte) (n int, err error) { // 写入原始输出 b.writer.Write(p) msg := string(p) level := "INFO" switch { case contains(msg, "FAIL") || contains(msg, "Error") || contains(msg, "error"): level = "ERROR" case contains(msg, "WARN") || contains(msg, "warn"): level = "WARN" } b.mu.Lock() b.entries = append(b.entries, LogEntry{ Time: time.Now(), Level: level, Message: msg, }) // 超过上限则截掉前半部分 if len(b.entries) > b.maxSize { b.entries = b.entries[len(b.entries)-b.maxSize:] } b.mu.Unlock() return len(p), nil } // Get 获取最近 n 条日志 func (b *LogBuffer) Get(n int) []LogEntry { b.mu.RLock() defer b.mu.RUnlock() total := len(b.entries) if n <= 0 || n > total { n = total } result := make([]LogEntry, n) copy(result, b.entries[total-n:]) return result } // Count 日志总数 func (b *LogBuffer) Count() int { b.mu.RLock() defer b.mu.RUnlock() return len(b.entries) } func contains(s, sub string) bool { return len(s) >= len(sub) && (s == sub || len(s) > 0 && findSubstring(s, sub)) } func findSubstring(s, sub string) bool { for i := 0; i <= len(s)-len(sub); i++ { if s[i:i+len(sub)] == sub { return true } } return false } // Logf 写入一条格式化日志到缓冲区 func Logf(format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) LogBuf.Write([]byte(msg + "\n")) }