A drop-in replacement for logrus that uses Go's standard log/slog
package under the hood. This library provides a migration path for projects that want to move from logrus to Go's structured logging without changing their existing code or to facilitate a gradual migration.
Note
Claude Code almost entirely created this project. It is an experiment to see how this might solve the migration problem. Using this I migrated the entire go-choria
code base in less than a hour with no code changes.
- Zero code changes: Replace your logrus import and your code continues to work
- No external dependencies: Uses only Go's standard library
log/slog
- Full API compatibility: Supports all logrus types, methods, and patterns
- Modern logging: Leverages Go 1.21+'s structured logging capabilities
- Performance: Benefits from slog's optimized implementation
go get github.com/choria-io/slogrus
Replace your logrus import:
// Before
import "github.com/sirupsen/logrus"
// After
import "github.com/choria-io/slogrus"
All your existing code continues to work:
slogrus.Info("Application started")
slogrus.WithField("user", "john").Info("User logged in")
slogrus.WithFields(slogrus.Fields{
"method": "GET",
"path": "/api/users",
"status": 200,
}).Info("Request completed")
Create and configure custom loggers:
logger := slogrus.New()
logger.SetLevel(slogrus.DebugLevel)
logger.SetOutput(os.Stdout)
logger.Debug("Debug message")
logger.Info("Info message")
logger.Error("Error message")
Use fields for structured logging:
log := slogrus.WithFields(slogrus.Fields{
"component": "database",
"operation": "query",
"table": "users",
})
log.Info("Query executed successfully")
log.WithField("duration", "45ms").Info("Query performance")
Chain methods for building complex log entries:
slogrus.WithField("service", "api").
WithField("version", "1.2.3").
WithError(err).
Error("Service startup failed")
Use context for request tracing:
ctx := context.WithValue(context.Background(), "requestID", "req-123")
slogrus.WithContext(ctx).Info("Processing request")
All logrus levels are supported:
slogrus.Trace("Very detailed information")
slogrus.Debug("Debug information")
slogrus.Info("General information")
slogrus.Warn("Warning message")
slogrus.Error("Error occurred")
slogrus.Fatal("Fatal error - will exit") // Calls os.Exit(1)
slogrus.Panic("Panic error - will panic") // Calls panic()
Support for formatted strings:
slogrus.Infof("User %s logged in", username)
slogrus.Errorf("Failed to connect to %s:%d", host, port)
// With fields
slogrus.WithField("component", "auth").
Warnf("Failed login attempt for user %s", username)
Use slog handlers for advanced output formatting:
// JSON output
logger := slogrus.NewJSONLogger(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
})
// Text output with options
logger := slogrus.NewTextLogger(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
AddSource: true, // Include source file information
})
// Custom handler
handler := slog.NewJSONHandler(file, &slog.HandlerOptions{
Level: slog.LevelWarn,
})
logger := slogrus.NewWithHandler(handler)
For mixed usage scenarios and gradual migration:
// Create from existing slog logger
existingSlog := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger := slogrus.FromSlogLogger(existingSlog)
// Use logrus-style API
logger.WithField("component", "auth").Info("User logged in")
// Access underlying slog logger for advanced features
slogger := logger.GetSlogLogger()
slogger.Info("Direct slog usage",
"user", "john",
"action", "login",
slog.Group("metadata",
"ip", "192.168.1.1",
"agent", "curl/7.68.0",
),
)
Basic formatter compatibility for easier migration:
// Switch to JSON format
slogrus.SetFormatter(&slogrus.JSONFormatter{})
// Switch to text format
slogrus.SetFormatter(&slogrus.TextFormatter{
DisableColors: true,
FullTimestamp: true,
})
// Set global level
slogrus.SetLevel(slogrus.WarnLevel)
// Check if level is enabled
if slogrus.StandardLogger().IsLevelEnabled(slogrus.DebugLevel) {
// Expensive debug operation
slogrus.Debug("Detailed debug info")
}
// Parse level from string
level, err := slogrus.ParseLevel("info")
if err != nil {
log.Fatal(err)
}
slogrus.SetLevel(level)
// Replace this
import "github.com/sirupsen/logrus"
// With this
import "github.com/choria-io/slogrus"
Run your tests to ensure everything works as expected. The API is fully compatible.
Consider leveraging slog-specific features:
// Take advantage of slog's structured logging
logger := slogrus.NewJSONLogger(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
AddSource: true,
})
This library implements the complete logrus API:
- Types:
Logger
,Entry
,Level
,Fields
- Levels:
PanicLevel
,FatalLevel
,ErrorLevel
,WarnLevel
,InfoLevel
,DebugLevel
,TraceLevel
- Methods: All logging methods (
Info
,Debug
,Error
, etc.) - Formatted methods:
Infof
,Debugf
,Errorf
, etc. - Line methods:
Infoln
,Debugln
,Errorln
, etc. - Entry methods:
WithField
,WithFields
,WithError
,WithContext
,WithTime
- Global functions: All package-level logging functions
- Configuration:
SetLevel
,SetOutput
,SetFormatter
,SetReportCaller
By using Go's standard log/slog
, this library benefits from:
- Optimized structured logging implementation
- Efficient level checking
- Minimal allocations for disabled log levels
- Native JSON encoding
While maintaining API compatibility, there are some behavioral differences:
- Formatters: Formatter interfaces are implemented but actual formatting is handled by slog handlers
- Hooks: Hook system is not implemented (use slog handlers instead)
- Output types: Only
io.Writer
outputs are supported (not syslog, etc.)
- Go 1.23 or later (for
log/slog
support)
See the test files for comprehensive usage examples:
slogrus_test.go
- Basic functionality testslogger_test.go
- Logger-specific testsentry_test.go
- Entry and structured logging testsinit_test.go
- Initialization and global function tests
This was written with a focus on performance and low allocations, extensive benchmarks are included:
Benchmark | Iterations | Time per Op | Bytes per Op | Allocs per Op |
---|---|---|---|---|
LoggerInfo | 1,907,768 | 617.4 ns/op | 16 B/op | 1 allocs/op |
LoggerInfof | 1,743,793 | 692.6 ns/op | 32 B/op | 1 allocs/op |
LoggerInfoln | 1,906,810 | 639.4 ns/op | 16 B/op | 1 allocs/op |
LoggerWithField | 1,000,000 | 1,014 ns/op | 528 B/op | 6 allocs/op |
LoggerWithFieldf | 1,000,000 | 1,090 ns/op | 544 B/op | 7 allocs/op |
LoggerWithFields | 930,514 | 1,269 ns/op | 608 B/op | 6 allocs/op |
LoggerWithFieldChaining | 772,938 | 1,551 ns/op | 1,289 B/op | 11 allocs/op |
LoggerDebugDisabled | 494,086,148 | 2.428 ns/op | 0 B/op | 0 allocs/op |
LoggerWithFieldDebugDisabled | 6,154,128 | 202.3 ns/op | 464 B/op | 4 allocs/op |
GlobalInfo | 1,899,432 | 633.5 ns/op | 16 B/op | 1 allocs/op |
GlobalWithField | 1,000,000 | 1,073 ns/op | 528 B/op | 6 allocs/op |
LoggerInfoJSON | 2,346,199 | 508.7 ns/op | 16 B/op | 1 allocs/op |
LoggerWithFieldJSON | 1,336,527 | 888.2 ns/op | 528 B/op | 6 allocs/op |
LoggerWithError | 931,095 | 1,225 ns/op | 544 B/op | 7 allocs/op |
ComplexLogging | 586,546 | 1,962 ns/op | 881 B/op | 7 allocs/op |
MemoryAllocation/DirectLog | 1,874,785 | 637.9 ns/op | 16 B/op | 1 allocs/op |
MemoryAllocation/WithOneField | 1,000,000 | 1,072 ns/op | 536 B/op | 6 allocs/op |
MemoryAllocation/WithThreeFields | 750,601 | 1,572 ns/op | 1,297 B/op | 10 allocs/op |
MemoryAllocation/WithFieldsMap | 934,754 | 1,342 ns/op | 616 B/op | 6 allocs/op |
Throughput | 1,874,892 | 607.6 ns/op | 750 B/op (1.65 MB/s) | 6 allocs/op |
LevelCheck | 1,000,000,000 | 0.3733 ns/op | 0 B/op | 0 allocs/op |
FromSlogLogger | 1,820,745 | 657.6 ns/op | 24 B/op | 1 allocs/op |
GetSlogLogger | 2,019,850 | 597.1 ns/op | 0 B/op | 0 allocs/op |