Files
2026-06-23 05:02:15 +08:00

101 lines
1.9 KiB
Go

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()