grokkit/internal/logger/logger.go
Greg Gauthier f0322a84bd
Some checks failed
CI / Test (push) Successful in 33s
CI / Lint (push) Failing after 17s
CI / Build (push) Successful in 21s
chore(lint): enhance golangci configuration and add security annotations
- Expand .golangci.yml with more linters (bodyclose, errcheck, etc.), settings for govet, revive, gocritic, gosec
- Add // nolint:gosec comments for intentional file operations and subprocesses
- Change file write permissions from 0644 to 0600 for better security
- Refactor loops, error handling, and test parallelism with t.Parallel()
- Minor fixes: ignore unused args, use errors.Is, adjust mkdir permissions to 0750
2026-03-04 20:00:32 +00:00

122 lines
2.4 KiB
Go

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, 0750); err != nil {
return err
}
logFile := filepath.Join(logDir, "grokkit.log")
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
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()
}