mattermost-push-proxy-router/metrics.go
Felipe M. 8789af2997
feat: include Go runtime and process metrics on /metrics
Register prometheus Go and process collectors alongside the router
metrics so /metrics exposes standard go_* and process_* series.
2026-07-27 08:39:11 +02:00

109 lines
3 KiB
Go

package main
import (
"net/http"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
const (
metricRequestsTotalName = "router_requests_total"
metricRoutingErrorsTotalName = "router_routing_errors_total"
metricUpstreamRequestsTotalName = "router_upstream_requests_total"
metricUpstreamDurationName = "router_upstream_duration_seconds"
)
type Metrics struct {
gatherer prometheus.Gatherer
requestsTotal *prometheus.CounterVec
routingErrorsTotal *prometheus.CounterVec
upstreamRequestsTotal *prometheus.CounterVec
upstreamDuration *prometheus.HistogramVec
}
func NewMetrics(reg prometheus.Registerer) *Metrics {
if reg == nil {
reg = prometheus.DefaultRegisterer
}
gatherer, ok := reg.(prometheus.Gatherer)
if !ok {
gatherer = prometheus.DefaultGatherer
}
reg.MustRegister(
collectors.NewGoCollector(),
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
)
m := &Metrics{
gatherer: gatherer,
requestsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: metricRequestsTotalName,
Help: "Number of push requests routed to a backend.",
}, []string{"path", "platform"}),
routingErrorsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: metricRoutingErrorsTotalName,
Help: "Number of push requests that failed before reaching a backend.",
}, []string{"reason"}),
upstreamRequestsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
Name: metricUpstreamRequestsTotalName,
Help: "Number of upstream proxy responses by outcome.",
}, []string{"platform", "outcome"}),
upstreamDuration: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: metricUpstreamDurationName,
Help: "Upstream proxy request latency in seconds.",
Buckets: prometheus.DefBuckets,
}, []string{"platform"}),
}
reg.MustRegister(
m.requestsTotal,
m.routingErrorsTotal,
m.upstreamRequestsTotal,
m.upstreamDuration,
)
return m
}
func (m *Metrics) Handler() http.Handler {
return promhttp.HandlerFor(m.gatherer, promhttp.HandlerOpts{})
}
func (m *Metrics) incRequest(path, platform string) {
m.requestsTotal.WithLabelValues(path, platform).Inc()
}
func (m *Metrics) incRoutingError(reason string) {
m.routingErrorsTotal.WithLabelValues(reason).Inc()
}
func (m *Metrics) observeUpstream(platform, outcome string, duration time.Duration) {
m.upstreamRequestsTotal.WithLabelValues(platform, outcome).Inc()
m.upstreamDuration.WithLabelValues(platform).Observe(duration.Seconds())
}
func routePathLabel(path string) string {
path = strings.TrimPrefix(path, "/api/v1/")
if path == "" {
return "unknown"
}
return path
}
func routingErrorReason(err error) string {
msg := err.Error()
switch {
case strings.HasPrefix(msg, "invalid JSON"):
return "invalid_json"
case strings.HasPrefix(msg, "missing platform"):
return "missing_platform"
default:
return "invalid_payload"
}
}