New metrics: XOnBattery, XOffBattery, LastSelfTest, NominalPower (#2)
* Produce metrics for XOnBattery, XOffBattery and LastSelftest. * Produce metrics for nominal output power. * Fix cosmetic issues: order of imports and dots at end of sentences.
This commit is contained in:
parent
31b4b80693
commit
290f3ea806
|
@ -2,6 +2,7 @@ package apcupsdexporter
|
|||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/mdlayher/apcupsd"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
@ -27,6 +28,10 @@ type UPSCollector struct {
|
|||
BatteryTimeLeftSeconds *prometheus.Desc
|
||||
BatteryTimeOnSeconds *prometheus.Desc
|
||||
BatteryCumulativeTimeOnSecondsTotal *prometheus.Desc
|
||||
LastTransferOnBattery *prometheus.Desc
|
||||
LastTransferOffBattery *prometheus.Desc
|
||||
LastSelftest *prometheus.Desc
|
||||
NominalPowerWatts *prometheus.Desc
|
||||
|
||||
ss StatusSource
|
||||
}
|
||||
|
@ -110,6 +115,34 @@ func NewUPSCollector(ss StatusSource) *UPSCollector {
|
|||
nil,
|
||||
),
|
||||
|
||||
LastTransferOnBattery: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "", "apcupsd_last_transfer_on_battery"),
|
||||
"Time of last transfer to battery since apcupsd startup.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
LastTransferOffBattery: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "", "apcupsd_last_transfer_off_battery"),
|
||||
"Time of last transfer from battery since apcupsd startup.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
LastSelftest: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "", "apcupsd_last_selftest"),
|
||||
"Time of last selftest since apcupsd startup.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
NominalPowerWatts: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, "", "apcupsd_nominal_power_watts"),
|
||||
"Nominal power output in watts.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
ss: ss,
|
||||
}
|
||||
}
|
||||
|
@ -198,9 +231,53 @@ func (c *UPSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, e
|
|||
labels...,
|
||||
)
|
||||
|
||||
collectTimestamp(
|
||||
ch,
|
||||
c.LastTransferOnBattery,
|
||||
s.XOnBattery,
|
||||
labels...,
|
||||
)
|
||||
|
||||
collectTimestamp(
|
||||
ch,
|
||||
c.LastTransferOffBattery,
|
||||
s.XOffBattery,
|
||||
labels...,
|
||||
)
|
||||
|
||||
collectTimestamp(
|
||||
ch,
|
||||
c.LastSelftest,
|
||||
s.LastSelftest,
|
||||
labels...,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.NominalPowerWatts,
|
||||
prometheus.GaugeValue,
|
||||
float64(s.NominalPower),
|
||||
labels...,
|
||||
)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// collectTimestamp collects timestamp metrics.
|
||||
// Timestamps that are zero (time.IsZero() == true) are ignored, as such a timestamp indicates
|
||||
// 'information not available', which is best expressed in Prometheus by not having the metric at all.
|
||||
func collectTimestamp(ch chan<- prometheus.Metric, desc *prometheus.Desc, time time.Time, labelValues ...string) {
|
||||
if time.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
desc,
|
||||
prometheus.GaugeValue,
|
||||
float64(time.Unix()),
|
||||
labelValues...,
|
||||
)
|
||||
}
|
||||
|
||||
// Describe sends the descriptors of each metric over to the provided channel.
|
||||
// The corresponding metric values are sent separately.
|
||||
func (c *UPSCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
|
|
|
@ -42,6 +42,10 @@ func TestUPSCollector(t *testing.T) {
|
|||
LineVoltage: 121.1,
|
||||
LoadPercent: 16.0,
|
||||
NumberTransfers: 1,
|
||||
XOnBattery: time.Unix(100001, 0),
|
||||
XOffBattery: time.Unix(100002, 0),
|
||||
LastSelftest: time.Unix(100003, 0),
|
||||
NominalPower: 50.0,
|
||||
},
|
||||
},
|
||||
matches: []*regexp.Regexp{
|
||||
|
@ -55,6 +59,10 @@ func TestUPSCollector(t *testing.T) {
|
|||
regexp.MustCompile(`apcupsd_line_nominal_volts{hostname="a",model="b",ups_name="c"} 120`),
|
||||
regexp.MustCompile(`apcupsd_line_volts{hostname="a",model="b",ups_name="c"} 121.1`),
|
||||
regexp.MustCompile(`apcupsd_ups_load_percent{hostname="a",model="b",ups_name="c"} 16`),
|
||||
regexp.MustCompile(`apcupsd_last_transfer_on_battery{hostname="a",model="b",ups_name="c"} 100001`),
|
||||
regexp.MustCompile(`apcupsd_last_transfer_off_battery{hostname="a",model="b",ups_name="c"} 100002`),
|
||||
regexp.MustCompile(`apcupsd_last_selftest{hostname="a",model="b",ups_name="c"} 100003`),
|
||||
regexp.MustCompile(`apcupsd_nominal_power_watts{hostname="a",model="b",ups_name="c"} 50`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -68,7 +76,7 @@ func TestUPSCollector(t *testing.T) {
|
|||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if !m.Match(out) {
|
||||
t.Fatal("\toutput failed to match regex")
|
||||
t.Fatalf("\toutput failed to match regex (regexp: %v)", m)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -76,6 +84,26 @@ func TestUPSCollector(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestZeroTimesAreIgnored tests that times with a zero value (time.IsZero() == true)
|
||||
// are not collected.
|
||||
func TestZeroTimesAreIgnored(t *testing.T) {
|
||||
ss := &testStatusSource{
|
||||
s: &apcupsd.Status{
|
||||
XOnBattery: time.Unix(123456, 0),
|
||||
XOffBattery: time.Time{},
|
||||
},
|
||||
}
|
||||
out := testCollector(t, NewUPSCollector(ss))
|
||||
// Test that in general timestamps are collected.
|
||||
if !regexp.MustCompile(`apcupsd_last_transfer_on_battery.* 123456`).Match(out) {
|
||||
t.Error("non-zero timestamp is not reported properly")
|
||||
}
|
||||
// Test that zero timestamps, however, are ignored.
|
||||
if regexp.MustCompile(`apcupsd_last_transfer_off_battery`).Match(out) {
|
||||
t.Error("Zero time is reported")
|
||||
}
|
||||
}
|
||||
|
||||
func metricName(t *testing.T, metric string) string {
|
||||
ss := strings.Split(metric, " ")
|
||||
if len(ss) != 2 {
|
||||
|
|
Loading…
Reference in New Issue