go_logs v3 introduces significant improvements while maintaining 100% backward compatibility with v2. This guide helps you migrate at your own pace.
What’s new in v3
Structured logging Type-safe fields instead of printf-style formatting
Child loggers Pre-configured loggers with inherited fields
Context support Automatic trace_id extraction from context
Dual formatters TextFormatter for dev, JSONFormatter for production
File rotation Automatic log rotation by size with backup management
Extensible hooks Pluggable system for Slack, Sentry, metrics
Data redaction Automatic masking of sensitive fields
Multiple instances Create independent loggers with different configs
Migration strategy
We recommend a 3-phase gradual migration :
Phase 1: Drop-in replacement
Update dependency to v3, run existing tests, deploy and monitor. No code changes required.
Phase 2: Adopt new features incrementally
Use v3 API for new code
Migrate critical paths to structured logging
Add child loggers for request tracing
Enable JSON formatter for production
Phase 3: Full migration (optional)
Replace all v2 functions with v3 API
Use structured fields throughout
Remove unused v2 functions
Backward compatibility
All v2 code continues to work without changes in v3.
// v2 code - works in v3 without modification
go_logs . Init ()
defer go_logs . Close ()
go_logs . InfoLog ( "Server started" )
go_logs . Errorf ( "Connection failed: %v " , err )
Quick start
New projects
Existing projects
Use v3 API from the start: package main
import " github.com/drossan/go_logs "
func main () {
// Create logger with structured fields
logger , _ := go_logs . New (
go_logs . WithLevel ( go_logs . InfoLevel ),
go_logs . WithFormatter ( go_logs . NewJSONFormatter ()),
)
// Log with structured fields
logger . Info ( "Server started" ,
go_logs . String ( "host" , "localhost" ),
go_logs . Int ( "port" , 8080 ),
go_logs . Bool ( "debug" , false ),
)
}
No immediate changes required - v2 code works as-is: // This continues to work in v3
go_logs . Init ()
defer go_logs . Close ()
go_logs . InfoLog ( "Application started" )
go_logs . Errorf ( "Error: %v " , err )
Step-by-step migration
Step 1: Update dependency
# Update to v3
go get -u github.com/drossan/go_logs@v3
go mod tidy
Step 2: Verify existing tests
# Run your existing test suite
go test ./...
# All v2 tests should pass without changes
Step 3: Migrate to v3 API (gradual)
Replace printf-style logging
go_logs . Infof ( "User %s (ID: %d ) logged in from %s " , username , userID , ip )
Benefits:
Structured fields are queryable in log aggregators (ELK, Splunk)
Type-safe (compile-time checks)
No format string errors
Use child loggers for context
go_logs . Infof ( "Processing request for user %s " , username )
// ... many lines of code ...
go_logs . Infof ( "Request completed successfully" )
Benefits:
All logs automatically include request_id and user_id
No need to pass context manually
Better traceability
Use context for distributed tracing
// Context accepted but ignored
go_logs . InfoLogCtx ( ctx , "Processing request" )
// Only text format available
go_logs . InfoLog ( "Server started" )
// Output: [INFO] Server started
Use rotating file writer
# File grows indefinitely
export SAVE_LOG_FILE = 1
export LOG_FILE_NAME = app . log
Result:
app.log - Current log file
app.log.1 - First backup
app.log.2 - Second backup
app.log.3 - Third backup
Older backups automatically deleted
Enable data redaction
// Risk: Passwords logged in plain text
go_logs . Infof ( "User login: %s , password: %s " , username , password )
Configuration changes
Environment variables
v2 variables (still supported)
# File logging
SAVE_LOG_FILE = 1
LOG_FILE_NAME = app.log
LOG_FILE_PATH = /var/log
# Slack notifications (legacy)
NOTIFICATIONS_SLACK_ENABLED = 1
NOTIFICATION_FATAL_LOG = 1
NOTIFICATION_ERROR_LOG = 1
NOTIFICATION_WARNING_LOG = 1
NOTIFICATION_INFO_LOG = 1
NOTIFICATION_SUCCESS_LOG = 1
# Slack credentials
SLACK_TOKEN = xoxb-...
SLACK_CHANNEL_ID = C1234567890
v3 new variables
# Log level (replaces NOTIFICATION_*_LOG)
LOG_LEVEL = info # trace, debug, info, warn, error, fatal, silent
# Output format
LOG_FORMAT = json # text (default) or json
# File rotation
LOG_MAX_SIZE = 100 # MB (default: 100)
LOG_MAX_BACKUPS = 3 # Number of backups (default: 3)
# Data redaction
LOG_REDACT_KEYS = password,token,secret,api_key
The legacy notification system takes precedence when both are configured. To use the new system, don’t set NOTIFICATION_*_LOG variables.
Complete migration example
v2 (Original)
v3 (Drop-in)
v3 (Gradual)
v3 (Full)
package main
import " github.com/drossan/go_logs "
func handleRequest ( userID int , username string ) {
go_logs . Infof ( "User %s (ID: %d ) logged in" , username , userID )
if err := processUser ( userID ); err != nil {
go_logs . Errorf ( "Failed to process user %d : %v " , userID , err )
return
}
go_logs . Infof ( "User %d processed successfully" , userID )
}
package main
import " github.com/drossan/go_logs "
func handleRequest ( userID int , username string ) {
// No code changes - works as-is
go_logs . Infof ( "User %s (ID: %d ) logged in" , username , userID )
if err := processUser ( userID ); err != nil {
go_logs . Errorf ( "Failed to process user %d : %v " , userID , err )
return
}
go_logs . Infof ( "User %d processed successfully" , userID )
}
package main
import " github.com/drossan/go_logs "
var logger , _ = go_logs . New (
go_logs . WithLevel ( go_logs . InfoLevel ),
go_logs . WithFormatter ( go_logs . NewJSONFormatter ()),
)
func handleRequest ( userID int , username string ) {
// Mix of v2 and v3 API
logger . Info ( "user_logged_in" ,
go_logs . String ( "username" , username ),
go_logs . Int ( "user_id" , userID ),
)
if err := processUser ( userID ); err != nil {
// Still using v2 for error handling
go_logs . Errorf ( "Failed to process user %d : %v " , userID , err )
return
}
logger . Info ( "user_processed" ,
go_logs . Int ( "user_id" , userID ),
)
}
package main
import " github.com/drossan/go_logs "
var logger , _ = go_logs . New (
go_logs . WithLevel ( go_logs . InfoLevel ),
go_logs . WithFormatter ( go_logs . NewJSONFormatter ()),
)
func handleRequest ( userID int , username string ) {
// Fully migrated to v3
logger . Info ( "user_logged_in" ,
go_logs . String ( "username" , username ),
go_logs . Int ( "user_id" , userID ),
)
if err := processUser ( userID ); err != nil {
logger . Error ( "user_processing_failed" ,
go_logs . Int ( "user_id" , userID ),
go_logs . Err ( err ),
)
return
}
logger . Info ( "user_processed" ,
go_logs . Int ( "user_id" , userID ),
)
}
Best practices
Use structured fields
logger . Infof ( "User %s logged in from %s at %s " , user , ip , time )
Use child loggers for context
logger . Info ( "Request" , go_logs . String ( "request_id" , reqID ))
logger . Info ( "Processing" , go_logs . String ( "request_id" , reqID ))
logger . Info ( "Complete" , go_logs . String ( "request_id" , reqID ))
Enable redaction for sensitive data
logger . Info ( "Login" ,
go_logs . String ( "username" , user ),
go_logs . String ( "password" , pass ), // Password in plain text!
)
FAQ
Do I need to change my code to use v3?
No! v3 is 100% backward compatible with v2. Your existing code will work without changes.
Should I migrate to the v3 API?
We recommend gradual migration:
Keep v2 code for existing functionality
Use v3 API for new features
Migrate critical paths to structured logging
Migrate remaining code over time
Will v2 functions be removed?
No. v2 functions are permanently supported for backward compatibility.
Can I mix v2 and v3 APIs?
Yes! You can use both APIs in the same application: // v2 API
go_logs . InfoLog ( "Legacy code" )
// v3 API
logger . Info ( "New code" , go_logs . String ( "key" , "value" ))
How do I enable JSON logging?
Set environment variable: Or configure programmatically: logger , _ := go_logs . New (
go_logs . WithFormatter ( go_logs . NewJSONFormatter ()),
)
What's the performance impact of v3?
Next steps
Backward compatibility Learn about v2 API compatibility
Core concepts Explore v3 structured logging
Configuration Configure your logger
Examples See practical examples