diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 5871ca0..19f4ec3 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -378,9 +378,10 @@ func (c *Checker) GetBackendInfo(name string) (metrics.BackendInfo, bool) { return metrics.BackendInfo{}, false } return metrics.BackendInfo{ - Health: w.backend, - Enabled: w.entry.Enabled, - HCName: w.entry.HealthCheck, + Health: w.backend, + Enabled: w.entry.Enabled, + HCName: w.entry.HealthCheck, + SourceTag: w.entry.SourceTag, }, true } diff --git a/internal/config/config.go b/internal/config/config.go index e9c63ba..19a0b3b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -123,6 +123,7 @@ type TCPParams struct { type Backend struct { Address net.IP HealthCheck string // name reference into Config.HealthChecks; "" = no probing, assume healthy + SourceTag string // nginx source tag; defaults to the backend name if omitted from config Enabled bool // default true; false = exclude from serving entirely } @@ -218,7 +219,8 @@ type rawParams struct { type rawBackend struct { Address string `yaml:"address"` HealthCheck string `yaml:"healthcheck"` - Enabled *bool `yaml:"enabled"` // nil → default true + SourceTag string `yaml:"source-tag"` // defaults to backend name if omitted + Enabled *bool `yaml:"enabled"` // nil → default true } type rawPoolBackend struct { @@ -588,9 +590,14 @@ func convertBackend(name string, r *rawBackend, hcs map[string]HealthCheck) (Bac return Backend{}, fmt.Errorf("invalid address %q", r.Address) } + sourceTag := r.SourceTag + if sourceTag == "" { + sourceTag = name + } b := Backend{ Address: ip, HealthCheck: r.HealthCheck, + SourceTag: sourceTag, Enabled: boolDefault(r.Enabled, true), } diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 1f230a9..7f22579 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -21,9 +21,10 @@ import ( // BackendInfo holds the health and config state needed by the collector. type BackendInfo struct { - Health *health.Backend - Enabled bool - HCName string // healthcheck name from config + Health *health.Backend + Enabled bool + HCName string // healthcheck name from config + SourceTag string // nginx source tag; equals backend name when unset in config } // StateSource provides read-only access to the running checker state. @@ -134,6 +135,7 @@ type Collector struct { src StateSource vpp VPPSource // optional; nil when VPP integration is disabled + backendInfo *prometheus.Desc backendState *prometheus.Desc backendHealth *prometheus.Desc backendEnabled *prometheus.Desc @@ -156,6 +158,11 @@ func NewCollector(src StateSource, vpp VPPSource) *Collector { return &Collector{ src: src, vpp: vpp, + backendInfo: prometheus.NewDesc( + "maglev_backend_info", + "Static backend metadata. Always 1; metadata is conveyed via labels.", + []string{"backend", "address", "healthcheck", "source_tag"}, nil, + ), backendState: prometheus.NewDesc( "maglev_backend_state", "Current backend state (1 = active for the given state label).", @@ -216,6 +223,7 @@ func NewCollector(src StateSource, vpp VPPSource) *Collector { // Describe implements prometheus.Collector. func (c *Collector) Describe(ch chan<- *prometheus.Desc) { + ch <- c.backendInfo ch <- c.backendState ch <- c.backendHealth ch <- c.backendEnabled @@ -247,6 +255,11 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) { } addr := info.Health.Address.String() + ch <- prometheus.MustNewConstMetric( + c.backendInfo, prometheus.GaugeValue, 1.0, + name, addr, info.HCName, info.SourceTag, + ) + // One time-series per possible state; the current state is 1, rest 0. for _, s := range states { val := 0.0