package observability import ( "context" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" ) // Metrics is the steward-managed Prometheus steward. It owns a private // Registry (no global registry leakage) and the lethe-specific collectors // other layers increment. The HTTP middleware (Phase 5) records request // counters/histograms here; the ingest service (Phase 7) increments the // ingest counters. // // Registering everything via Registry.MustRegister in Init keeps wiring in // one place. Cardinality control on HTTPRequests/HTTPDuration is enforced by // the middleware: the route label must come from chi's RoutePattern (never // the raw URL path). type Metrics struct { Registry *prometheus.Registry HTTPRequests *prometheus.CounterVec HTTPDuration *prometheus.HistogramVec IngestLinesAccepted prometheus.Counter IngestLinesErrored prometheus.Counter IngestChunksCommitted prometheus.Counter } // Init builds a fresh registry, attaches the standard process and Go runtime // collectors, and registers the lethe-specific HTTP and ingest series. func (m *Metrics) Init(_ context.Context) error { m.Registry = prometheus.NewRegistry() m.Registry.MustRegister( collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), collectors.NewGoCollector(), ) m.HTTPRequests = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "lethe_http_requests_total", Help: "Total HTTP requests handled, labelled by method, chi route pattern, and status code.", }, []string{"method", "route", "status"}, ) m.HTTPDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "lethe_http_request_duration_seconds", Help: "HTTP request duration in seconds, labelled by method and chi route pattern.", Buckets: prometheus.DefBuckets, }, []string{"method", "route"}, ) m.IngestLinesAccepted = prometheus.NewCounter(prometheus.CounterOpts{ Name: "lethe_ingest_lines_accepted_total", Help: "Total ingest JSONL lines accepted (validated and queued for commit).", }) m.IngestLinesErrored = prometheus.NewCounter(prometheus.CounterOpts{ Name: "lethe_ingest_lines_errored_total", Help: "Total ingest JSONL lines rejected (validation, size, or schema failures).", }) m.IngestChunksCommitted = prometheus.NewCounter(prometheus.CounterOpts{ Name: "lethe_ingest_chunks_committed_total", Help: "Total ingest chunks successfully committed to the database.", }) m.Registry.MustRegister( m.HTTPRequests, m.HTTPDuration, m.IngestLinesAccepted, m.IngestLinesErrored, m.IngestChunksCommitted, ) return nil }