Skip to main content

Overview

The timing middleware adds Server-Timing headers to responses, allowing you to measure and report server-side performance metrics. These metrics are visible in browser DevTools.

Installation

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

Quick Start

app := mizu.New()
app.Use(timing.New())

Examples

Basic Usage

app.Use(timing.New())

app.Get("/", func(c *mizu.Ctx) error {
    // Total time is automatically tracked
    return c.JSON(200, data)
})
// Response header: Server-Timing: total;dur=12.34

Track Custom Metrics

app.Get("/users", func(c *mizu.Ctx) error {
    // Track database query time
    start := time.Now()
    users, err := db.GetUsers()
    timing.Add(c, "db", time.Since(start), "Database query")

    // Track serialization time
    start = time.Now()
    data := serialize(users)
    timing.Add(c, "serialize", time.Since(start), "JSON serialization")

    return c.JSON(200, data)
})
// Server-Timing: total;dur=45.67, db;dur=30.12;desc="Database query", serialize;dur=5.23;desc="JSON serialization"

Using Start/Stop Pattern

app.Get("/data", func(c *mizu.Ctx) error {
    // Start a timing measurement
    stop := timing.Start(c, "external_api")

    resp, err := http.Get("https://api.example.com/data")
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    // Stop and record with description
    stop("External API call")

    return c.JSON(200, resp.Body)
})

Using Track Helper

app.Get("/compute", func(c *mizu.Ctx) error {
    var result int

    // Track a function call
    timing.Track(c, "calculation", func() {
        result = expensiveCalculation()
    })

    return c.JSON(200, map[string]int{"result": result})
})

Multiple Metrics

app.Get("/dashboard", func(c *mizu.Ctx) error {
    // Track multiple operations
    stopAuth := timing.Start(c, "auth")
    user := authenticateUser(c)
    stopAuth("Authentication")

    stopData := timing.Start(c, "data")
    dashboard := loadDashboard(user.ID)
    stopData("Load dashboard data")

    stopRender := timing.Start(c, "render")
    html := renderTemplate(dashboard)
    stopRender("Template rendering")

    return c.HTML(200, html)
})
// Server-Timing: total;dur=120.50, auth;dur=15.20;desc="Authentication", data;dur=80.30;desc="Load dashboard data", render;dur=25.00;desc="Template rendering"

API Reference

func New() mizu.Middleware
func Add(c *mizu.Ctx, name string, duration time.Duration, description string)
func Start(c *mizu.Ctx, name string) func(description string)
func Track(c *mizu.Ctx, name string, fn func())

Functions

FunctionDescription
New()Creates the timing middleware
Add()Adds a metric with name, duration, and optional description
Start()Starts timing and returns a stop function
Track()Times a function execution

Response Header Format

Server-Timing: <name>;dur=<milliseconds>;desc="<description>"
Multiple metrics are comma-separated:
Server-Timing: total;dur=150.00, db;dur=45.20;desc="Query users", cache;dur=2.10;desc="Redis lookup"

Viewing in Browser

Open DevTools → Network → Select request → Timing tab to see:
  • Total server processing time
  • Individual metric breakdowns
  • Descriptions for each metric

Technical Details

Implementation Architecture

The timing middleware uses a context-based storage mechanism to track performance metrics throughout the request lifecycle.

Core Components

  1. timingData Structure
    • Thread-safe metric storage using sync.Mutex
    • Tracks request start time
    • Maintains a slice of custom metrics
  2. Context Integration
    • Uses typed context key for type-safe storage
    • Stores timing data in request context
    • Enables metric collection across handler chain
  3. Metric Recording
    • Metrics stored as {name, duration, description} tuples
    • Total duration calculated from request start time
    • All durations converted to milliseconds (microseconds/1000)

Header Generation

The middleware builds the Server-Timing header after handler execution:
  • Total time is always included first
  • Custom metrics appended in order of addition
  • Format: name;dur=<ms>;desc="<description>"
  • Multiple metrics joined with , separator

Thread Safety

All metric operations are protected by mutex locks:
  • Add() acquires lock before appending metrics
  • Header generation locks before reading metrics
  • Safe for concurrent metric recording

Best Practices

  • Track database queries, cache lookups, and external API calls
  • Use descriptive names for metrics
  • Add descriptions for clarity
  • Keep metric names short and consistent
  • Use the Start/Stop pattern for cleaner code

Testing

Test Coverage

Test CaseDescriptionExpected Behavior
TestNewBasic middleware functionalitySets Server-Timing header with total duration after 10ms sleep
TestAddAdding custom metrics with/without descriptionsIncludes “db;dur=50” with description and “cache;dur=5” without description
TestStartStart/Stop pattern for timing operationsRecords operation duration (~20ms) with “Long operation” description
TestTrackTrack helper for timing function executionRecords compute duration (~15ms) without description
TestAdd_NoMiddlewareGraceful handling when middleware not installedAdd() doesn’t panic, returns successfully
TestNew_MultipleMetricsMultiple metrics in single requestHeader contains 4 entries (total + 3 custom metrics)