Skip to content

Logging System

Blueprint provides a structured logging system built on top of zerolog that offers consistent logging patterns across different components.

Features

  • Structured logging with standardized field names
  • Context-aware logging for tracking requests across multiple services
  • Distributed tracing with trace and request IDs
  • Component-specific logging for HTTP and Kafka operations
  • Log levels for different severity of messages
  • Performance-oriented with minimal allocations

Basic Usage

import "github.com/oddbit-project/blueprint/log"

// Create a logger for a module
logger := log.New("mymodule")

// Log messages at different levels
logger.Info("Application started", log.KV{
    "version": "1.0.0",
})

logger.Debug("Processing item", log.KV{
    "item_id": 123,
})

// Log errors with stack traces
err := someOperation()
if err != nil {
    logger.Error(err, "Failed to process item", log.KV{
        "item_id": 123,
    })
}

Context-Aware Logging

import "github.com/oddbit-project/blueprint/log"

// Create a request context with trace ID
ctx, logger := log.NewRequestContext(context.Background(), "api")

// Add fields to the logger
ctx = log.WithField(ctx, "user_id", userId)

// Log using the context
log.Info(ctx, "Processing user request")

// Pass the context to other functions
processRequest(ctx, request)

// In other functions, retrieve the logger from context
func processRequest(ctx context.Context, req Request) {
    logger := log.FromContext(ctx)
    logger.Info("Processing request details")

    // Or use helper functions
    log.Info(ctx, "Alternative way to log")
}

HTTP Request Logging

import (
    "github.com/gin-gonic/gin"
    "github.com/oddbit-project/blueprint/log"
    "github.com/oddbit-project/provider/httpserver"
)

// Use the HTTP logging middleware
router.Use(httpserver.HTTPLogMiddleware("api"))

// Log within handlers
func handler(c *gin.Context) {
    // Get the request logger
    logger := httpserver.GetRequestLogger(c)

    // Or use helper functions
    httpserver.RequestInfo(c, "Processing API request", log.KV{
        "request_data": someData,
    })

    // Error handling
    if err := someOperation(); err != nil {
        httpserver.RequestError(c, err, "Operation failed")
        return
    }
}

Kafka Message Logging

Kafka consumer example:

import (
    "context"
    "github.com/oddbit-project/blueprint/log"
    "github.com/oddbit-project/blueprint/provider/kafka"
)

ctx := context.Background()

// Producer logging
producer, _ := kafka.NewProducer(cfg, nil)
err := producer.WriteJson(ctx, data)

// Consumer with logging
consumer, _ := kafka.NewConsumer(cfg, nil)
consumer.Subscribe(ctx, func(ctx context.Context, msg kafka.Message) error {
    // use consumer logger
    consumer.Logger.Info("processing message...")

    // log kafka message
    kafka.LogMessageReceived(consumer.Logger, msg, consumer.GetConfig().Group)    

    // Add your processing logic
    // ...

    return nil
})

Configuration

import "github.com/oddbit-project/blueprint/log"

// Create a configuration
cfg := log.NewDefaultConfig()
cfg.Level = "debug"           // log level: debug, info, warn, error
cfg.Format = "pretty"        // output format: pretty or json
cfg.IncludeTimestamp = true   // include timestamp in logs
cfg.IncludeCaller = true      // include caller information
cfg.IncludeHostname = true    // include hostname

// Configure the global logger
err := log.Configure(cfg)
if err != nil {
    panic(err)
}

Best Practices

  1. Include relevant fields: Add meaningful fields to help with debugging and analysis
  2. Be consistent with log levels:
  3. DEBUG: Detailed information for debugging
  4. INFO: General operational information
  5. WARN: Situations that might cause issues
  6. ERROR: Errors that prevent normal operation
  7. FATAL: Critical errors that require shutdown
  8. Sanitize sensitive data: Don't log passwords, tokens, or other sensitive information
  9. Use structured logging: Avoid string concatenation or formatting in log messages