diff --git a/README.md b/README.md index 7be144a..e8199f8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Production-ready Go logging package built on top of `slog` with automatic file r - **Dual Output Streams**: Separate files for general logs and errors - **Automatic File Rotation**: Daily rotation with configurable retention -- **Debug/Production Modes**: Easy switching between detailed and minimal logging +- **Flexible Log Levels**: Support for all slog levels (DEBUG, INFO, WARN, ERROR) - **Structured Logging**: Built on Go's standard `slog` package - **Thread-Safe**: Concurrent logging support with mutex protection - **Zero Dependencies**: Uses only Go standard library @@ -55,7 +55,7 @@ func main() { config := islogger.DefaultConfig(). WithAppName("myapp"). WithLogDir("logs"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithRetentionDays(14). WithJSONFormat(true). WithTimeFormat("2006-01-02 15:04:05") @@ -74,7 +74,7 @@ defer logger.Close() |--------|---------|-------------| | `LogDir` | `"logs"` | Directory for log files | | `AppName` | `"app"` | Application name (used in filenames) | -| `Debug` | `false` | Enable debug level logging | +| `LogLevel` | `INFO` | Minimum log level (DEBUG, INFO, WARN, ERROR) | | `RetentionDays` | `7` | Days to keep old log files | | `JSONFormat` | `false` | Use JSON format instead of text | | `AddSource` | `false` | Include source file and line info | @@ -114,16 +114,18 @@ logs/ ## 🎯 Usage Examples -### Debug Mode Switching +### Log Level Management ```go -// Start in production mode (only warnings and errors) -logger.SetDebug(false) +// Start with WARN level (only warnings and errors) +logger.SetLevel(slog.LevelWarn) logger.Debug("Won't be logged") +logger.Info("Won't be logged") +logger.Warn("Will be logged") logger.Error("Will be logged") -// Switch to debug mode -logger.SetDebug(true) +// Switch to DEBUG level (all messages) +logger.SetLevel(slog.LevelDebug) logger.Debug("Now this will be logged") ``` @@ -410,7 +412,7 @@ Complete example for production environments: config := islogger.DefaultConfig(). WithAppName("myapp"). WithLogDir("/var/log/myapp"). - WithDebug(false). + WithLogLevel(slog.LevelWarn). WithRetentionDays(30). WithJSONFormat(true). // Security: mask sensitive fields @@ -477,7 +479,6 @@ With(args ...any) *Logger WithContext(ctx context.Context) *Logger // Control functions -SetDebug(debug bool) error SetLevel(level slog.Level) error Flush() error Close() error @@ -500,7 +501,6 @@ With(args ...any) *Logger WithContext(ctx context.Context) *Logger // Management methods -SetDebug(debug bool) error SetLevel(level slog.Level) error Flush() error RotateNow() error @@ -516,7 +516,7 @@ Close() error config := DefaultConfig(). WithAppName("myapp"). WithLogDir("custom-logs"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithRetentionDays(30). WithJSONFormat(true). WithTimeFormat("2006-01-02 15:04:05"). @@ -551,7 +551,7 @@ RegexMaskFilter(pattern, mask string) RegexFilter ## 🎨 Log Levels -- **Debug**: Detailed information for debugging (only in debug mode) +- **Debug**: Detailed information for debugging (when LogLevel is DEBUG) - **Info**: General information about application flow - **Warn**: Warning messages (logged to both files) - **Error**: Error messages (logged to both files) diff --git a/config.go b/config.go index 47d8e07..f0ea343 100644 --- a/config.go +++ b/config.go @@ -7,14 +7,14 @@ import ( ) type Config struct { - LogDir string // Directory for log files - AppName string // Application name for log file prefix - Debug bool // Enable debug logging - RetentionDays int // Number of days to keep log files - JSONFormat bool // Use JSON format instead of text - AddSource bool // Add source file and line info - TimeFormat string // Custom time format - ConsoleOutput bool // Enable output to console (stdout/stderr) + LogDir string // Directory for log files + AppName string // Application name for log file prefix + LogLevel slog.Level // Minimum log level (DEBUG, INFO, WARN, ERROR) + RetentionDays int // Number of days to keep log files + JSONFormat bool // Use JSON format instead of text + AddSource bool // Add source file and line info + TimeFormat string // Custom time format + ConsoleOutput bool // Enable output to console (stdout/stderr) // Buffering configuration BufferSize int // Buffer size in bytes (0 = no buffering) @@ -29,7 +29,7 @@ func DefaultConfig() Config { return Config{ LogDir: "logs", AppName: "app", - Debug: false, + LogLevel: slog.LevelInfo, // INFO and above by default RetentionDays: 7, JSONFormat: false, AddSource: false, @@ -42,9 +42,9 @@ func DefaultConfig() Config { } } -// WithDebug enables debug logging -func (c Config) WithDebug(debug bool) Config { - c.Debug = debug +// WithLogLevel sets the minimum log level +func (c Config) WithLogLevel(level slog.Level) Config { + c.LogLevel = level return c } diff --git a/console_test.go b/console_test.go index e8e52b8..c6f4547 100644 --- a/console_test.go +++ b/console_test.go @@ -2,6 +2,7 @@ package iSlogger import ( "bytes" + "log/slog" "os" "strings" "testing" @@ -20,7 +21,7 @@ func TestConsoleOutput_Enabled(t *testing.T) { WithAppName("console-test"). WithLogDir("test-logs"). WithConsoleOutput(true). - WithDebug(true) + WithLogLevel(slog.LevelDebug) logger, err := New(config) if err != nil { @@ -57,7 +58,7 @@ func TestConsoleOutput_Disabled(t *testing.T) { WithAppName("console-test-disabled"). WithLogDir("test-logs"). WithConsoleOutput(false). - WithDebug(true) + WithLogLevel(slog.LevelDebug) logger, err := New(config) if err != nil { diff --git a/examples/advanced/main.go b/examples/advanced/main.go index aa9de84..972fca5 100644 --- a/examples/advanced/main.go +++ b/examples/advanced/main.go @@ -13,7 +13,7 @@ func main() { // Advanced configuration with JSON format and custom time config := iSlogger.DefaultConfig(). WithAppName("advanced-app"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithLogDir("advanced-logs"). WithRetentionDays(14). WithJSONFormat(true). diff --git a/examples/basic/main.go b/examples/basic/main.go index 38d665e..5a3498f 100644 --- a/examples/basic/main.go +++ b/examples/basic/main.go @@ -1,6 +1,7 @@ package main import ( + "log/slog" "time" "github.com/sarff/iSlogger" @@ -10,7 +11,7 @@ func main() { // Initialize global logger with default configuration config := iSlogger.DefaultConfig(). WithAppName("myapp"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithLogDir("logs") if err := iSlogger.Init(config); err != nil { @@ -29,18 +30,18 @@ func main() { userLogger.Info("User logged in", "username", "john_doe") userLogger.Error("User action failed", "action", "purchase", "reason", "insufficient funds") - // Demonstrate debug mode switching - iSlogger.Info("Switching to production mode...") - iSlogger.SetDebug(false) // Only warnings and errors will be logged + // Demonstrate log level switching + iSlogger.Info("Switching to WARN level...") + iSlogger.SetLevel(slog.LevelWarn) // Only warnings and errors will be logged iSlogger.Debug("This won't appear") // Won't be logged iSlogger.Info("This won't appear") // Won't be logged iSlogger.Warn("This will appear") // Will be logged iSlogger.Error("This will appear") // Will be logged - // Switch back to debug mode - iSlogger.SetDebug(true) - iSlogger.Debug("Debug mode is back!") + // Switch back to debug level + iSlogger.SetLevel(slog.LevelDebug) + iSlogger.Debug("Debug level is back!") // Structured logging example iSlogger.Info("Request processed", diff --git a/examples/console/main.go b/examples/console/main.go index 3a46d65..c1ccdbf 100644 --- a/examples/console/main.go +++ b/examples/console/main.go @@ -1,6 +1,7 @@ package main import ( + "log/slog" "time" "github.com/sarff/iSlogger" @@ -10,7 +11,7 @@ func main() { // Example 1: Default behavior (console output enabled) config1 := iSlogger.DefaultConfig(). WithAppName("console-app"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithLogDir("logs") logger1, err := iSlogger.New(config1) @@ -26,7 +27,7 @@ func main() { // Example 2: Disable console output (file only) config2 := iSlogger.DefaultConfig(). WithAppName("file-only-app"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithLogDir("logs"). WithConsoleOutput(false) @@ -43,7 +44,7 @@ func main() { // Example 3: Explicitly enable console output config3 := iSlogger.DefaultConfig(). WithAppName("explicit-console"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithLogDir("logs"). WithConsoleOutput(true) @@ -59,7 +60,7 @@ func main() { // Example 4: JSON format with console output config4 := iSlogger.DefaultConfig(). WithAppName("json-console"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithLogDir("logs"). WithJSONFormat(true). WithConsoleOutput(true) diff --git a/examples/filters/main.go b/examples/filters/main.go index 2b3a610..a8d7369 100644 --- a/examples/filters/main.go +++ b/examples/filters/main.go @@ -27,7 +27,7 @@ func basicFilteringExample() { config := iSlogger.DefaultConfig(). WithAppName("filter-basic"). WithLogDir("logs"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). // Mask sensitive fields WithFieldMask("password", "***"). WithFieldMask("api_key", "***HIDDEN***"). @@ -60,7 +60,7 @@ func conditionalLoggingExample() { config := iSlogger.DefaultConfig(). WithAppName("filter-condition"). WithLogDir("logs"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). // Only log warnings/errors OR messages containing "important" WithCondition(iSlogger.AnyCondition( iSlogger.LevelCondition(slog.LevelWarn), @@ -97,7 +97,7 @@ func rateLimitingExample() { config := iSlogger.DefaultConfig(). WithAppName("filter-rate"). WithLogDir("logs"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). // Limit DEBUG messages to 5 per minute WithRateLimit(slog.LevelDebug, 5, time.Minute). // Limit INFO messages to 20 per minute @@ -126,7 +126,7 @@ func productionFilteringExample() { config := iSlogger.DefaultConfig(). WithAppName("filter-production"). WithLogDir("logs"). - WithDebug(false). // Production mode + WithLogLevel(slog.LevelWarn). // Production mode (WARN+) // Security: mask all sensitive fields WithFieldMask("password", "***"). WithFieldMask("api_key", "***"). diff --git a/examples/web-app/main.go b/examples/web-app/main.go index 62c3654..128f65a 100644 --- a/examples/web-app/main.go +++ b/examples/web-app/main.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "os" "os/signal" @@ -27,7 +28,7 @@ func main() { // Initialize logger for web application config := iSlogger.DefaultConfig(). WithAppName("webapp"). - WithDebug(false). // Production mode + WithLogLevel(slog.LevelWarn). // Production mode WithLogDir("web-logs"). WithJSONFormat(true) // JSON format for log aggregation diff --git a/filters_test.go b/filters_test.go index 2347c80..e51043c 100644 --- a/filters_test.go +++ b/filters_test.go @@ -11,7 +11,7 @@ func TestFieldMasking(t *testing.T) { config := DefaultConfig(). WithAppName("test-mask"). WithLogDir("test-logs-mask"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithFieldMask("password", "***"). WithFieldMask("credit_card", "****-****-****-****") @@ -32,7 +32,7 @@ func TestFieldRedaction(t *testing.T) { config := DefaultConfig(). WithAppName("test-redact"). WithLogDir("test-logs-redact"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithFieldRedaction("sensitive_data") logger, err := New(config) @@ -51,7 +51,7 @@ func TestRegexFilter(t *testing.T) { config := DefaultConfig(). WithAppName("test-regex"). WithLogDir("test-logs-regex"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithRegexFilter(`\d{4}-\d{4}-\d{4}-\d{4}`, "****-****-****-****"). // Credit card pattern WithRegexFilter(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`, "***@***.***") // Email pattern @@ -71,7 +71,7 @@ func TestConditionalLogging(t *testing.T) { config := DefaultConfig(). WithAppName("test-condition"). WithLogDir("test-logs-condition"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithLevelCondition(slog.LevelWarn). // Only WARN and above WithMessageContainsCondition("important") // OR contains "important" @@ -95,7 +95,7 @@ func TestAttributeCondition(t *testing.T) { config := DefaultConfig(). WithAppName("test-attr"). WithLogDir("test-logs-attr"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithAttributeCondition("user_type", "admin") // Only admin users logger, err := New(config) @@ -115,7 +115,7 @@ func TestTimeBasedCondition(t *testing.T) { config := DefaultConfig(). WithAppName("test-time"). WithLogDir("test-logs-time"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithTimeBasedCondition(9, 17) // Only during work hours (9-17) logger, err := New(config) @@ -136,7 +136,7 @@ func TestRateLimit(t *testing.T) { config := DefaultConfig(). WithAppName("test-rate"). WithLogDir("test-logs-rate"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithRateLimit(slog.LevelDebug, 3, time.Minute) // Max 3 DEBUG per minute logger, err := New(config) @@ -159,7 +159,7 @@ func TestCombinedFilters(t *testing.T) { config := DefaultConfig(). WithAppName("test-combined"). WithLogDir("test-logs-combined"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithFieldMask("password", "***"). WithRegexFilter(`\d{4}-\d{4}-\d{4}-\d{4}`, "****-****-****-****"). WithLevelCondition(slog.LevelInfo). // Only INFO and above diff --git a/global.go b/global.go index 1ec579e..850262e 100644 --- a/global.go +++ b/global.go @@ -128,18 +128,6 @@ func SetLevel(level slog.Level) error { return nil } -// SetDebug enables or disables debug mode for the global logger -func SetDebug(debug bool) error { - globalMu.RLock() - logger := defaultLogger - globalMu.RUnlock() - - if logger != nil { - return logger.SetDebug(debug) - } - return nil -} - // Flush flushes all buffers of the global logger func Flush() error { globalMu.RLock() diff --git a/logger.go b/logger.go index d9018ef..b1dfe79 100644 --- a/logger.go +++ b/logger.go @@ -168,12 +168,8 @@ func (l *Logger) initLoggers() error { }, } - // Set log level based on debug mode - if l.config.Debug { - opts.Level = slog.LevelDebug - } else { - opts.Level = slog.LevelWarn - } + // Set log level from config + opts.Level = l.config.LogLevel // Create base handlers var infoHandler, errorHandler slog.Handler @@ -276,13 +272,7 @@ func (l *Logger) WithContext(ctx context.Context) *Logger { // SetLevel changes the log level dynamically func (l *Logger) SetLevel(level slog.Level) error { - l.config.Debug = level == slog.LevelDebug - return l.initLoggers() -} - -// SetDebug enables or disables debug mode -func (l *Logger) SetDebug(debug bool) error { - l.config.Debug = debug + l.config.LogLevel = level return l.initLoggers() } diff --git a/logger_test.go b/logger_test.go index 4e74364..15b3a8d 100644 --- a/logger_test.go +++ b/logger_test.go @@ -13,7 +13,7 @@ func TestNew(t *testing.T) { config := DefaultConfig(). WithAppName("test"). WithLogDir("test-logs"). - WithDebug(true) + WithLogLevel(slog.LevelDebug) logger, err := New(config) if err != nil { @@ -26,8 +26,8 @@ func TestNew(t *testing.T) { t.Errorf("Expected app name 'test', got '%s'", logger.config.AppName) } - if !logger.config.Debug { - t.Error("Expected debug mode to be enabled") + if logger.config.LogLevel != slog.LevelDebug { + t.Error("Expected log level to be DEBUG") } } @@ -35,7 +35,7 @@ func TestLogLevels(t *testing.T) { config := DefaultConfig(). WithAppName("test-levels"). WithLogDir("test-logs-levels"). - WithDebug(true) + WithLogLevel(slog.LevelDebug) logger, err := New(config) if err != nil { @@ -62,30 +62,30 @@ func TestLogLevels(t *testing.T) { } } -func TestDebugMode(t *testing.T) { +func TestLogLevelChange(t *testing.T) { config := DefaultConfig(). - WithAppName("test-debug"). - WithLogDir("test-logs-debug"). - WithDebug(false) // Start in production mode + WithAppName("test-level"). + WithLogDir("test-logs-level"). + WithLogLevel(slog.LevelWarn) // Start with WARN level logger, err := New(config) if err != nil { t.Fatalf("Failed to create logger: %v", err) } defer logger.Close() - defer os.RemoveAll("test-logs-debug") + defer os.RemoveAll("test-logs-level") - if logger.config.Debug { - t.Error("Expected debug mode to be disabled initially") + if logger.config.LogLevel != slog.LevelWarn { + t.Error("Expected log level to be WARN initially") } - err = logger.SetDebug(true) + err = logger.SetLevel(slog.LevelDebug) if err != nil { - t.Errorf("Failed to enable debug mode: %v", err) + t.Errorf("Failed to change log level: %v", err) } - if !logger.config.Debug { - t.Error("Expected debug mode to be enabled after SetDebug(true)") + if logger.config.LogLevel != slog.LevelDebug { + t.Error("Expected log level to be DEBUG after SetLevel(DEBUG)") } } @@ -93,7 +93,7 @@ func TestWith(t *testing.T) { config := DefaultConfig(). WithAppName("test-with"). WithLogDir("test-logs-with"). - WithDebug(true) + WithLogLevel(slog.LevelDebug) logger, err := New(config) if err != nil { @@ -112,7 +112,7 @@ func TestGlobalLogger(t *testing.T) { config := DefaultConfig(). WithAppName("test-global"). WithLogDir("test-logs-global"). - WithDebug(true) + WithLogLevel(slog.LevelDebug) err := Init(config) if err != nil { @@ -136,7 +136,7 @@ func TestConfigBuilder(t *testing.T) { config := DefaultConfig(). WithAppName("builder-test"). WithLogDir("builder-logs"). - WithDebug(true). + WithLogLevel(slog.LevelDebug). WithRetentionDays(14). WithJSONFormat(true). WithTimeFormat("2006-01-02 15:04:05"). @@ -150,8 +150,8 @@ func TestConfigBuilder(t *testing.T) { t.Errorf("Expected log dir 'builder-logs', got '%s'", config.LogDir) } - if !config.Debug { - t.Error("Expected debug mode to be enabled") + if config.LogLevel != slog.LevelDebug { + t.Error("Expected log level to be DEBUG") } if config.RetentionDays != 14 { @@ -175,7 +175,7 @@ func TestFileRotation(t *testing.T) { config := DefaultConfig(). WithAppName("test-rotation"). WithLogDir("test-logs-rotation"). - WithDebug(true) + WithLogLevel(slog.LevelDebug) logger, err := New(config) if err != nil { @@ -308,7 +308,7 @@ func BenchmarkLogging(b *testing.B) { config := DefaultConfig(). WithAppName("bench"). WithLogDir("bench-logs"). - WithDebug(true) + WithLogLevel(slog.LevelDebug) logger, err := New(config) if err != nil { @@ -332,7 +332,7 @@ func TestLogger_BufferedWrites(t *testing.T) { config := DefaultConfig(). WithLogDir(tempDir). WithAppName("buffer_test"). - WithDebug(true). // Enable debug to see INFO messages + WithLogLevel(slog.LevelDebug). // Enable debug to see INFO messages WithBufferSize(1024). WithFlushInterval(100 * time.Millisecond). WithFlushOnLevel(slog.LevelError) @@ -391,8 +391,8 @@ func TestLogger_BufferedWritesWithoutBuffering(t *testing.T) { config := DefaultConfig(). WithLogDir(tempDir). WithAppName("nobuffer_test"). - WithDebug(true). // Enable debug to see INFO messages - WithoutBuffering() // Disable buffering + WithLogLevel(slog.LevelDebug). // Enable debug to see INFO messages + WithoutBuffering() // Disable buffering l, err := New(config) if err != nil { @@ -432,7 +432,7 @@ func TestLogger_BufferedWritesAutoFlush(t *testing.T) { config := DefaultConfig(). WithLogDir(tempDir). WithAppName("autoflush_test"). - WithDebug(true). // Enable debug to see INFO messages + WithLogLevel(slog.LevelDebug). // Enable debug to see INFO messages WithBufferSize(1024). WithFlushInterval(50 * time.Millisecond) // Very short interval