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.

loggingGoconfiguration-basedJava-inspiredlevel-based loggingappenders

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)
    }
}