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:
Rolf Schäuble 2017-04-14 19:48:42 +02:00 committed by Matt Layher
parent 31b4b80693
commit 290f3ea806
2 changed files with 106 additions and 1 deletions

View File

@ -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) {

View File

@ -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 {