package logger import ( "fmt" "io" "os" "path/filepath" "sync" "time" ) type LogEntry struct { Timestamp string `json:"timestamp"` Level string `json:"level"` Message string `json:"message"` } type RingLogger struct { mu sync.RWMutex entries []LogEntry maxSize int file *os.File multiOut io.Writer } var globalLogger *RingLogger var once sync.Once func Init(logFilePath string, maxSize int) *RingLogger { once.Do(func() { if maxSize <= 0 { maxSize = 500 } dir := filepath.Dir(logFilePath) if dir != "" && dir != "." { _ = os.MkdirAll(dir, 0755) } f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) var out io.Writer = os.Stdout if err == nil { out = io.MultiWriter(os.Stdout, f) } globalLogger = &RingLogger{ entries: make([]LogEntry, 0, maxSize), maxSize: maxSize, file: f, multiOut: out, } }) return globalLogger } func Get() *RingLogger { if globalLogger == nil { return Init("logs/app.log", 500) } return globalLogger } func (l *RingLogger) Log(level, message string) { ts := time.Now().Format("2006-01-02 15:04:05") entry := LogEntry{ Timestamp: ts, Level: level, Message: message, } l.mu.Lock() if len(l.entries) >= l.maxSize { l.entries = l.entries[1:] } l.entries = append(l.entries, entry) l.mu.Unlock() line := fmt.Sprintf("[%s] [%s] %s\n", ts, level, message) if l.multiOut != nil { _, _ = l.multiOut.Write([]byte(line)) } } func (l *RingLogger) GetEntries(limit int, levelFilter string) []LogEntry { l.mu.RLock() defer l.mu.RUnlock() result := make([]LogEntry, 0, len(l.entries)) for i := len(l.entries) - 1; i >= 0; i-- { e := l.entries[i] if levelFilter != "" && levelFilter != "ALL" && e.Level != levelFilter { continue } result = append(result, e) if limit > 0 && len(result) >= limit { break } } return result } func (l *RingLogger) Clear() { l.mu.Lock() defer l.mu.Unlock() l.entries = make([]LogEntry, 0, l.maxSize) } func Info(format string, a ...interface{}) { Get().Log("INFO", fmt.Sprintf(format, a...)) } func Warn(format string, a ...interface{}) { Get().Log("WARN", fmt.Sprintf(format, a...)) } func Error(format string, a ...interface{}) { Get().Log("ERROR", fmt.Sprintf(format, a...)) } func SQL(format string, a ...interface{}) { Get().Log("SQL", fmt.Sprintf(format, a...)) }