From 3e3d44a16877203c86098fc1d07e2677ce333bcf Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 1 Mar 2026 15:49:21 -0800 Subject: [PATCH] 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 https://git.eeqj.de/sneak/webhooker/issues/8 --- internal/config/config.go | 2 -- internal/logger/logger.go | 60 +++++++++++++-------------------------- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index b78eae2..ee4eb86 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -118,8 +118,6 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) { if s.Debug { params.Logger.EnableDebugLogging() - s.log = params.Logger.Get() - log.Debug("Debug mode enabled") } // Log configuration summary (without secrets) diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 687b241..61ead7f 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -17,8 +17,9 @@ type LoggerParams struct { } type Logger struct { - logger *slog.Logger - params LoggerParams + logger *slog.Logger + levelVar *slog.LevelVar + params LoggerParams } // nolint:revive // lc parameter is required by fx even if unused @@ -26,24 +27,30 @@ 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: slog.LevelInfo, - 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 - }, + Level: l.levelVar, + ReplaceAttr: replaceAttr, } if tty { @@ -63,34 +70,7 @@ func New(lc fx.Lifecycle, params LoggerParams) (*Logger, error) { } func (l *Logger) EnableDebugLogging() { - // Recreate logger with debug level - tty := false - if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) != 0 { - tty = true - } - - var handler slog.Handler - opts := &slog.HandlerOptions{ - Level: slog.LevelDebug, - 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 - }, - } - - if tty { - handler = slog.NewTextHandler(os.Stdout, opts) - } else { - handler = slog.NewJSONHandler(os.Stdout, opts) - } - - l.logger = slog.New(handler) - slog.SetDefault(l.logger) + l.levelVar.Set(slog.LevelDebug) l.logger.Debug("debug logging enabled", "debug", true) }