Skip to main content

Overview

The otel middleware integrates with OpenTelemetry for distributed tracing, metrics, and observability. Use it when you need:
  • Distributed tracing
  • OpenTelemetry integration
  • Observability platforms (Jaeger, Zipkin, etc.)

Installation

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

Quick Start

app := mizu.New()

// Initialize OpenTelemetry
shutdown := otel.Init(otel.Options{
    ServiceName: "my-service",
    Endpoint:    "localhost:4317",
})
defer shutdown()

app.Use(otel.Middleware())

Configuration

Options

OptionTypeDefaultDescription
ServiceNamestringRequiredService name
EndpointstringRequiredCollector endpoint
InsecureboolfalseSkip TLS
Propagators[]stringW3CPropagation formats

Examples

Basic Setup

shutdown := otel.Init(otel.Options{
    ServiceName: "api",
    Endpoint:    "localhost:4317",
})
defer shutdown()

app.Use(otel.Middleware())

With Jaeger

shutdown := otel.Init(otel.Options{
    ServiceName: "api",
    Endpoint:    "jaeger:4317",
    Propagators: []string{"jaeger", "w3c"},
})

Custom Spans

app.Get("/process", func(c *mizu.Ctx) error {
    ctx, span := otel.StartSpan(c, "process-data")
    defer span.End()

    // Add attributes
    span.SetAttributes(
        attribute.String("user.id", userID),
    )

    // Do work
    result := processData(ctx)

    return c.JSON(200, result)
})

Propagate to Downstream

app.Get("/", func(c *mizu.Ctx) error {
    req, _ := http.NewRequestWithContext(c.Context(), "GET", url, nil)
    otel.Inject(c.Context(), req.Header)
    // Make request...
})

API Reference

Functions

// Init initializes OpenTelemetry
func Init(opts Options) func()

// Middleware creates OTel middleware
func Middleware() mizu.Middleware

// StartSpan creates child span
func StartSpan(c *mizu.Ctx, name string) (context.Context, trace.Span)

// Inject propagates context
func Inject(ctx context.Context, headers http.Header)

Technical Details

Architecture

The otel middleware is a lightweight OpenTelemetry-compatible implementation that provides distributed tracing without external dependencies. It implements the OpenTelemetry specification for trace context propagation.

Components

SpanContext: Holds the trace context with TraceID (16 bytes), SpanID (8 bytes), TraceFlags, and optional TraceState. Span: Represents a single operation with metadata including:
  • Name (HTTP method + path)
  • Parent/child relationships
  • Start/end timestamps
  • Status (Unset, OK, Error)
  • Attributes (key-value pairs)
  • Events (timestamped logs)
  • Links (connections to other spans)
SpanProcessor: Interface for processing completed spans. Implementations include:
  • InMemoryProcessor: Stores spans in memory for testing
  • PrintProcessor: Prints spans to stdout
  • Custom processors can export to external systems

Propagation Formats

W3C Trace Context (default):
  • Header: Traceparent: 00-{traceId}-{spanId}-{flags}
  • Optional: Tracestate for vendor-specific data
  • Format: version-traceId-spanId-flags
B3 Propagation:
  • Single header: B3: {traceId}-{spanId}-{sampled}
  • Multi-header: X-B3-Traceid, X-B3-Spanid, X-B3-Sampled
  • Compatible with Zipkin and other B3-based systems

ID Generation

  • TraceID: 128-bit (16 bytes) cryptographically random hex string
  • SpanID: 64-bit (8 bytes) cryptographically random hex string
  • Generated using crypto/rand for uniqueness

Request Flow

  1. Extract parent context from incoming request headers
  2. Create new span with inherited TraceID or generate new one
  3. Generate new SpanID for current span
  4. Add default HTTP attributes (method, URL, status, etc.)
  5. Call OnStart hook if configured
  6. Inject trace context into response headers
  7. Store span in request context
  8. Execute handler
  9. Set final status and attributes based on response
  10. Call OnEnd hook and SpanProcessor

Thread Safety

  • Span methods use mutex locks for concurrent access
  • InMemoryProcessor is thread-safe with mutex protection
  • Safe for use with concurrent requests

Best Practices

  • Use consistent service names
  • Add meaningful span attributes
  • Propagate context to all calls
  • Sample appropriately in production

Testing

The otel middleware includes comprehensive test coverage for all features:
Test CaseDescriptionExpected Behavior
TestNewBasic middleware creationCreates middleware with default options and processes requests successfully
TestSpanCreationSpan generationCreates span with correct name (HTTP method + path), generates TraceID and SpanID
TestSpanAttributesAttribute collectionCaptures service.name, service.version, http.method, http.status_code, http.user_agent
TestW3CTracePropagationW3C Trace Context propagationInherits TraceID from parent, generates new SpanID, sets parent relationship, injects traceparent header
TestB3PropagationB3 single header formatExtracts trace context from B3 header, injects B3 headers in response
TestB3MultiHeaderPropagationB3 multi-header formatExtracts trace context from X-B3-* headers, maintains compatibility
TestSkipPathsPath filteringSkips configured paths (e.g., /health), traces non-skipped paths
TestSamplerCustom samplingRespects sampler function, only creates spans for sampled requests
TestErrorStatusError handlingSets StatusError for HTTP 4xx/5xx responses, captures error attributes
TestOnStartOnEndLifecycle hooksCalls OnStart when span begins, OnEnd when span completes
TestGetSpanContext accessRetrieves span from context, allows custom attributes and events
TestSpanDurationTiming measurementCalculates span duration, sets end time correctly
TestSpanContextIsValidContext validationValidates SpanContext has TraceID and SpanID
TestSpanContextIsSampledSampling flag checkCorrectly interprets trace flags for sampling decision
TestInMemoryProcessorSpan processingStores spans, retrieves all spans, clears span storage