Skip to main content

Overview

The logger middleware logs HTTP requests with configurable format, output destination, and filtering. It captures method, path, status, latency, and custom fields. Use it when you need:
  • HTTP request logging
  • Access logs for debugging
  • Request timing metrics

Installation

import "github.com/go-mizu/mizu/middlewares/logger"

Quick Start

app := mizu.New()

// Default logger (stdout)
app.Use(logger.New())

app.Get("/", func(c *mizu.Ctx) error {
    return c.Text(200, "Hello!")
})
// Output: 2024/01/15 10:30:00 | 200 | 1.2ms | GET /

Configuration

Options

OptionTypeDefaultDescription
Outputio.Writeros.StdoutLog output destination
FormatstringDefault formatLog format template
Skipfunc(*mizu.Ctx) boolnilSkip logging for requests

Format Tags

TagDescription
${method}HTTP method
${path}Request path
${status}Response status code
${latency}Request duration
${ip}Client IP address
${host}Request host
${protocol}HTTP protocol
${referer}Referer header
${user_agent}User-Agent header
${bytes_out}Response size
${query}Query string
${header:X-Name}Custom header value

Examples

Default Logger

app.Use(logger.New())

Custom Format

app.Use(logger.WithOptions(logger.Options{
    Format: "[${method}] ${path} -> ${status}\n",
}))
// Output: [GET] /api/users -> 200

JSON Format

app.Use(logger.WithOptions(logger.Options{
    Format: `{"method":"${method}","path":"${path}","status":${status},"latency":"${latency}"}` + "\n",
}))

Write to File

file, _ := os.OpenFile("access.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)

app.Use(logger.WithOptions(logger.Options{
    Output: file,
}))

Skip Health Checks

app.Use(logger.WithOptions(logger.Options{
    Skip: func(c *mizu.Ctx) bool {
        return c.Request().URL.Path == "/health"
    },
}))

Include Request ID

app.Use(logger.WithOptions(logger.Options{
    Format: "${header:X-Request-ID} | ${method} ${path} | ${status}\n",
}))

Full Access Log

app.Use(logger.WithOptions(logger.Options{
    Format: `${ip} - [${time}] "${method} ${path} ${protocol}" ${status} ${bytes_out} "${referer}" "${user_agent}"` + "\n",
}))

API Reference

Functions

// New creates logger middleware with defaults
func New() mizu.Middleware

// WithOptions creates logger middleware with configuration
func WithOptions(opts Options) mizu.Middleware

Technical Details

Implementation Architecture

The logger middleware captures HTTP request metrics by wrapping the response writer and measuring execution time:
  1. Response Writer Wrapping: A custom responseWriter type wraps the standard http.ResponseWriter to capture:
    • HTTP status code (defaults to 200)
    • Response size in bytes
  2. Timing Measurement: Uses time.Now() before request processing and time.Since() after to calculate latency with nanosecond precision.
  3. Client IP Detection: Implements intelligent IP extraction with fallback chain:
    • Checks X-Forwarded-For header (uses first IP if comma-separated list)
    • Checks X-Real-IP header
    • Falls back to RemoteAddr from the request
  4. Format String Processing: The formatLog function performs template tag replacement:
    • Basic tags use strings.ReplaceAll() for simple substitution
    • Dynamic tags (${header:name}, ${form:name}) use index-based parsing to extract custom values
    • Supports all standard HTTP request/response attributes
  5. Skip Function: Optional predicate function allows conditional logging based on request context, executed before timing starts to avoid overhead.

Performance Characteristics

  • Minimal overhead: Single writer wrap and string replacement operations
  • Zero allocations for skipped requests
  • String concatenation deferred until after request completion
  • No buffering of response body (streaming-friendly)

Best Practices

  • Skip logging for health check endpoints
  • Use structured (JSON) format for log aggregation
  • Include request IDs for tracing
  • Log to stderr in containers for proper log handling

Testing

The logger middleware includes comprehensive test coverage for all features and edge cases:
Test CaseDescriptionExpected Behavior
TestNewDefault logger with standard outputLogs contain status code (200), HTTP method (GET), and request path (/test)
TestWithOptions_CustomFormatCustom format string with method, path, and statusOutput matches exact format: [GET] /api -> 201\n
TestWithOptions_SkipSkip function filtering health check endpointNo log output for /health endpoint, logs /api endpoint normally
TestWithOptions_HeadersCustom header extraction using ${header:X-Request-ID}Logs the exact value of the X-Request-ID header (test-123)
TestWithOptions_AllTagsAll available format tags (host, protocol, referer, user_agent, bytes_out, query)Log contains all requested tag values from the HTTP request
TestWithOptions_XForwardedForClient IP extraction from X-Forwarded-For headerExtracts first IP from comma-separated list (1.2.3.4)
TestWithOptions_XRealIPClient IP extraction from X-Real-IP headerUses X-Real-IP value (10.0.0.1) when X-Forwarded-For not present
TestDefaultOutputDefault output configuration (stdout)Middleware executes without panic, returns 200 status