log4go
Go logging library inspired by Java log4j. Provides configuration methods and API design familiar to Java developers. Traditional configuration-based logging framework applying concepts like appenders, layouts, and filters to Go.
Library
log4go
Overview
log4go is a "Go logging library inspired by Java log4j," developed as a configuration-based logging framework. It provides configuration methods and API design familiar to Java developers, applying concepts like appenders, layouts, and filters to Go. This traditional configuration-based logging framework supports level-based logging and advanced configurability, designed to handle complex logging requirements.
Details
log4go 2025 edition provides a Java log4j-like logging experience in the Go ecosystem as a specialized library. Multiple implementations exist, with various forks developed based on alecthomas/log4go, each providing unique extensions. It features XML/JSON configuration file support, category-based output, log rotation functionality, and other enterprise-level log management capabilities. While adoption examples exist in teams with Java development backgrounds, other choices tend to be prioritized in modern Go development.
Key Features
- Java log4j Compatible API: Configuration methods and API design familiar to Java developers
- Level-based Log Control: Finest, Fine, Debug, Trace, Info, Warning, Error, Critical
- Multiple Output Destinations: Support for console, file, and network output
- Configuration File Support: Detailed control through XML and JSON format configuration files
- Log Rotation: Size and time-based log file management
- Category-based Processing: Output control per different categories
Pros and Cons
Pros
- Low learning cost for Java developers due to API design similar to Java log4j
- Rich configuration options and flexible appender system
- External configuration management possible through XML or JSON configuration files
- Detailed log level control per category
- File rotation and simultaneous output to multiple destinations
- Capable of handling complex log requirements in Enterprise environments
Cons
- Insufficient adaptation to modern Go conventions with maintainability issues
- Performance inferior to slog, Zap, and Zerolog
- Insufficient official documentation with limited usage examples
- Multiple forks exist making it difficult to determine which to choose
- Low recommendation for new projects with limited community support
- Existence significance diminished with the emergence of Go standard library slog
Reference Pages
Usage Examples
Installation and Basic Setup
# Install log4go (alecthomas version)
go get github.com/alecthomas/log4go
# Or enhanced version (jeanphorn version)
go get github.com/jeanphorn/log4go
# Check dependencies in project
go mod tidy
# Verify in Go environment
go list -m github.com/alecthomas/log4go
Basic Logger Creation and Usage
package main
import (
"time"
l4g "github.com/alecthomas/log4go"
)
func main() {
// Create new Logger
log := l4g.NewLogger()
defer log.Close()
// Add console appender (DEBUG level and above)
log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter())
// Add file appender (INFO level and above)
log.AddFilter("file", l4g.INFO, l4g.NewFileLogWriter("application.log", true))
// Output examples by log level
log.Finest("Most detailed info: %s", "database connection details")
log.Fine("Detailed info: %s", "request processing start")
log.Debug("Debug info: %s", "variable value check")
log.Trace("Trace info: %s", "function call")
log.Info("Info: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
log.Warn("Warning: %s", "memory usage exceeds 80%")
log.Error("Error: %s", "database connection failed")
log.Critical("Critical error: %s", "system shutdown")
// Structured logging (message and properties)
log.Info("User login: UserID=%d, IP=%s", 12345, "192.168.1.100")
log.Error("Process failed: TaskID=%s, Error=%v", "task-001", "timeout error")
}
Advanced Configuration with XML Configuration File
<!-- log4go.xml -->
<logging>
<filter enabled="true">
<tag>stdout</tag>
<type>console</type>
<level>DEBUG</level>
</filter>
<filter enabled="true">
<tag>file</tag>
<type>file</type>
<level>INFO</level>
<property name="filename">logs/application.log</property>
<property name="format">[%D %T] [%L] (%S) %M</property>
<property name="rotate">true</property>
<property name="maxsize">100M</property>
<property name="maxlines">1000000</property>
<property name="daily">true</property>
</filter>
<filter enabled="true">
<tag>error_file</tag>
<type>file</type>
<level>ERROR</level>
<property name="filename">logs/error.log</property>
<property name="format">[%D %T] [%L] (%S) %M</property>
</filter>
<filter enabled="true">
<tag>network</tag>
<type>socket</type>
<level>WARN</level>
<property name="endpoint">192.168.1.200:9999</property>
<property name="protocol">udp</property>
</filter>
</logging>
package main
import (
l4g "github.com/alecthomas/log4go"
)
func main() {
// Load logger configuration from XML configuration file
l4g.LoadConfiguration("log4go.xml")
defer l4g.Close()
// Use global logger
l4g.Debug("Application start")
l4g.Info("Configuration file loading complete")
l4g.Warn("Warning: configuration values differ from defaults")
l4g.Error("Error: external API access failed")
// Create and use custom logger
customLog := l4g.NewLogger()
customLog.LoadConfiguration("custom-log4go.xml")
defer customLog.Close()
customLog.Info("Output from custom logger")
}
Log Rotation and File Management
package main
import (
"time"
l4g "github.com/alecthomas/log4go"
)
func main() {
log := l4g.NewLogger()
defer log.Close()
// File logger with rotation
flw := l4g.NewFileLogWriter("logs/rotating.log", true)
// Rotation settings
flw.SetRotateSize(1024 * 1024) // Rotate at 1MB
flw.SetRotateLines(10000) // Rotate at 10,000 lines
flw.SetRotateDaily(true) // Daily rotation
log.AddFilter("rotating_file", l4g.INFO, flw)
// Network logger (syslog-style)
socketWriter := l4g.NewSocketLogWriter("tcp", "192.168.1.100:514")
log.AddFilter("network", l4g.WARN, socketWriter)
// Simultaneous log output to multiple outputs
for i := 0; i < 100; i++ {
log.Info("Log output test: Number=%d, Timestamp=%s",
i, time.Now().Format(time.RFC3339))
if i%10 == 0 {
log.Warn("Warning log every 10 items: Progress=%d%%", i)
}
time.Sleep(100 * time.Millisecond)
}
log.Info("Log output complete")
}
Category-based Log Control and Filtering
package main
import (
l4g "github.com/alecthomas/log4go"
)
// Category-based logger management
type LoggerManager struct {
appLogger l4g.Logger
dbLogger l4g.Logger
apiLogger l4g.Logger
systemLogger l4g.Logger
}
func NewLoggerManager() *LoggerManager {
lm := &LoggerManager{
appLogger: l4g.NewLogger(),
dbLogger: l4g.NewLogger(),
apiLogger: l4g.NewLogger(),
systemLogger: l4g.NewLogger(),
}
// Application logging
lm.appLogger.AddFilter("app_console", l4g.INFO, l4g.NewConsoleLogWriter())
lm.appLogger.AddFilter("app_file", l4g.DEBUG,
l4g.NewFileLogWriter("logs/application.log", true))
// Database logging (detailed level)
lm.dbLogger.AddFilter("db_file", l4g.FINE,
l4g.NewFileLogWriter("logs/database.log", true))
// API logging (request/response)
lm.apiLogger.AddFilter("api_file", l4g.INFO,
l4g.NewFileLogWriter("logs/api.log", true))
// System logging (errors only)
lm.systemLogger.AddFilter("system_file", l4g.ERROR,
l4g.NewFileLogWriter("logs/system.log", true))
return lm
}
func (lm *LoggerManager) Close() {
lm.appLogger.Close()
lm.dbLogger.Close()
lm.apiLogger.Close()
lm.systemLogger.Close()
}
func main() {
logManager := NewLoggerManager()
defer logManager.Close()
// Category-based log output
logManager.appLogger.Info("Application start")
logManager.dbLogger.Fine("Database connection: host=%s", "localhost:5432")
logManager.apiLogger.Info("API request: GET /api/users")
logManager.systemLogger.Error("System error: out of memory")
// Business process simulation
processUserRequest(logManager)
processDatabaseQuery(logManager)
processAPICall(logManager)
}
func processUserRequest(lm *LoggerManager) {
lm.appLogger.Info("User request processing start")
lm.appLogger.Debug("Session check: UserID=12345")
lm.appLogger.Info("User request processing complete")
}
func processDatabaseQuery(lm *LoggerManager) {
lm.dbLogger.Fine("Query execution start: SELECT * FROM users WHERE active = 1")
lm.dbLogger.Debug("Query execution time: 120ms")
lm.dbLogger.Info("Query execution complete: Result count=1500")
}
func processAPICall(lm *LoggerManager) {
lm.apiLogger.Info("External API call: https://api.example.com/data")
lm.apiLogger.Warn("API response delay: Response time=5.2 seconds")
lm.apiLogger.Info("API call complete: Status=200")
}
Error Handling and Performance Considerations
package main
import (
"errors"
"fmt"
"runtime"
"time"
l4g "github.com/alecthomas/log4go"
)
func main() {
log := l4g.NewLogger()
defer log.Close()
// Asynchronous file writing configuration
flw := l4g.NewFileLogWriter("logs/performance.log", true)
log.AddFilter("perf_file", l4g.INFO, flw)
// Log operations with error handling
if err := safeLogging(log); err != nil {
fmt.Printf("Log operation error: %v\n", err)
}
// Performance test
performanceTest(log)
// Memory usage monitoring
monitorMemoryUsage(log)
}
func safeLogging(log l4g.Logger) error {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Panic occurred in log operation: %v\n", r)
}
}()
// Log output for various data types
log.Info("String log: %s", "test message")
log.Info("Numeric log: %d", 12345)
log.Info("Float log: %.2f", 123.456)
log.Info("Boolean log: %t", true)
// Detailed error information logging
err := errors.New("sample error")
log.Error("Error occurred: %v", err)
// Stack trace information (manual retrieval)
_, file, line, ok := runtime.Caller(0)
if ok {
log.Debug("Caller info: File=%s, Line=%d", file, line)
}
return nil
}
func performanceTest(log l4g.Logger) {
start := time.Now()
// Performance test with massive log output
for i := 0; i < 1000; i++ {
log.Info("Performance test: Number=%d", i)
if i%100 == 0 {
elapsed := time.Since(start)
log.Debug("Progress: %d/1000, Elapsed time=%v", i, elapsed)
}
}
totalElapsed := time.Since(start)
log.Info("Performance test complete: Total execution time=%v", totalElapsed)
}
func monitorMemoryUsage(log l4g.Logger) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
log.Info("Memory usage: Alloc=%d KB, TotalAlloc=%d KB, Sys=%d KB",
m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024)
log.Info("GC count: %d times", m.NumGC)
log.Info("Goroutine count: %d", runtime.NumGoroutine())
}
Practical Application Integration Example
package main
import (
"fmt"
"net/http"
"os"
"time"
l4g "github.com/alecthomas/log4go"
)
// Application-wide logger configuration
type Application struct {
logger l4g.Logger
server *http.Server
}
func NewApplication() *Application {
app := &Application{}
app.setupLogging()
app.setupHTTPServer()
return app
}
func (app *Application) setupLogging() {
app.logger = l4g.NewLogger()
// Log level control by environment variables
logLevel := l4g.INFO
if level := os.Getenv("LOG_LEVEL"); level != "" {
switch level {
case "DEBUG":
logLevel = l4g.DEBUG
case "WARN":
logLevel = l4g.WARNING
case "ERROR":
logLevel = l4g.ERROR
}
}
// Console output (development environment)
if os.Getenv("ENV") == "development" {
app.logger.AddFilter("console", logLevel, l4g.NewConsoleLogWriter())
}
// File output (production environment)
app.logger.AddFilter("file", logLevel,
l4g.NewFileLogWriter("logs/app.log", true))
// Error-specific file
app.logger.AddFilter("error_file", l4g.ERROR,
l4g.NewFileLogWriter("logs/error.log", true))
app.logger.Info("Logger configuration complete: Log level=%v", logLevel)
}
func (app *Application) setupHTTPServer() {
mux := http.NewServeMux()
// Handlers with middleware
mux.HandleFunc("/api/users", app.withLogging(app.handleUsers))
mux.HandleFunc("/api/health", app.withLogging(app.handleHealth))
app.server = &http.Server{
Addr: ":8080",
Handler: mux,
}
}
func (app *Application) withLogging(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
app.logger.Info("HTTP request start: %s %s from %s",
r.Method, r.URL.Path, r.RemoteAddr)
// Request processing
handler(w, r)
elapsed := time.Since(start)
app.logger.Info("HTTP request complete: %s %s, Processing time=%v",
r.Method, r.URL.Path, elapsed)
}
}
func (app *Application) handleUsers(w http.ResponseWriter, r *http.Request) {
app.logger.Debug("User list retrieval processing start")
// Business logic simulation
time.Sleep(100 * time.Millisecond)
response := `{"users": [{"id": 1, "name": "John Doe"}]}`
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(response))
app.logger.Debug("User list retrieval processing complete")
}
func (app *Application) handleHealth(w http.ResponseWriter, r *http.Request) {
app.logger.Debug("Health check processing")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "healthy"}`))
}
func (app *Application) Start() error {
app.logger.Info("HTTP server start: Address=%s", app.server.Addr)
return app.server.ListenAndServe()
}
func (app *Application) Shutdown() {
app.logger.Info("Application shutdown processing start")
app.logger.Close()
}
func main() {
app := NewApplication()
defer app.Shutdown()
app.logger.Info("Application start")
if err := app.Start(); err != nil {
app.logger.Error("Server start error: %v", err)
fmt.Printf("Fatal error: %v\n", err)
os.Exit(1)
}
}