A production-ready structured logging package for Go, built on top of log/slog with file rotation support and flexible output options.
✨ Core Features:
- 📝 Structured logging with
log/slog - 🎯 Multiple output types:
stdout,filewith automatic rotation - 📊 Log format options:
text(human-readable),json(machine-readable) - 📋 Configurable log levels:
debug,info,warn,error - 🔄 Automatic file rotation using lumberjack
- 🛡️ Safe resource management with
CloseableLogger - 🚨 Enhanced error logging with automatic stack traces
- 🔧 Zero-downtime logger replacement in running applications
go get github.com/AlexeiDKL/loggerpackage main
import (
"log"
"github.com/AlexeiDKL/logger"
)
func main() {
cfg := logger.Config{
LogOutput: "stdout",
LogType: "text",
Level: "info",
}
log, err := logger.InitLogger(cfg)
if err != nil {
log.Fatalf("failed to initialize logger: %v", err)
}
defer log.Close()
log.Info("Application started", "version", "1.0.0")
log.Debug("This won''t be printed with info level")
log.Warn("Warning message", "code", 42)
log.Error("Error occurred", "err", "something went wrong")
}cfg := logger.Config{
LogOutput: "file",
Path: "/var/log/myapp/app.log", // absolute path required for file output
LogType: "json", // json or text
Level: "debug",
MaxSize: 100, // MB before rotation
MaxBackups: 5, // max rotated files to keep
MaxAge: 30, // days before deletion
Compress: true, // gzip compressed backups
}
appLog, err := logger.InitLogger(cfg)
if err != nil {
panic(err)
}
defer appLog.Close()
appLog.Info("Request processed", "duration_ms", 145, "status", 200)type Config struct {
LogType string // "json" or "text"
LogOutput string // "stdout" or "file"
Level string // "debug", "info", "warn", "error"
Path string // required for file output (absolute path)
MaxSize int // MB (file rotation)
MaxBackups int // number of backup files to keep
MaxAge int // days before backup deletion
Compress bool // gzip compress backups
}- debug: Detailed diagnostic information (development)
- info: General informational messages (production)
- warn: Warning conditions (recoverable issues)
- error: Error conditions (unrecoverable issues)
All methods follow log/slog patterns and safely handle nil loggers:
| Method | Description |
|---|---|
Info(msg, keysAndValues...) |
Log info level message |
Debug(msg, keysAndValues...) |
Log debug level message |
Warn(msg, keysAndValues...) |
Log warn level + auto stack trace if error/err/panic present |
WarnStack(msg, keysAndValues...) |
Log warn level + explicit stack trace |
Error(msg, keysAndValues...) |
Log error level + auto stack trace if error/err/panic present |
ErrorStack(msg, keysAndValues...) |
Log error level + explicit stack trace |
Console(msg, keysAndValues...) |
Output directly to stderr (config warnings) |
Close() |
Flush buffers and release resources (MUST call before shutdown) |
log.Warn("validation failed", "err", errors.New("invalid input"))
// Output includes automatic stack trace when "err" key is detected
log.Error("database error", "error", "connection timeout")
// "error" key also triggers stack trace
log.Error("panic recovered", "panic", "nil pointer", "user_id", 123)
// "panic" key also adds stack tracelog.ErrorStack("critical failure", "component", "auth_service")
// Always includes full stack trace regardless of keyslog.Info("user login",
"user_id", 42,
"ip_address", "192.168.1.1",
"duration_ms", 234,
"success", true,
)The logger validates file paths and rotation parameters:
// ❌ Will error: relative path for file output
cfg := logger.Config{
LogOutput: "file",
Path: "logs/app.log", // must be absolute
LogType: "text",
Level: "info",
}
_, err := logger.InitLogger(cfg) // error: relative path
// ❌ Will error: negative rotation values
cfg := logger.Config{
LogOutput: "file",
Path: "/var/log/app.log",
MaxSize: -1, // invalid
MaxBackups: -1, // invalid
MaxAge: -1, // invalid
}
_, err := logger.InitLogger(cfg) // error: invalid params// ⚠️ File output without path falls back to ./apps.log
cfg := logger.Config{
LogOutput: "file",
Path: "", // empty path
LogType: "text",
Level: "info",
}
log, err := logger.InitLogger(cfg)
// Uses ./apps.log and prints warning to stderr:
// "WARNING: log path not specified, using fallback ./apps.log. Please set absolute path in config to avoid issues."✅ DO:
- ✓ Always call
defer log.Close()to flush buffers and release file handles - ✓ Use absolute paths for file output to avoid confusion
- ✓ Use structured logging with key-value pairs
- ✓ Include relevant context (user_id, request_id, duration, etc.)
- ✓ Use appropriate log levels (debug for noise, info for important events, error for issues)
❌ DON''T:
- ✗ Ignore the Close() method - logs may be lost
- ✗ Use string concatenation instead of structured keys
- ✗ Log the same message with different casing (normalizes to lowercase)
- ✗ Rely on fallback path for production - explicitly set absolute path
Run the test suite:
go test -vThe package includes tests for:
- ✓ Stdout output (text format)
- ✓ File output (absolute and relative paths)
- ✓ File rotation configuration
- ✓ Invalid log types and outputs
- ✓ Invalid rotation parameters
- ✓ Fallback path handling
CloseableLogger wraps slog.Logger, which is safe for concurrent use. Multiple goroutines can safely call logging methods simultaneously.
// Safe to use from multiple goroutines
go func() { log.Info("goroutine 1") }()
go func() { log.Info("goroutine 2") }()log/slog(stdlib, Go 1.21+)gopkg.in/natefinch/lumberjack.v2- File rotation
- Go 1.21 or higher (uses
log/slog)
MIT License - See LICENSE file for details
Contributions are welcome! Please open an issue or submit a pull request.
Alexei DKL