middleware

package
v1.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 29, 2025 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package middleware provides optional Echo middleware helpers for EchoNext applications.

This package COMPLEMENTS Echo's built-in middleware rather than replacing it. Echo already has excellent middleware for logging, recovery, CORS, etc. These helpers add additional functionality that works alongside Echo's middleware.

Features:

  • RequestID: Add correlation IDs to requests
  • Metrics: Simple request metrics collection
  • StructuredLogger: Enhanced logging with structured fields
  • OpenTelemetry: Automatic distributed tracing and metrics

OpenTelemetry Integration

The OTEL middleware provides automatic instrumentation for distributed tracing and metrics collection. It supports auto-configuration from environment variables.

Environment Variables:

  • OTEL_SERVICE_NAME: Service name (default: "echonext-service")
  • OTEL_SERVICE_VERSION: Service version (default: "1.0.0")
  • OTEL_ENVIRONMENT: Deployment environment (default: "development")
  • OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector endpoint (default: "localhost:4317")
  • OTEL_INSECURE: Use insecure connection (default: "true")
  • OTEL_SAMPLE_RATE: Trace sampling rate 0.0-1.0 (default: "1.0")
  • OTEL_ENABLE_TRACING: Enable tracing (default: "true")
  • OTEL_ENABLE_METRICS: Enable metrics (default: "true")

Example OTEL usage:

import (
    "context"
    "github.com/abdussamadbello/echonext"
    "github.com/abdussamadbello/echonext/pkg/contrib/middleware"
)

func main() {
    app := echonext.New()

    // Initialize OTEL with auto-configuration from environment
    shutdown, err := middleware.InitOTEL(context.Background(), middleware.DefaultOTELConfig())
    if err != nil {
        log.Fatal(err)
    }
    defer shutdown.Shutdown(context.Background())

    // Add OTEL middleware for automatic request tracing
    app.Use(middleware.OTELMiddleware("my-service"))

    // Or with custom options
    app.Use(middleware.OTELMiddleware("my-service",
        middleware.WithSkipper(func(c echo.Context) bool {
            return c.Path() == "/health"
        }),
        middleware.WithCustomAttributes(
            attribute.String("custom.key", "value"),
        ),
    ))

    // Access trace info in handlers
    app.GET("/users", func(c echo.Context) error {
        traceID := middleware.GetTraceID(c)
        middleware.AddSpanEvent(c, "fetching users")
        // ...
    })
}

Traced HTTP Client (Outgoing Requests)

The package provides an instrumented HTTP client for tracing outgoing requests. This enables full distributed tracing across service boundaries.

Creating a traced client:

// Option 1: Create new traced client
client := middleware.NewTracedHTTPClient()

// Option 2: With custom options
client := middleware.NewTracedHTTPClient(
    middleware.WithClientTimeout(10 * time.Second),
    middleware.WithBaseTransport(customTransport),
)

// Option 3: Wrap an existing client
client := middleware.WrapHTTPClient(existingClient)

Making traced outgoing requests in handlers:

app.GET("/users/:id", func(c echo.Context) error {
    // Get context with trace info from incoming request
    ctx := c.Request().Context()

    // Make traced GET request - trace context is automatically propagated
    resp, err := client.Get(ctx, "http://user-service/api/users")
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    // Or create a request manually with trace context
    req, _ := middleware.NewRequestWithTrace(c, "POST", "http://other-service/api", body)
    resp, err = client.Do(req)
})

The traced client automatically:

  • Creates spans for each outgoing HTTP request
  • Propagates trace context (traceparent, tracestate headers)
  • Records HTTP method, URL, status code, and duration
  • Links outgoing spans to the parent incoming request span

Helper functions:

  • NewTracedHTTPClient: Create a new instrumented HTTP client
  • WrapHTTPClient: Wrap an existing http.Client with tracing
  • WrapTransport: Wrap an http.RoundTripper with tracing
  • NewRequestWithTrace: Create http.Request with trace context from echo.Context
  • HTTPClientFromContext: Get context with trace info from echo.Context

Basic Middleware Usage

Example usage:

import (
    "github.com/abdussamadbello/echonext"
    "github.com/abdussamadbello/echonext/pkg/contrib/middleware"
    echomw "github.com/labstack/echo/v4/middleware"
)

app := echonext.New()

// Use Echo's built-in middleware
app.Use(echomw.Recover())
app.Use(echomw.CORS())

// Add contrib middleware
app.Use(middleware.RequestID())

// Create metrics collector
metrics := middleware.NewMetrics()
app.Use(middleware.MetricsMiddleware(metrics))

// Expose metrics endpoint
app.GET("/metrics", middleware.MetricsHandler(metrics))

// Use structured logging with request IDs
app.Use(middleware.StructuredLogger(middleware.StructuredLoggerConfig{
    CustomFields: func(c echo.Context) map[string]interface{} {
        return map[string]interface{}{
            "user_agent": c.Request().UserAgent(),
        }
    },
}))

Package middleware provides optional Echo middleware helpers for EchoNext applications. These complement Echo's built-in middleware rather than replacing them.

Index

Constants

View Source
const (
	// HeaderXRequestID is the header name for request ID
	HeaderXRequestID = "X-Request-ID"
	// ContextKeyRequestID is the context key for storing request ID
	ContextKeyRequestID = "request_id"
)

Variables

This section is empty.

Functions

func AddSpanEvent

func AddSpanEvent(c echo.Context, name string, attrs ...attribute.KeyValue)

AddSpanEvent adds an event to the current span

func GetRequestID

func GetRequestID(c echo.Context) string

GetRequestID retrieves the request ID from context

func GetSpanID

func GetSpanID(c echo.Context) string

GetSpanID extracts the span ID from the current request context

func GetTraceID

func GetTraceID(c echo.Context) string

GetTraceID extracts the trace ID from the current request context

func HTTPClientFromContext

func HTTPClientFromContext(c echo.Context) context.Context

HTTPClientFromContext creates a request with trace context from echo.Context This is a convenience function for making outgoing requests that continue the trace

func InitDefaultTracedClient

func InitDefaultTracedClient(opts ...TracedClientOption)

InitDefaultTracedClient initializes the default traced HTTP client Call this after InitOTEL

func MetricsHandler

func MetricsHandler(metrics *Metrics) echo.HandlerFunc

MetricsHandler returns a handler that exposes metrics

func MetricsMiddleware

func MetricsMiddleware(metrics *Metrics) echo.MiddlewareFunc

MetricsMiddleware returns middleware that collects request metrics

func NewRequestWithTrace

func NewRequestWithTrace(c echo.Context, method, url string, body interface{}) (*http.Request, error)

NewRequestWithTrace creates an http.Request with trace context from echo.Context

func OTELMiddleware

func OTELMiddleware(serviceName string, opts ...OTELMiddlewareOption) echo.MiddlewareFunc

OTELMiddleware returns Echo middleware that instruments HTTP requests with OpenTelemetry This is the primary middleware for automatic OTEL instrumentation

func OTELTracingMiddleware

func OTELTracingMiddleware(serviceName string) echo.MiddlewareFunc

OTELTracingMiddleware returns a simpler tracing-only middleware Use this if you only need distributed tracing without metrics

func RecordError

func RecordError(c echo.Context, err error, opts ...trace.EventOption)

RecordError records an error in the current span

func RequestID

func RequestID() echo.MiddlewareFunc

RequestID returns a middleware that adds a request ID to each request The request ID is added to the response header and stored in context

func RequestIDWithConfig

func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc

RequestIDWithConfig returns RequestID middleware with custom configuration

func SetSpanAttributes

func SetSpanAttributes(c echo.Context, attrs ...attribute.KeyValue)

SetSpanAttributes adds attributes to the current span

func SetSpanStatus

func SetSpanStatus(c echo.Context, code trace.SpanKind, description string)

SetSpanStatus sets the status of the current span

func StartSpan

func StartSpan(c echo.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span)

StartSpan starts a new child span from the request context Remember to call span.End() when done

func StructuredLogger

func StructuredLogger(config StructuredLoggerConfig) echo.MiddlewareFunc

StructuredLogger returns a middleware that adds structured logging This complements Echo's built-in logger middleware

func WrapTransport

func WrapTransport(transport http.RoundTripper) http.RoundTripper

WrapTransport wraps an existing http.RoundTripper with OTEL instrumentation Use this to add tracing to an existing HTTP client

Types

type Metrics

type Metrics struct {
	TotalRequests  int64
	TotalErrors    int64
	RequestsByCode map[int]int64
	TotalDuration  time.Duration
	// contains filtered or unexported fields
}

Metrics holds request metrics

func NewMetrics

func NewMetrics() *Metrics

NewMetrics creates a new metrics instance

func (*Metrics) GetMetrics

func (m *Metrics) GetMetrics() map[string]interface{}

GetMetrics returns current metrics (thread-safe)

func (*Metrics) Record

func (m *Metrics) Record(statusCode int, duration time.Duration)

Record records a request metric

type OTELConfig

type OTELConfig struct {
	// ServiceName is the name of the service (required)
	ServiceName string

	// ServiceVersion is the version of the service
	ServiceVersion string

	// Environment is the deployment environment (e.g., "production", "staging")
	Environment string

	// Endpoint is the OTLP collector endpoint (e.g., "localhost:4317")
	// If empty, uses OTEL_EXPORTER_OTLP_ENDPOINT env var
	Endpoint string

	// Insecure disables TLS for the OTLP connection
	Insecure bool

	// SampleRate is the trace sampling rate (0.0 to 1.0)
	// Default is 1.0 (sample everything)
	SampleRate float64

	// EnableTracing enables distributed tracing
	// Default is true
	EnableTracing bool

	// EnableMetrics enables metrics collection
	// Default is true
	EnableMetrics bool

	// Skipper defines a function to skip OTEL for certain requests
	Skipper func(echo.Context) bool

	// CustomAttributes adds custom attributes to all spans
	CustomAttributes []attribute.KeyValue

	// PropagateHeaders specifies additional headers to propagate
	PropagateHeaders []string
}

OTELConfig configures OpenTelemetry instrumentation

func DefaultOTELConfig

func DefaultOTELConfig() OTELConfig

DefaultOTELConfig returns a default OTEL configuration It reads from environment variables when available

type OTELMiddlewareOption

type OTELMiddlewareOption func(*otelMiddlewareConfig)

OTELMiddlewareOption configures the OTEL middleware

func WithCustomAttributes

func WithCustomAttributes(attrs ...attribute.KeyValue) OTELMiddlewareOption

WithCustomAttributes adds custom attributes to all spans

func WithSkipper

func WithSkipper(skipper func(echo.Context) bool) OTELMiddlewareOption

WithSkipper sets a skipper function for the middleware

type OTELShutdown

type OTELShutdown struct {
	// contains filtered or unexported fields
}

OTELShutdown holds shutdown functions for OTEL providers

func InitOTEL

func InitOTEL(ctx context.Context, cfg OTELConfig) (*OTELShutdown, error)

InitOTEL initializes OpenTelemetry with the given configuration Returns a shutdown function that should be called on application exit

func (*OTELShutdown) Shutdown

func (s *OTELShutdown) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down OTEL providers

type RequestIDConfig

type RequestIDConfig struct {
	// Skipper defines a function to skip middleware
	Skipper func(c echo.Context) bool

	// Generator defines a function to generate request ID
	// Default: generates UUID v4
	Generator func() string

	// RequestIDHeader is the header name for request ID
	// Default: X-Request-ID
	RequestIDHeader string

	// ContextKey is the key used to store request ID in context
	// Default: request_id
	ContextKey string
}

RequestIDConfig defines configuration for RequestID middleware

func DefaultRequestIDConfig

func DefaultRequestIDConfig() RequestIDConfig

DefaultRequestIDConfig returns default configuration

type StructuredLoggerConfig

type StructuredLoggerConfig struct {
	// Skipper defines a function to skip middleware
	Skipper middleware.Skipper

	// CustomFields allows adding custom fields to log entries
	CustomFields func(c echo.Context) map[string]interface{}
}

StructuredLoggerConfig extends Echo's logger with structured fields

type TracedClientOption

type TracedClientOption func(*tracedClientConfig)

TracedClientOption configures the traced HTTP client

func WithBaseTransport

func WithBaseTransport(transport http.RoundTripper) TracedClientOption

WithBaseTransport sets the base transport to wrap

func WithClientTimeout

func WithClientTimeout(timeout time.Duration) TracedClientOption

WithClientTimeout sets the client timeout

func WithSpanNameFormatter

func WithSpanNameFormatter(formatter func(operation string, r *http.Request) string) TracedClientOption

WithSpanNameFormatter sets a custom span name formatter

type TracedHTTPClient

type TracedHTTPClient struct {
	*http.Client
}

TracedHTTPClient is an HTTP client with OpenTelemetry instrumentation for tracing outgoing requests

var DefaultTracedClient *TracedHTTPClient

DefaultTracedClient is a pre-configured traced HTTP client Initialize after calling InitOTEL

func NewTracedHTTPClient

func NewTracedHTTPClient(opts ...TracedClientOption) *TracedHTTPClient

NewTracedHTTPClient creates a new HTTP client with OTEL instrumentation All outgoing requests will automatically create spans and propagate trace context

func WrapHTTPClient

func WrapHTTPClient(client *http.Client) *TracedHTTPClient

WrapHTTPClient wraps an existing HTTP client with OTEL instrumentation

func (*TracedHTTPClient) Do

func (c *TracedHTTPClient) Do(req *http.Request) (*http.Response, error)

Do executes the request with tracing Use this when you have access to echo.Context to propagate the trace

func (*TracedHTTPClient) DoWithContext

func (c *TracedHTTPClient) DoWithContext(ctx context.Context, req *http.Request) (*http.Response, error)

DoWithContext executes the request with the given context The context should contain the trace information (from echo.Context.Request().Context())

func (*TracedHTTPClient) Get

func (c *TracedHTTPClient) Get(ctx context.Context, url string) (*http.Response, error)

Get performs a traced GET request

func (*TracedHTTPClient) Post

func (c *TracedHTTPClient) Post(ctx context.Context, url string, contentType string, body interface{}) (*http.Response, error)

Post performs a traced POST request

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL