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