Files
vecna/pkg/metrics/middleware.go
T
warkanum 1953a4f4f9 feat(server): add passthrough proxy for OpenAI-compatible endpoints
* implement proxy handler for various OpenAI API routes
* add error handling for request body and response streaming
* introduce new error response format for API compatibility
* add tests for recover middleware to handle panics gracefully
2026-08-01 20:15:59 +02:00

69 lines
2.0 KiB
Go

package metrics
import (
"fmt"
"net/http"
"github.com/uptrace/bunrouter"
)
// Middleware returns a bunrouter middleware that records per-request Prometheus metrics.
// It reads timing from the RequestTrace stored in the context (set by server/trace.go).
// The trace target/url labels are optional; pass empty strings if not applicable.
func (r *Registry) Middleware(getTrace func(req bunrouter.Request) TraceSnapshot) bunrouter.MiddlewareFunc {
return func(next bunrouter.HandlerFunc) bunrouter.HandlerFunc {
return func(w http.ResponseWriter, req bunrouter.Request) error {
rw := &statusWriter{ResponseWriter: w, status: http.StatusOK}
err := next(rw, req)
snap := getTrace(req)
endpoint := req.URL.Path
status := fmt.Sprintf("%d", rw.status)
r.RequestsTotal.WithLabelValues(endpoint, status).Inc()
r.RequestDuration.WithLabelValues(endpoint).Observe(snap.TotalSeconds)
if snap.ForwardTarget != "" {
r.ForwardDuration.WithLabelValues(snap.ForwardTarget, snap.ForwardURL).Observe(snap.ForwardSeconds)
}
if snap.AdapterType != "" {
r.TranslateDuration.WithLabelValues(snap.AdapterType).Observe(snap.TranslateSeconds)
}
if snap.PromptTokens > 0 || snap.TotalTokens > 0 {
r.AddTokens(snap.ForwardTarget, snap.ForwardModel, snap.PromptTokens, snap.TotalTokens)
}
return err
}
}
}
// TraceSnapshot carries the timing and usage values the metrics middleware needs.
type TraceSnapshot struct {
TotalSeconds float64
ForwardSeconds float64
TranslateSeconds float64
ForwardTarget string
ForwardURL string
ForwardModel string
AdapterType string
PromptTokens int
TotalTokens int
}
// statusWriter wraps http.ResponseWriter to capture the written status code.
type statusWriter struct {
http.ResponseWriter
status int
}
func (sw *statusWriter) WriteHeader(code int) {
sw.status = code
sw.ResponseWriter.WriteHeader(code)
}
func (sw *statusWriter) Flush() {
if f, ok := sw.ResponseWriter.(http.Flusher); ok {
f.Flush()
}
}