FLog

Advanced logging framework providing quick and simple logging solution. Saves all logs to database and enables export as zip file. Provides functionality specialized for log collection and analysis in production apps.

Go LoggingGolangSimple LoggingFast LoggingFormatted Logging

Library

Flog

Overview

Flog is "a simple and fast logging library for Go language" that exists as diverse logging solutions with multiple implementations. The Facebook version is a library-optimized logging library derived from glog, the Coder version is a lightweight log for CLI applications emphasizing human-readable formatting, and the jellynian version is a high-speed log pursuing simplicity and performance, each with different characteristics and use cases.

Details

The Flog library family has evolved to address different needs in the Go language logging library ecosystem. Facebook's (now Meta) flog is a version that improved glog with library internal usage in mind, providing features such as environment variable configuration support, immediate use without flag.Parse(), and addition of DEBUG and CRITICAL levels. The Coder version is intended for use in CLI applications, emphasizing beautiful formatting and minimal design that are human-readable. The jellynian version is characterized by simplicity and high speed, implementing basic logging functionality in a lightweight manner.

Key Features

  • Multiple Implementations: Three major implementations - Facebook, Coder, and jellynian versions
  • Flexible Configuration: Configurable via environment variables or structures
  • Immediate Use: Start logging immediately without requiring flag.Parse()
  • Extended Log Levels: Additional support for DEBUG and CRITICAL levels
  • Format Optimization: Output emphasizing CLI and human readability
  • High Performance: Lightweight and fast log processing

Pros and Cons

Pros

  • High performance and easy integration with Go native implementation
  • Optimal choice possible from multiple implementations for project needs
  • Facebook version optimized for library internal integration
  • Coder version achieves human readability and beautiful output for CLI applications
  • jellynian version meets basic needs with simplicity and high speed
  • Flexibility with environment variable configuration and immediate usability

Cons

  • Confusion during selection and investigation costs due to multiple libraries with the same name
  • Portability and compatibility issues as APIs and features differ across implementations
  • Difficulty in unified information gathering due to scattered community support
  • Lack of complex log management features (rotation, aggregation, etc.)
  • Potential feature insufficiency for serious enterprise log management
  • Increased learning costs due to different documentation and examples for each implementation

Reference Pages

Code Examples

Basic Setup

// Facebook Flog (facebookincubator/flog)
package main

import (
    "github.com/facebookincubator/flog"
)

func main() {
    // Ready to use immediately without configuration
    flog.Info("Application started")
    
    // Set log level with environment variables
    // export FLOG_LEVEL=DEBUG
    flog.Debug("Debug information")
    flog.Critical("Critical system error")
}

// Coder Flog (coder/flog)
package main

import (
    "go.coder.com/flog"
)

func main() {
    // Format emphasizing human readability
    flog.Info("Application started successfully")
    flog.Warn("Memory usage is high")
    flog.Error("Failed to connect to database")
}

// jellynian Flog (jellynian/flog)
package main

import (
    "github.com/jellynian/flog"
)

func main() {
    // Simple and fast log output
    flog.Println("Simple log message")
    flog.Printf("User %s logged in", "john")
}

Basic Log Output

// Facebook Flog - Library-optimized
package main

import (
    "context"
    "github.com/facebookincubator/flog"
)

func processUser(ctx context.Context, userID string) {
    // Context-aware logging
    flog.InfoContext(ctx, "Processing user", "user_id", userID)
    
    // Output by log level
    flog.Debug("Detailed debug information")
    flog.Info("General information")
    flog.Warning("Warning message")
    flog.Error("Error occurred")
    flog.Critical("Critical system failure")
    
    // Structured logging
    flog.Info("User operation",
        "user_id", userID,
        "action", "login",
        "timestamp", time.Now(),
        "ip", "192.168.1.100",
    )
}

// Configuration customization
func configureLogging() {
    config := flog.Config{
        Level:  flog.LevelInfo,
        Format: flog.FormatJSON, // Output in JSON format
    }
    
    flog.Configure(config)
}

func main() {
    configureLogging()
    
    ctx := context.Background()
    processUser(ctx, "user-123")
}

Advanced Configuration (Format, Filtering)

// Coder Flog - Beautiful formatting for CLI applications
package main

import (
    "fmt"
    "os"
    "time"
    
    "go.coder.com/flog"
)

// Create custom logger
func setupCustomLogger() *flog.Logger {
    return flog.New(
        flog.OutputHuman(), // Output emphasizing human readability
        flog.LevelFilter(flog.LevelInfo), // Show only INFO and above
        flog.TimeFormat(time.RFC3339), // Time format setting
    )
}

// Logger with context
func createContextLogger(component string) *flog.Logger {
    return flog.New(
        flog.OutputHuman(),
        flog.LevelFilter(flog.LevelDebug),
    ).With(
        flog.F("component", component),
        flog.F("version", "1.0.0"),
    )
}

func main() {
    // Use custom logger
    logger := setupCustomLogger()
    
    logger.Info("Starting application",
        flog.F("pid", os.Getpid()),
        flog.F("env", os.Getenv("ENV")),
    )
    
    // Component-specific loggers
    dbLogger := createContextLogger("database")
    apiLogger := createContextLogger("api")
    
    dbLogger.Info("Database connection established",
        flog.F("host", "localhost"),
        flog.F("port", 5432),
    )
    
    apiLogger.Warn("API rate limit approaching",
        flog.F("current", 950),
        flog.F("limit", 1000),
    )
    
    // Include stack trace in error logs
    if err := processData(); err != nil {
        logger.Error("Failed to process data",
            flog.F("error", err),
            flog.F("stack", fmt.Sprintf("%+v", err)),
        )
    }
}

func processData() error {
    return fmt.Errorf("sample error for demonstration")
}

Error Handling

// jellynian Flog - Simple high-speed logging
package main

import (
    "errors"
    "fmt"
    "time"
    
    "github.com/jellynian/flog"
)

// Log function with error handling
func logWithErrorHandling(level, message string, args ...interface{}) {
    defer func() {
        if r := recover(); r != nil {
            // Fallback on panic
            fmt.Printf("[PANIC] Logging failed: %v\n", r)
            fmt.Printf("[FALLBACK] %s: %s\n", level, fmt.Sprintf(message, args...))
        }
    }()
    
    switch level {
    case "INFO":
        flog.Printf("[INFO] "+message, args...)
    case "WARN":
        flog.Printf("[WARN] "+message, args...)
    case "ERROR":
        flog.Printf("[ERROR] "+message, args...)
    default:
        flog.Printf("[LOG] "+message, args...)
    }
}

// Log with retry functionality
func logWithRetry(message string, maxRetries int) error {
    var lastErr error
    
    for i := 0; i <= maxRetries; i++ {
        func() {
            defer func() {
                if r := recover(); r != nil {
                    lastErr = fmt.Errorf("log panic: %v", r)
                }
            }()
            
            flog.Println(message)
            lastErr = nil // Clear error on success
        }()
        
        if lastErr == nil {
            return nil // Success
        }
        
        if i < maxRetries {
            time.Sleep(time.Duration(i+1) * 100 * time.Millisecond)
        }
    }
    
    return fmt.Errorf("failed to log after %d retries: %w", maxRetries, lastErr)
}

// Batch log processing
type LogEntry struct {
    Level     string
    Message   string
    Timestamp time.Time
    Fields    map[string]interface{}
}

func processBatchLogs(entries []LogEntry) {
    for _, entry := range entries {
        safeLog := func(e LogEntry) {
            defer func() {
                if r := recover(); r != nil {
                    fmt.Printf("[BATCH_LOG_ERROR] Failed to log entry: %v\n", r)
                }
            }()
            
            timestamp := entry.Timestamp.Format("2006-01-02 15:04:05")
            message := fmt.Sprintf("[%s] [%s] %s", timestamp, entry.Level, entry.Message)
            
            if len(entry.Fields) > 0 {
                message += fmt.Sprintf(" %+v", entry.Fields)
            }
            
            flog.Println(message)
        }
        
        safeLog(entry)
    }
}

func main() {
    // Safe log output
    logWithErrorHandling("INFO", "Application started at %v", time.Now())
    logWithErrorHandling("WARN", "Memory usage: %d MB", 512)
    
    // Log with retry
    if err := logWithRetry("Critical system message", 3); err != nil {
        fmt.Printf("Final log failure: %v\n", err)
    }
    
    // Batch log processing
    entries := []LogEntry{
        {
            Level:     "INFO",
            Message:   "User login",
            Timestamp: time.Now(),
            Fields:    map[string]interface{}{"user_id": 123, "ip": "192.168.1.1"},
        },
        {
            Level:     "ERROR",
            Message:   "Payment failed",
            Timestamp: time.Now(),
            Fields:    map[string]interface{}{"order_id": "ORD-456", "amount": 9800},
        },
    }
    
    processBatchLogs(entries)
}

Practical Examples (Integrated Log System)

// Universal logger integrating multiple Flog implementations
package main

import (
    "context"
    "fmt"
    "io"
    "os"
    "runtime"
    "sync"
    "time"
    
    // Import each Flog implementation
    facebooklog "github.com/facebookincubator/flog"
    coderlog "go.coder.com/flog"
    jellynianlog "github.com/jellynian/flog"
)

type LogLevel int

const (
    DEBUG LogLevel = iota
    INFO
    WARN
    ERROR
    CRITICAL
)

type LoggerType int

const (
    FacebookLogger LoggerType = iota
    CoderLogger
    JellynianLogger
)

// Integrated logger interface
type UniversalLogger struct {
    loggerType LoggerType
    level      LogLevel
    output     io.Writer
    mu         sync.RWMutex
    
    // Instances of each logger
    facebookLogger *facebooklog.Logger
    coderLogger    *coderlog.Logger
}

func NewUniversalLogger(loggerType LoggerType, level LogLevel) *UniversalLogger {
    ul := &UniversalLogger{
        loggerType: loggerType,
        level:      level,
        output:     os.Stderr,
    }
    
    switch loggerType {
    case FacebookLogger:
        // Facebook Flog configuration
        config := facebooklog.Config{
            Level: facebooklog.Level(level),
            Format: facebooklog.FormatJSON,
        }
        facebooklog.Configure(config)
        
    case CoderLogger:
        // Coder Flog configuration
        ul.coderLogger = coderlog.New(
            coderlog.OutputHuman(),
            coderlog.LevelFilter(coderlog.Level(level)),
            coderlog.TimeFormat(time.RFC3339),
        )
    }
    
    return ul
}

func (ul *UniversalLogger) log(ctx context.Context, level LogLevel, message string, fields ...interface{}) {
    ul.mu.RLock()
    defer ul.mu.RUnlock()
    
    if level < ul.level {
        return
    }
    
    // Get caller information
    _, file, line, _ := runtime.Caller(2)
    
    // Add common fields
    commonFields := []interface{}{
        "timestamp", time.Now(),
        "file", file,
        "line", line,
        "goroutine", runtime.NumGoroutine(),
    }
    
    allFields := append(commonFields, fields...)
    
    switch ul.loggerType {
    case FacebookLogger:
        switch level {
        case DEBUG:
            facebooklog.DebugContext(ctx, message, allFields...)
        case INFO:
            facebooklog.InfoContext(ctx, message, allFields...)
        case WARN:
            facebooklog.WarningContext(ctx, message, allFields...)
        case ERROR:
            facebooklog.ErrorContext(ctx, message, allFields...)
        case CRITICAL:
            facebooklog.CriticalContext(ctx, message, allFields...)
        }
        
    case CoderLogger:
        logger := ul.coderLogger
        for i := 0; i < len(allFields); i += 2 {
            if i+1 < len(allFields) {
                logger = logger.With(coderlog.F(fmt.Sprint(allFields[i]), allFields[i+1]))
            }
        }
        
        switch level {
        case DEBUG:
            logger.Debug(message)
        case INFO:
            logger.Info(message)
        case WARN:
            logger.Warn(message)
        case ERROR:
            logger.Error(message)
        case CRITICAL:
            logger.Error("[CRITICAL] " + message)
        }
        
    case JellynianLogger:
        // jellynian flog uses simple Printf style
        levelStr := map[LogLevel]string{
            DEBUG:    "DEBUG",
            INFO:     "INFO",
            WARN:     "WARN",
            ERROR:    "ERROR",
            CRITICAL: "CRITICAL",
        }
        
        timestamp := time.Now().Format("2006-01-02 15:04:05")
        logLine := fmt.Sprintf("[%s] [%s] %s", timestamp, levelStr[level], message)
        
        if len(fields) > 0 {
            logLine += fmt.Sprintf(" %+v", fields)
        }
        
        jellynianlog.Println(logLine)
    }
}

// Convenience methods
func (ul *UniversalLogger) Debug(ctx context.Context, message string, fields ...interface{}) {
    ul.log(ctx, DEBUG, message, fields...)
}

func (ul *UniversalLogger) Info(ctx context.Context, message string, fields ...interface{}) {
    ul.log(ctx, INFO, message, fields...)
}

func (ul *UniversalLogger) Warn(ctx context.Context, message string, fields ...interface{}) {
    ul.log(ctx, WARN, message, fields...)
}

func (ul *UniversalLogger) Error(ctx context.Context, message string, fields ...interface{}) {
    ul.log(ctx, ERROR, message, fields...)
}

func (ul *UniversalLogger) Critical(ctx context.Context, message string, fields ...interface{}) {
    ul.log(ctx, CRITICAL, message, fields...)
}

// Performance monitoring functionality
func (ul *UniversalLogger) WithTiming(ctx context.Context, operation string, fn func() error) error {
    start := time.Now()
    
    ul.Debug(ctx, "Operation started", "operation", operation)
    
    err := fn()
    
    duration := time.Since(start)
    
    if err != nil {
        ul.Error(ctx, "Operation failed",
            "operation", operation,
            "duration", duration,
            "error", err,
        )
    } else {
        ul.Info(ctx, "Operation completed",
            "operation", operation,
            "duration", duration,
        )
    }
    
    return err
}

func main() {
    // Test each logger
    ctx := context.Background()
    
    // Test with Facebook Flog
    fbLogger := NewUniversalLogger(FacebookLogger, DEBUG)
    fbLogger.Info(ctx, "Testing Facebook Flog", "component", "main")
    
    // Test with Coder Flog
    coderLogger := NewUniversalLogger(CoderLogger, INFO)
    coderLogger.Warn(ctx, "Testing Coder Flog", "component", "main")
    
    // Test with jellynian Flog
    jellynianLogger := NewUniversalLogger(JellynianLogger, DEBUG)
    jellynianLogger.Error(ctx, "Testing jellynian Flog", "component", "main")
    
    // Test performance monitoring
    err := fbLogger.WithTiming(ctx, "database_query", func() error {
        time.Sleep(100 * time.Millisecond) // Dummy processing
        return nil
    })
    
    if err != nil {
        fbLogger.Critical(ctx, "Critical error in main", "error", err)
    }
}