package logging import ( "sync" "time" ) // Level classifies the severity of a log entry. type Level int const ( LevelInfo Level = iota LevelWarn LevelError ) // Entry is a single log record. type Entry struct { Ts int64 Level Level Source string Message string Details string } // Logger is a thread-safe ring-buffer log with cap 500. type Logger struct { mu sync.Mutex buf []Entry listeners []func(Entry) now func() int64 } const bufCap = 500 // NewLogger creates a Logger with a 500-entry ring buffer. func NewLogger() *Logger { return &Logger{ now: func() int64 { return time.Now().UnixMilli() }, } } func (l *Logger) add(level Level, source, message string, details []string) { var d string if len(details) > 0 { d = details[0] } e := Entry{ Ts: l.now(), Level: level, Source: source, Message: message, Details: d, } l.mu.Lock() l.buf = append(l.buf, e) if len(l.buf) > bufCap { l.buf = l.buf[len(l.buf)-bufCap:] } fns := l.listeners l.mu.Unlock() for _, fn := range fns { fn(e) } } // Info records an info-level entry. func (l *Logger) Info(source, message string, details ...string) { l.add(LevelInfo, source, message, details) } // Warn records a warn-level entry. func (l *Logger) Warn(source, message string, details ...string) { l.add(LevelWarn, source, message, details) } // Error records an error-level entry. func (l *Logger) Error(source, message string, details ...string) { l.add(LevelError, source, message, details) } // Snapshot returns a copy of all buffered entries, oldest first. func (l *Logger) Snapshot() []Entry { l.mu.Lock() out := make([]Entry, len(l.buf)) copy(out, l.buf) l.mu.Unlock() return out } // Subscribe registers fn to be called for every new entry. func (l *Logger) Subscribe(fn func(Entry)) { l.mu.Lock() l.listeners = append(l.listeners, fn) l.mu.Unlock() } // Default is the package-level logger instance. var Default = NewLogger()