HTTP Middleware¶
Add tracing to HTTP handlers and clients.
import "github.com/agentplexus/go-opik/middleware"
Server Middleware¶
Wrap HTTP Handlers¶
Automatically create traces for incoming requests:
opikClient, _ := opik.NewClient()
// Wrap a handler
handler := middleware.TracingMiddleware(opikClient, "api-request")(yourHandler)
// Use with http.ServeMux
mux := http.NewServeMux()
mux.Handle("/api/", middleware.TracingMiddleware(opikClient, "api")(apiHandler))
What Gets Traced¶
Each request creates a trace with:
| Field | Description |
|---|---|
| Name | Configured name (e.g., "api-request") |
| Input | Method, URL, headers |
| Output | Status code, response size |
| Metadata | Duration, client IP |
Example Handler¶
func main() {
opikClient, _ := opik.NewClient()
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Access trace from context
trace := opik.TraceFromContext(r.Context())
// Create spans for sub-operations
ctx, span, _ := opik.StartSpan(r.Context(), "process-request")
// Do work...
span.End(ctx)
w.Write([]byte("OK"))
})
// Wrap with tracing
tracedHandler := middleware.TracingMiddleware(opikClient, "my-api")(handler)
http.ListenAndServe(":8080", tracedHandler)
}
Client Middleware¶
Tracing HTTP Client¶
Create a client that traces all outgoing requests:
httpClient := middleware.TracingHTTPClient("external-api")
resp, _ := httpClient.Get("https://api.example.com/data")
Tracing Round Tripper¶
Wrap an existing transport:
transport := middleware.NewTracingRoundTripper(http.DefaultTransport, "api-call")
httpClient := &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
What Gets Traced¶
Each outgoing request creates a span with:
| Field | Description |
|---|---|
| Name | Configured name |
| Type | general |
| Input | Method, URL |
| Output | Status code |
| Metadata | Duration |
Combining Server and Client¶
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Trace from middleware is in context
trace := opik.TraceFromContext(ctx)
// Create span for external call
ctx, span, _ := opik.StartSpan(ctx, "fetch-data")
// Use tracing client - span becomes parent
client := middleware.TracingHTTPClient("external-api")
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.example.com", nil)
resp, err := client.Do(req)
span.End(ctx)
// Process response...
}
Framework Integration¶
Chi¶
import "github.com/go-chi/chi/v5"
r := chi.NewRouter()
r.Use(func(next http.Handler) http.Handler {
return middleware.TracingMiddleware(opikClient, "api")(next)
})
Gin¶
import "github.com/gin-gonic/gin"
r := gin.Default()
r.Use(func(c *gin.Context) {
handler := middleware.TracingMiddleware(opikClient, "api")(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Request = r.WithContext(r.Context())
c.Next()
}),
)
handler.ServeHTTP(c.Writer, c.Request)
})
Echo¶
import "github.com/labstack/echo/v4"
e := echo.New()
e.Use(echo.WrapMiddleware(func(next http.Handler) http.Handler {
return middleware.TracingMiddleware(opikClient, "api")(next)
}))
Best Practices¶
- Name traces descriptively: Use service/endpoint names
- Add to all entry points: Trace all HTTP handlers
- Propagate context: Pass request context to downstream calls
- Use with distributed tracing: Combine with header propagation