Add Prometheus metrics for connections, firewall, L7, and bytes transferred
Instrument mc-proxy with prometheus/client_golang. New internal/metrics/ package defines counters, gauges, and histograms for connection totals, active connections, firewall blocks by reason, backend dial latency, bytes transferred, L7 HTTP status codes, and L7 policy blocks. Optional [metrics] config section starts a scrape endpoint. Firewall gains BlockedWithReason() to report block cause. L7 handler wraps ResponseWriter to record status codes per hostname. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,14 +12,17 @@ import (
|
||||
"net/http/httputil"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/mc-proxy/internal/metrics"
|
||||
"git.wntrmute.dev/kyle/mc-proxy/internal/proxyproto"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// RouteConfig holds the L7 route parameters needed by the l7 package.
|
||||
type RouteConfig struct {
|
||||
Hostname string
|
||||
Backend string
|
||||
TLSCert string
|
||||
TLSKey string
|
||||
@@ -29,6 +32,21 @@ type RouteConfig struct {
|
||||
Policies []PolicyRule
|
||||
}
|
||||
|
||||
// statusRecorder wraps http.ResponseWriter to capture the status code.
|
||||
type statusRecorder struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
}
|
||||
|
||||
func (sr *statusRecorder) WriteHeader(code int) {
|
||||
sr.status = code
|
||||
sr.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (sr *statusRecorder) Unwrap() http.ResponseWriter {
|
||||
return sr.ResponseWriter
|
||||
}
|
||||
|
||||
// contextKey is an unexported type for context keys in this package.
|
||||
type contextKey int
|
||||
|
||||
@@ -75,12 +93,14 @@ func Serve(ctx context.Context, conn net.Conn, peeked []byte, route RouteConfig,
|
||||
return fmt.Errorf("creating reverse proxy: %w", err)
|
||||
}
|
||||
|
||||
// Build handler chain: context injection → L7 policies → reverse proxy.
|
||||
// Build handler chain: context injection → metrics → L7 policies → reverse proxy.
|
||||
var inner http.Handler = rp
|
||||
inner = PolicyMiddleware(route.Policies, inner)
|
||||
inner = PolicyMiddleware(route.Policies, route.Hostname, inner)
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), clientAddrKey, clientAddr))
|
||||
inner.ServeHTTP(w, r)
|
||||
sr := &statusRecorder{ResponseWriter: w, status: http.StatusOK}
|
||||
inner.ServeHTTP(sr, r)
|
||||
metrics.L7ResponsesTotal.WithLabelValues(route.Hostname, strconv.Itoa(sr.status)).Inc()
|
||||
})
|
||||
|
||||
// Serve HTTP on the TLS connection. Use HTTP/2 if negotiated,
|
||||
|
||||
Reference in New Issue
Block a user