Skip to main content
The log package initializes the default slog logger with tint for colorized, structured output and provides utilities to inject request-scoped values from context.

Installation

import "github.com/aarock1234/go-template/pkg/log"
Simply importing the package is enough to configure the logger:
import (
    "log/slog"
    _ "github.com/aarock1234/go-template/pkg/log" // Initialize logger
)

Features

  • Colorized output: Automatically detects TTY and enables colors
  • Structured logging: Built on Go’s standard log/slog
  • Context injection: Add request-scoped values to all log records
  • Configurable log level: Set via LOG_LEVEL environment variable
  • Human-readable timestamps: Formatted as “Mon, Jan 2 2006, 3:04:05 pm MST”

Quick Start

package main

import (
    "context"
    "log/slog"
    "github.com/aarock1234/go-template/pkg/log"
)

func main() {
    // Basic logging
    slog.Info("server started", slog.Int("port", 8080))
    slog.Debug("debug information", slog.String("module", "main"))
    slog.Warn("warning message")
    slog.Error("error occurred", slog.String("error", "connection failed"))
    
    // Context-aware logging
    ctx := log.WithIdempotencyKey(context.Background(), "req-123")
    slog.InfoContext(ctx, "processing request") // Includes idempotency_key
}

Functions

WithIdempotencyKey

Returns a copy of the context carrying the given idempotency key.
pkg/log/log.go:21
func WithIdempotencyKey(ctx context.Context, key string) context.Context
ctx
context.Context
required
The parent context
key
string
required
The idempotency key to inject into log records
Example:
func handleRequest(w http.ResponseWriter, r *http.Request) {
    // Extract idempotency key from header
    key := r.Header.Get("Idempotency-Key")
    ctx := log.WithIdempotencyKey(r.Context(), key)
    
    // All logs in this context will include the idempotency key
    slog.InfoContext(ctx, "processing request")
    // Output: ... msg="processing request" idempotency_key=req-123
    
    processPayment(ctx)
}

func processPayment(ctx context.Context) {
    slog.InfoContext(ctx, "payment processed")
    // Output: ... msg="payment processed" idempotency_key=req-123
}

Types

ContextHandler

Wraps an slog.Handler to inject request-scoped values from the context into every log record.
pkg/log/log.go:27
type ContextHandler struct {
    slog.Handler
}
Methods:
pkg/log/log.go:32
func (h *ContextHandler) Handle(ctx context.Context, r slog.Record) error
Logs a slog.Record with request-scoped values extracted from the context. Currently supports:
  • idempotency_key - Injected when context contains an idempotency key

Configuration

Log Level

Set the log level using the LOG_LEVEL environment variable:
# .env
LOG_LEVEL=debug
Supported values:
  • debug - Show all logs including debug messages
  • info (default) - Show info, warn, and error logs
  • warn or warning - Show only warnings and errors
  • error - Show only errors
Example:
# Development
LOG_LEVEL=debug

# Production
LOG_LEVEL=info

Color Output

Colors are automatically enabled when:
  • Writing to a TTY (terminal)
  • Using Windows: Colors work via go-colorable
Colors are disabled when:
  • Output is redirected to a file
  • Running in a non-TTY environment (e.g., CI/CD)

Log Format

Logs are formatted with tint’s colorized, structured output:
Mon, Jan 2 2006, 3:04:05 pm MST INF server started port=8080
Mon, Jan 2 2006, 3:04:06 pm MST DBG debug message module=main
Mon, Jan 2 2006, 3:04:07 pm MST WRN warning message
Mon, Jan 2 2006, 3:04:08 pm MST ERR error occurred error="connection failed"
With context values:
Mon, Jan 2 2006, 3:04:09 pm MST INF processing request idempotency_key=req-123

Usage Examples

Basic Structured Logging

import "log/slog"

// Simple message
slog.Info("application started")

// With attributes
slog.Info("user logged in",
    slog.String("user_id", "123"),
    slog.String("ip", "192.168.1.1"),
)

// Grouped attributes
slog.Info("request completed",
    slog.Group("request",
        slog.String("method", "POST"),
        slog.String("path", "/api/users"),
    ),
    slog.Int("status", 201),
)

HTTP Middleware

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Add idempotency key to context
        ctx := r.Context()
        if key := r.Header.Get("Idempotency-Key"); key != "" {
            ctx = log.WithIdempotencyKey(ctx, key)
        }
        
        // Log the request
        slog.InfoContext(ctx, "incoming request",
            slog.String("method", r.Method),
            slog.String("path", r.URL.Path),
        )
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

Error Logging

func processData(ctx context.Context) error {
    data, err := fetchData()
    if err != nil {
        slog.ErrorContext(ctx, "failed to fetch data",
            slog.String("error", err.Error()),
        )
        return err
    }
    
    slog.InfoContext(ctx, "data processed", slog.Int("records", len(data)))
    return nil
}

Creating a Logger with Additional Context

// Create a logger with persistent attributes
logger := slog.With(
    slog.String("service", "payment-processor"),
    slog.String("version", "1.0.0"),
)

// All logs from this logger include the attributes
logger.Info("service started") // Includes service=payment-processor version=1.0.0
logger.Error("payment failed", slog.String("error", "insufficient funds"))

Dependencies

The package uses:

Best Practices

  1. Use structured logging: Prefer key-value attributes over string formatting
// Good
slog.Info("user created", slog.String("user_id", id))

// Avoid
slog.Info(fmt.Sprintf("user %s created", id))
  1. Use context-aware logging: Pass context through your application
func HandleRequest(ctx context.Context) {
    slog.InfoContext(ctx, "processing") // Includes context values
}
  1. Set appropriate log levels: Use debug for development, info for production
  2. Add persistent context: Create loggers with common attributes
logger := slog.With(slog.String("module", "database"))

Build docs developers (and LLMs) love