feat: add structured logging throughout audio pipeline
Add a lightweight log package (log/log.go) with debug/info/warn/error levels. Wire it up via a -log flag (default: warn). Logging covers: UDP transport: - Socket open/failure, CryptSetup receipt, crypto initialization - First UDP audio send confirmation - UDP read errors and packet counts - TCP fallback reason (no socket, waiting for crypto) Audio pipeline: - Source (mic) routine start with config details - Audio stream start/end per remote user - First audio stream creation for each user - Packet loss detection with sequence gaps and PLC generation - Decoder reset on sequence reordering Usage: barnard -log=debug -server=mumble.example.com Levels: debug, info, warn, error
This commit is contained in:
committed by
Brandon McGinty
parent
a218493eb0
commit
e483512f74
+116
@@ -0,0 +1,116 @@
|
||||
// Package log provides debug logging for the barnard audio pipeline.
|
||||
// Logging is disabled by default and can be enabled via SetLogger.
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Level represents logging severity.
|
||||
type Level int
|
||||
|
||||
const (
|
||||
LevelDebug Level = iota
|
||||
LevelInfo
|
||||
LevelWarn
|
||||
LevelError
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case LevelDebug:
|
||||
return "DEBUG"
|
||||
case LevelInfo:
|
||||
return "INFO"
|
||||
case LevelWarn:
|
||||
return "WARN"
|
||||
case LevelError:
|
||||
return "ERROR"
|
||||
default:
|
||||
return "???"
|
||||
}
|
||||
}
|
||||
|
||||
// Logger receives log messages. The default logger is a no-op.
|
||||
type Logger interface {
|
||||
Log(level Level, format string, args ...interface{})
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
logger Logger = &nopLogger{}
|
||||
)
|
||||
|
||||
type nopLogger struct{}
|
||||
|
||||
func (n *nopLogger) Log(level Level, format string, args ...interface{}) {}
|
||||
|
||||
// SetLogger sets the destination for log messages. Pass nil to disable.
|
||||
func SetLogger(l Logger) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if l == nil {
|
||||
logger = &nopLogger{}
|
||||
} else {
|
||||
logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// WriterLogger is a simple Logger that writes to an io.Writer.
|
||||
type WriterLogger struct {
|
||||
mu sync.Mutex
|
||||
w io.Writer
|
||||
level Level
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// NewWriterLogger creates a logger that writes to w, filtering below level.
|
||||
func NewWriterLogger(w io.Writer, level Level) *WriterLogger {
|
||||
if w == nil {
|
||||
w = os.Stderr
|
||||
}
|
||||
return &WriterLogger{w: w, level: level}
|
||||
}
|
||||
|
||||
func (wl *WriterLogger) Log(level Level, format string, args ...interface{}) {
|
||||
if level < wl.level {
|
||||
return
|
||||
}
|
||||
wl.mu.Lock()
|
||||
defer wl.mu.Unlock()
|
||||
now := time.Now().Format("15:04:05.000")
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
fmt.Fprintf(wl.w, "%s [%-5s] %s\n", now, level.String(), msg)
|
||||
}
|
||||
|
||||
func Debug(format string, args ...interface{}) {
|
||||
mu.Lock()
|
||||
l := logger
|
||||
mu.Unlock()
|
||||
l.Log(LevelDebug, format, args...)
|
||||
}
|
||||
|
||||
func Info(format string, args ...interface{}) {
|
||||
mu.Lock()
|
||||
l := logger
|
||||
mu.Unlock()
|
||||
l.Log(LevelInfo, format, args...)
|
||||
}
|
||||
|
||||
func Warn(format string, args ...interface{}) {
|
||||
mu.Lock()
|
||||
l := logger
|
||||
mu.Unlock()
|
||||
l.Log(LevelWarn, format, args...)
|
||||
}
|
||||
|
||||
func Error(format string, args ...interface{}) {
|
||||
mu.Lock()
|
||||
l := logger
|
||||
mu.Unlock()
|
||||
l.Log(LevelError, format, args...)
|
||||
}
|
||||
Reference in New Issue
Block a user