package logger import ( "context" "io" "log/slog" "os" "path/filepath" ) var ( logger *slog.Logger level = new(slog.LevelVar) // Allows dynamic level changes ) // Init initializes the logger with the specified log level func Init(logLevel string) error { home, err := os.UserHomeDir() if err != nil { home = "." } logDir := filepath.Join(home, ".config", "grokkit") if err := os.MkdirAll(logDir, 0755); err != nil { return err } logFile := filepath.Join(logDir, "grokkit.log") file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { return err } // Parse log level switch logLevel { case "debug": level.Set(slog.LevelDebug) case "info": level.Set(slog.LevelInfo) case "warn": level.Set(slog.LevelWarn) case "error": level.Set(slog.LevelError) default: level.Set(slog.LevelInfo) } // Create multi-writer for both file and stderr (if debug) var w io.Writer = file if logLevel == "debug" { w = io.MultiWriter(file, os.Stderr) } // Use JSON handler for structured logging handler := slog.NewJSONHandler(w, &slog.HandlerOptions{ Level: level, }) logger = slog.New(handler) slog.SetDefault(logger) return nil } // SetLevel changes the log level dynamically func SetLevel(logLevel string) { switch logLevel { case "debug": level.Set(slog.LevelDebug) case "info": level.Set(slog.LevelInfo) case "warn": level.Set(slog.LevelWarn) case "error": level.Set(slog.LevelError) } } // Debug logs at debug level with structured fields func Debug(msg string, args ...any) { if logger != nil { logger.Debug(msg, args...) } } // Info logs at info level with structured fields func Info(msg string, args ...any) { if logger != nil { logger.Info(msg, args...) } } // Warn logs at warn level with structured fields func Warn(msg string, args ...any) { if logger != nil { logger.Warn(msg, args...) } } // Error logs at error level with structured fields func Error(msg string, args ...any) { if logger != nil { logger.Error(msg, args...) } } // With returns a logger with additional context fields func With(args ...any) *slog.Logger { if logger != nil { return logger.With(args...) } return slog.Default() } // WithContext returns a logger with values from context func WithContext(ctx context.Context) *slog.Logger { if logger != nil { return logger.With(slog.Any("context", ctx)) } return slog.Default() }