logger

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 27, 2025 License: MIT Imports: 5 Imported by: 0

README

logger Package

Structured JSON logging with zerolog for LaResto microservices. Logs are output to stdout for collection by Elasticsearch/Kibana.

Features

  • Structured JSON Logs: Machine-readable, queryable logs
  • Multiple Log Levels: Debug, Info, Warn, Error, Fatal
  • Context Propagation: Automatic request ID and user ID extraction
  • Type-Safe Fields: Optimized logging for common types
  • Zero Allocation: Uses zerolog for high performance
  • Permanent Fields: Attach fields to logger instances

Installation

import "github.com/LaRestoOU/laresto-go-common/pkg/logger"

Quick Start

Basic Usage
// Create a logger
log := logger.New(logger.Config{
    Level:       "info",
    ServiceName: "auth-service",
    Environment: "production",
})

// Log messages
log.Info("User logged in", "user_id", "123", "email", "user@example.com")
log.Warn("Rate limit approaching", "current", 95, "limit", 100)
log.Error("Database connection failed", err, "retry_count", 3)
Development Logger
// Use default configuration for development
log := logger.NewDefault()
log.Debug("Starting authentication process")

Log Levels

Level Use Case Example
Debug Development, detailed debugging log.Debug("SQL query", "query", sql)
Info Important events, user actions log.Info("User logged in", "user_id", id)
Warn Warning conditions, recoverable issues log.Warn("Cache miss", "key", key)
Error Error conditions, failures log.Error("Payment failed", err)
Fatal Critical errors, process exits log.Fatal("Database unreachable", err)

Configuration

cfg := logger.Config{
    // Minimum log level (debug, info, warn, error)
    Level: "info",
    
    // Service name (appears in all logs)
    ServiceName: "auth-service",
    
    // Environment (development, staging, production)
    Environment: "production",
    
    // Output writer (defaults to os.Stdout)
    Output: os.Stdout,
}

log := logger.New(cfg)

Context-Aware Logging

Extract request ID and user ID from context automatically:

func HandleRequest(c *gin.Context) {
    // Create context-aware logger
    log := logger.New(config).WithContext(c.Request.Context())
    
    // All logs include request_id and user_id
    log.Info("Processing order")
    log.Error("Validation failed", err)
}

// Output:
// {"level":"info","request_id":"abc-123","user_id":"456","message":"Processing order"}

Permanent Fields

Attach fields that appear in all logs:

// Single field
moduleLogger := log.WithField("module", "payment")
moduleLogger.Info("Processing payment") // includes "module":"payment"

// Multiple fields
authLogger := log.WithFields(
    "module", "auth",
    "version", "v2",
)
authLogger.Info("Login attempt") // includes both fields

Field Types

The logger optimizes for common types:

log.Info("Request completed",
    "user_id", "123",              // string
    "status_code", 200,            // int
    "duration_ms", int64(150),     // int64
    "success", true,               // bool
    "duration", 150*time.Millisecond, // duration
    "timestamp", time.Now(),       // time
    "error", err,                  // error
    "metadata", map[string]string{...}, // any other type
)

Usage Patterns

Service Initialization
func main() {
    log := logger.New(logger.Config{
        Level:       getEnv("LOG_LEVEL", "info"),
        ServiceName: "auth-service",
        Environment: getEnv("ENVIRONMENT", "production"),
    })
    
    log.Info("Service starting",
        "port", 8080,
        "version", "v1.0.0",
    )
    
    // Pass logger to handlers
    handler := NewAuthHandler(log)
}
HTTP Request Logging
func (h *Handler) Login(c *gin.Context) {
    log := h.logger.WithContext(c.Request.Context())
    
    var req LoginRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        log.Warn("Invalid login request", "error", err)
        c.JSON(400, gin.H{"error": "Invalid request"})
        return
    }
    
    log.Info("Login attempt", "email", req.Email)
    
    user, err := h.authService.Login(req.Email, req.Password)
    if err != nil {
        log.Error("Login failed", err, "email", req.Email)
        c.JSON(401, gin.H{"error": "Unauthorized"})
        return
    }
    
    log.Info("Login successful", "user_id", user.ID)
    c.JSON(200, user)
}
Background Processing
func (w *Worker) ProcessOrder(ctx context.Context, order Order) {
    log := w.logger.WithContext(ctx).WithField("order_id", order.ID)
    
    log.Info("Processing order started")
    
    if err := w.validateOrder(order); err != nil {
        log.Error("Order validation failed", err)
        return
    }
    
    log.Info("Order validated")
    
    if err := w.processPayment(order); err != nil {
        log.Error("Payment processing failed", err)
        return
    }
    
    log.Info("Order processing completed",
        "duration", time.Since(order.CreatedAt),
    )
}

Log Output Format

All logs are JSON-formatted:

{
  "level": "info",
  "service": "auth-service",
  "environment": "production",
  "time": "2024-12-27T10:30:00Z",
  "message": "User logged in",
  "request_id": "abc-123",
  "user_id": "456",
  "email": "user@example.com"
}

Best Practices

DO ✅
// Use structured fields
log.Info("User logged in", "user_id", userID, "ip", ipAddress)

// Include context
log := logger.WithContext(ctx)

// Use appropriate levels
log.Debug("Cache hit", "key", key)  // Development only
log.Info("Order created", "order_id", id)  // Important events
log.Error("Payment failed", err, "order_id", id)  // Errors

// Add permanent fields for modules
paymentLog := log.WithField("module", "payment")
DON'T ❌
// Don't log sensitive data
log.Info("Login", "password", password)  // NEVER!
log.Info("Payment", "card_number", card)  // NEVER!
log.Info("Token", "jwt", token)  // NEVER!

// Don't use string concatenation
log.Info("User " + userID + " logged in")  // Use fields instead

// Don't log at wrong level
log.Error("User logged in", nil)  // Not an error!
log.Debug("Payment failed", err)  // Should be Error!

// Don't log too much in production
log.Debug("Processing...", "step", 1)  // Set level to "info" in prod

Sensitive Data Guidelines

NEVER log:

  • Passwords (plain or hashed)
  • JWT tokens or session IDs
  • Credit card numbers
  • Personal identification numbers
  • API keys or secrets
  • Full email addresses in production (hash or mask them)

Safe to log:

  • User IDs (numeric or UUID)
  • Request IDs
  • Timestamps
  • Status codes
  • Durations
  • Error messages (without sensitive context)

Integration with ELK Stack

Logs are written to stdout in JSON format and collected by Docker's logging driver:

# docker-compose.yml
services:
  auth-service:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Logs flow: Service → Docker → Filebeat → Elasticsearch → Kibana

Querying Logs in Kibana

# Find all errors for a specific user
level:"error" AND user_id:"123"

# Find slow requests (>1 second)
duration_ms:>1000

# Find logs for a specific request
request_id:"abc-123"

# Find authentication failures
service:"auth-service" AND message:"Login failed"

Performance Considerations

  • Zero Allocation: zerolog doesn't allocate memory during logging
  • Lazy Evaluation: Log fields only serialized if level is enabled
  • Buffered I/O: Logs are buffered before writing to stdout
  • Type Optimization: Common types have optimized serialization paths

Benchmarks (from zerolog):

  • ~50ns per log line (with 10 fields)
  • Zero allocations for basic types
  • 10x faster than standard library log

Testing

func TestHandler_Login(t *testing.T) {
    // Use a buffer to capture logs
    buf := &bytes.Buffer{}
    log := logger.New(logger.Config{
        Level:  "debug",
        Output: buf,
    })
    
    handler := NewHandler(log)
    
    // Test handler
    handler.Login(ctx, req)
    
    // Verify logs
    logs := buf.String()
    if !strings.Contains(logs, "Login successful") {
        t.Error("Expected login success log")
    }
}

iOS Developer Notes

Logger is similar to:

  • OSLog in iOS - structured logging framework
  • Custom logging wrapper around print()
  • Analytics/crash reporting SDKs

Comparison:

// iOS OSLog
os_log(.info, log: logger, "User %{public}@ logged in", userID)

// Go logger
log.Info("User logged in", "user_id", userID)

Key concepts:

  • Log levels = OSLog's .debug, .info, .error, .fault
  • Structured fields = Like passing dictionaries to analytics
  • Context = Similar to MDC (Mapped Diagnostic Context)
  • JSON output = Like serializing to Crashlytics/Firebase

API Reference

Types
type Logger struct {
    // Internal zerolog instance
}

type Config struct {
    Level       string    // "debug", "info", "warn", "error"
    ServiceName string    // Service identifier
    Environment string    // "development", "staging", "production"
    Output      io.Writer // Where to write logs (default: os.Stdout)
}
Functions
// New creates a configured logger
func New(cfg Config) *Logger

// NewDefault creates a development logger
func NewDefault() *Logger
Methods
// Logging methods
func (l *Logger) Debug(msg string, fields ...interface{})
func (l *Logger) Info(msg string, fields ...interface{})
func (l *Logger) Warn(msg string, fields ...interface{})
func (l *Logger) Error(msg string, err error, fields ...interface{})
func (l *Logger) Fatal(msg string, err error, fields ...interface{})

// Context and field methods
func (l *Logger) WithContext(ctx context.Context) *Logger
func (l *Logger) WithField(key string, value interface{}) *Logger
func (l *Logger) WithFields(fields ...interface{}) *Logger

Examples

See examples/simple-service for a complete example of using the logger package.

License

MIT License - see LICENSE file for details

Documentation

Overview

Package logger provides structured logging with zerolog for LaResto microservices. It outputs JSON-formatted logs to stdout for collection by ELK stack.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// Level sets the minimum log level (debug, info, warn, error)
	Level string

	// ServiceName is the name of the microservice
	ServiceName string

	// Environment is the deployment environment (development, staging, production)
	Environment string

	// Output specifies where to write logs (defaults to os.Stdout)
	Output io.Writer
}

Config holds logger configuration.

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger wraps zerolog.Logger with convenient methods for structured logging.

func New

func New(cfg Config) *Logger

New creates a new Logger with the given configuration.

func NewDefault

func NewDefault() *Logger

NewDefault creates a logger with default configuration for development.

func (*Logger) Debug

func (l *Logger) Debug(msg string, fields ...interface{})

Debug logs a debug message with optional fields. Fields should be provided as key-value pairs: logger.Debug("msg", "key1", val1, "key2", val2)

func (*Logger) Error

func (l *Logger) Error(msg string, err error, fields ...interface{})

Error logs an error message with optional fields. The first field can be an error: logger.Error("msg", err, "key", val)

func (*Logger) Fatal

func (l *Logger) Fatal(msg string, err error, fields ...interface{})

Fatal logs a fatal message and exits the program.

func (*Logger) Info

func (l *Logger) Info(msg string, fields ...interface{})

Info logs an info message with optional fields.

func (*Logger) Warn

func (l *Logger) Warn(msg string, fields ...interface{})

Warn logs a warning message with optional fields.

func (*Logger) WithContext

func (l *Logger) WithContext(ctx context.Context) *Logger

WithContext returns a logger that includes context values. It extracts request_id and user_id from context if present.

func (*Logger) WithField

func (l *Logger) WithField(key string, value interface{}) *Logger

WithField returns a logger with a permanent field.

func (*Logger) WithFields

func (l *Logger) WithFields(fields ...interface{}) *Logger

WithFields returns a logger with permanent fields. Fields should be provided as key-value pairs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL