Files
log/doc/adapters.md

7.7 KiB

Compatibility Adapters

Guide to using lixenwraith/log with popular Go networking frameworks through compatibility adapters.

Overview

The compat package provides adapters that allow the lixenwraith/log logger to work seamlessly with:

  • gnet v2: High-performance event-driven networking framework
  • fasthttp: Fast HTTP implementation

Features

  • Full interface compatibility
  • Preserves structured logging
  • Configurable behavior
  • Shared logger instances
  • Optional field extraction

gnet Adapter

Basic Usage

import (
    "github.com/lixenwraith/log"
    "github.com/lixenwraith/log/compat"
    "github.com/panjf2000/gnet/v2"
)

// Create logger
logger := log.NewLogger()
cfg := log.DefaultConfig()
cfg.Directory = "/var/log/gnet"
logger.ApplyConfig(cfg)
defer logger.Shutdown()

// Create adapter
adapter := compat.NewGnetAdapter(logger)

// Use with gnet
gnet.Run(eventHandler, "tcp://127.0.0.1:9000", 
    gnet.WithLogger(adapter),
)

gnet Interface Implementation

The adapter implements all gnet logger methods:

type GnetAdapter struct {
    logger *log.Logger
}

// Methods implemented:
// - Debugf(format string, args ...any)
// - Infof(format string, args ...any)
// - Warnf(format string, args ...any)
// - Errorf(format string, args ...any)
// - Fatalf(format string, args ...any)

Custom Fatal Behavior

Override default fatal handling:

adapter := compat.NewGnetAdapter(logger,
    compat.WithFatalHandler(func(msg string) {
        // Custom cleanup
        saveApplicationState()
        notifyOperations(msg)
        gracefulShutdown()
        os.Exit(1)
    }),
)

Complete gnet Example

type echoServer struct {
    gnet.BuiltinEventEngine
    logger gnet.Logger
}

func (es *echoServer) OnBoot(eng gnet.Engine) gnet.Action {
    es.logger.Infof("Server started on %s", eng.Addrs)
    return gnet.None
}

func (es *echoServer) OnTraffic(c gnet.Conn) gnet.Action {
    buf, _ := c.Next(-1)
    es.logger.Debugf("Received %d bytes from %s", len(buf), c.RemoteAddr())
    c.Write(buf)
    return gnet.None
}

func main() {
    logger := log.NewLogger()
	cfg := log.DefaultConfig()
    cfg.Directory = "/var/log/gnet"
	cfg.Format = "json"
	cfg.BufferSize = 2048
	logger.ApplyConfig(cfg)
    defer logger.Shutdown()
    
    adapter := compat.NewGnetAdapter(logger)
    
    gnet.Run(
        &echoServer{logger: adapter},
        "tcp://127.0.0.1:9000",
        gnet.WithMulticore(true),
        gnet.WithLogger(adapter),
    )
}

fasthttp Adapter

Basic Usage

import (
    "github.com/lixenwraith/log"
    "github.com/lixenwraith/log/compat"
    "github.com/valyala/fasthttp"
)

// Create logger
logger := log.NewLogger()
cfg := log.DefaultConfig()
cfg.Directory = "/var/log/fasthttp"
logger.ApplyConfig(cfg)
defer logger.Shutdown()

// Create adapter
adapter := compat.NewFastHTTPAdapter(logger)

// Configure server
server := &fasthttp.Server{
    Handler: requestHandler,
    Logger:  adapter,
}

Level Detection

The adapter automatically detects log levels from message content:

// Default detection rules:
// - Contains "error", "failed", "fatal", "panic" → ERROR
// - Contains "warn", "warning", "deprecated" → WARN  
// - Contains "debug", "trace" → DEBUG
// - Otherwise → INFO

Custom Level Detection

adapter := compat.NewFastHTTPAdapter(logger,
    compat.WithDefaultLevel(log.LevelInfo),
    compat.WithLevelDetector(func(msg string) int64 {
        // Custom detection logic
        if strings.Contains(msg, "CRITICAL") {
            return log.LevelError
        }
        if strings.Contains(msg, "performance") {
            return log.LevelWarn
        }
        // Return 0 to use the adapter's default log level (log.LevelInfo by default)
        return 0
    }),
)

Builder Pattern

Share a configured logger across adapters:

// Create and configure your main logger
logger := log.NewLogger()
cfg := log.DefaultConfig()
cfg.Level = log.LevelDebug
logger.ApplyConfig(cfg)
logger.Start()
defer logger.Shutdown()

// Create builder with existing logger
builder := compat.NewBuilder().WithLogger(logger)

// Build adapters
gnetAdapter, _ := builder.BuildGnet()
if err != nil { return err }

fasthttpAdapter, _ := builder.BuildFastHTTP()
if err != nil { return err }

Creating New Logger

Let the builder create a logger with config:

// Option 1: With custom config
cfg := log.DefaultConfig()
cfg.Directory = "/var/log/app"
builder := compat.NewBuilder().WithConfig(cfg)

// Option 2: Default config (created on first build)
builder := compat.NewBuilder()
if err != nil { return err }

// Build adapters
gnetAdapter, _ := builder.BuildGnet()
logger, _ := builder.GetLogger() // Retrieve for direct use

Structured gnet Adapter

Extract fields from printf-style formats:

structuredAdapter, _ := builder.BuildStructuredGnet()
// "client=%s port=%d" → {"client": "...", "port": ...}

Structured Logging

Field Extraction

Structured adapters can extract fields from printf-style formats:

// Regular adapter output:
// "client=192.168.1.1 port=8080"

// Structured adapter output:
// {"client": "192.168.1.1", "port": 8080, "source": "gnet"}

Pattern Detection

The structured adapter recognizes patterns like:

  • key=%v
  • key: %v
  • key = %v
adapter := compat.NewStructuredGnetAdapter(logger)

// These will extract structured fields:
adapter.Infof("client=%s port=%d", "192.168.1.1", 8080)
// → {"client": "192.168.1.1", "port": 8080}

adapter.Errorf("user: %s, error: %s", "john", "auth failed")
// → {"user": "john", "error": "auth failed"}

// These remain as messages:
adapter.Infof("Connected to server")
// → {"msg": "Connected to server"}

Integration Examples

Microservice with Both Frameworks

type Service struct {
    gnetAdapter     *compat.GnetAdapter
    fasthttpAdapter *compat.FastHTTPAdapter
    logger          *log.Logger
}

func NewService() (*Service, error) {
    // Create and configure logger
    logger := log.NewLogger()
    cfg := log.DefaultConfig()
    cfg.Directory = "/var/log/service"
    cfg.Format = "json"
    cfg.HeartbeatLevel = 2
    if err := logger.ApplyConfig(cfg); err != nil {
        return nil, err
    }
    if err := logger.Start(); err != nil {
        return nil, err
    }

    // Create builder with the logger
    builder := compat.NewBuilder().WithLogger(logger)

    // Build adapters
    gnetAdapter, err := builder.BuildGnet()
    if err != nil {
        logger.Shutdown()
        return nil, err
    }

    fasthttpAdapter, err := builder.BuildFastHTTP()
    if err != nil {
        logger.Shutdown()
        return nil, err
    }

    return &Service{
        gnetAdapter:     gnetAdapter,
        fasthttpAdapter: fasthttpAdapter,
        logger:          logger,
    }, nil
}

Middleware Integration

// gnet middleware
func loggingMiddleware(adapter *compat.GnetAdapter) gnet.EventHandler {
    return func(c gnet.Conn) gnet.Action {
        start := time.Now()
        addr := c.RemoteAddr()
        
        // Process connection
        action := next(c)
        
        adapter.Infof("conn_duration=%v remote=%s action=%v",
            time.Since(start), addr, action)
        
        return action
    }
}

// fasthttp middleware
func requestLogger(adapter *compat.FastHTTPAdapter) fasthttp.RequestHandler {
    return func(ctx *fasthttp.RequestCtx) {
        start := time.Now()
        
        // Process request
        next(ctx)
        
        // Adapter will detect level from status
        adapter.Printf("method=%s path=%s status=%d duration=%v",
            ctx.Method(), ctx.Path(), 
            ctx.Response.StatusCode(),
            time.Since(start))
    }
}