101 lines
1.9 KiB
Go
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()
|