Files
x/metrics/prom.go
T
ginuerzh 55a246869e fix(metrics): add cross-platform process metrics collector for non-Linux systems
The standard Prometheus ProcessCollector uses procfs (Linux-only /proc),
so process_resident_memory_bytes and process_virtual_memory_bytes were
silently absent on FreeBSD, Darwin, and other non-Linux platforms.

- Switch to a custom prometheus.Registry to isolate GOST metrics from
  the default registry auto-registrations
- Add gopsutil/v3-based process collector (process_other.go) for
  platforms without procfs (!linux && !windows)
- Keep standard ProcessCollector (process_standard.go) on Linux/Windows
- Update metrics service and handler/metrics to serve from
  promhttp.HandlerFor(customRegistry) instead of promhttp.Handler()
- Add Registry() accessor for the custom registry

Fixes go-gost/gost#509
2026-06-21 21:55:27 +08:00

147 lines
4.4 KiB
Go

package metrics
import (
"maps"
"os"
"github.com/go-gost/core/metrics"
"github.com/prometheus/client_golang/prometheus"
)
type promMetrics struct {
host string
gauges map[metrics.MetricName]*prometheus.GaugeVec
counters map[metrics.MetricName]*prometheus.CounterVec
histograms map[metrics.MetricName]*prometheus.HistogramVec
}
// NewMetrics returns a Prometheus-based Metrics implementation. All metrics are
// registered with the given registry. Use nil to register with the default
// Prometheus registry.
func NewMetrics(reg *prometheus.Registry) metrics.Metrics {
if reg == nil {
reg = prometheus.DefaultRegisterer.(*prometheus.Registry)
}
host, _ := os.Hostname()
m := &promMetrics{
host: host,
gauges: map[metrics.MetricName]*prometheus.GaugeVec{
MetricServicesGauge: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: string(MetricServicesGauge),
Help: "Current number of services",
},
[]string{"host"}),
MetricServiceRequestsInFlightGauge: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: string(MetricServiceRequestsInFlightGauge),
Help: "Current in-flight requests",
},
[]string{"host", "service", "client"}),
},
counters: map[metrics.MetricName]*prometheus.CounterVec{
MetricServiceRequestsCounter: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: string(MetricServiceRequestsCounter),
Help: "Total number of requests",
},
[]string{"host", "service", "client"}),
MetricServiceTransferInputBytesCounter: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: string(MetricServiceTransferInputBytesCounter),
Help: "Total service input data transfer size in bytes",
},
[]string{"host", "service", "client"}),
MetricServiceTransferOutputBytesCounter: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: string(MetricServiceTransferOutputBytesCounter),
Help: "Total service output data transfer size in bytes",
},
[]string{"host", "service", "client"}),
MetricServiceHandlerErrorsCounter: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: string(MetricServiceHandlerErrorsCounter),
Help: "Total service handler errors",
},
[]string{"host", "service", "client"}),
MetricChainErrorsCounter: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: string(MetricChainErrorsCounter),
Help: "Total chain errors",
},
[]string{"host", "chain", "node"}),
MetricRecorderRecordsCounter: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: string(MetricRecorderRecordsCounter),
Help: "Total records written by recorder",
},
[]string{"host", "recorder"}),
},
histograms: map[metrics.MetricName]*prometheus.HistogramVec{
MetricServiceRequestsDurationObserver: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: string(MetricServiceRequestsDurationObserver),
Help: "Distribution of request latencies",
Buckets: []float64{
.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 15, 30, 60,
},
},
[]string{"host", "service"}),
MetricNodeConnectDurationObserver: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: string(MetricNodeConnectDurationObserver),
Help: "Distribution of chain node connect latencies",
Buckets: []float64{
.01, .05, .1, .25, .5, 1, 1.5, 2, 5, 10, 15, 30, 60,
},
},
[]string{"host", "chain", "node"}),
},
}
for k := range m.gauges {
reg.MustRegister(m.gauges[k])
}
for k := range m.counters {
reg.MustRegister(m.counters[k])
}
for k := range m.histograms {
reg.MustRegister(m.histograms[k])
}
return m
}
func (m *promMetrics) Gauge(name metrics.MetricName, labels metrics.Labels) metrics.Gauge {
v, ok := m.gauges[name]
if !ok {
return nopGauge
}
plabels := prometheus.Labels{}
maps.Copy(plabels, labels)
plabels["host"] = m.host
return v.With(plabels)
}
func (m *promMetrics) Counter(name metrics.MetricName, labels metrics.Labels) metrics.Counter {
v, ok := m.counters[name]
if !ok {
return nopCounter
}
plabels := prometheus.Labels{}
maps.Copy(plabels, labels)
plabels["host"] = m.host
return v.With(plabels)
}
func (m *promMetrics) Observer(name metrics.MetricName, labels metrics.Labels) metrics.Observer {
v, ok := m.histograms[name]
if !ok {
return nopObserver
}
plabels := prometheus.Labels{}
maps.Copy(plabels, labels)
plabels["host"] = m.host
return v.With(plabels)
}