Files
webhooker/internal/logger/logger.go
clawbot 3e3d44a168 refactor: use slog.LevelVar for dynamic log levels
Replace the pattern of recreating the logger handler when enabling debug
logging. Now use slog.LevelVar which allows changing the log level
dynamically without recreating the handler or logger instance.

closes #8
2026-03-01 16:01:44 -08:00

93 lines
2.0 KiB
Go

package logger
import (
"io"
"log/slog"
"os"
"time"
"go.uber.org/fx"
"sneak.berlin/go/webhooker/internal/globals"
)
// nolint:revive // LoggerParams is a standard fx naming convention
type LoggerParams struct {
fx.In
Globals *globals.Globals
}
type Logger struct {
logger *slog.Logger
levelVar *slog.LevelVar
params LoggerParams
}
// nolint:revive // lc parameter is required by fx even if unused
func New(lc fx.Lifecycle, params LoggerParams) (*Logger, error) {
l := new(Logger)
l.params = params
// Use slog.LevelVar for dynamic log level changes
l.levelVar = new(slog.LevelVar)
l.levelVar.Set(slog.LevelInfo)
// Determine if we're running in a terminal
tty := false
if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) != 0 {
tty = true
}
replaceAttr := func(_ []string, a slog.Attr) slog.Attr { // nolint:revive // groups unused
// Always use UTC for timestamps
if a.Key == slog.TimeKey {
if t, ok := a.Value.Any().(time.Time); ok {
return slog.Time(slog.TimeKey, t.UTC())
}
}
return a
}
var handler slog.Handler
opts := &slog.HandlerOptions{
Level: l.levelVar,
ReplaceAttr: replaceAttr,
}
if tty {
// Use text handler for terminal output (human-readable)
handler = slog.NewTextHandler(os.Stdout, opts)
} else {
// Use JSON handler for production (machine-readable)
handler = slog.NewJSONHandler(os.Stdout, opts)
}
l.logger = slog.New(handler)
// Set as default logger
slog.SetDefault(l.logger)
return l, nil
}
func (l *Logger) EnableDebugLogging() {
l.levelVar.Set(slog.LevelDebug)
l.logger.Debug("debug logging enabled", "debug", true)
}
func (l *Logger) Get() *slog.Logger {
return l.logger
}
func (l *Logger) Identify() {
l.logger.Info("starting",
"appname", l.params.Globals.Appname,
"version", l.params.Globals.Version,
"buildarch", l.params.Globals.Buildarch,
)
}
// Helper methods to maintain compatibility with existing code
func (l *Logger) Writer() io.Writer {
return os.Stdout
}