Merge pull request #807 from tomwilkie/systemd-timers

Export systemd timers last trigger seconds.
pull/727/merge
Tom Wilkie 7 years ago committed by GitHub
commit 05d14ef9ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,6 +18,7 @@ package collector
import ( import (
"fmt" "fmt"
"regexp" "regexp"
"strings"
"github.com/coreos/go-systemd/dbus" "github.com/coreos/go-systemd/dbus"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@ -35,6 +36,7 @@ type systemdCollector struct {
unitDesc *prometheus.Desc unitDesc *prometheus.Desc
systemRunningDesc *prometheus.Desc systemRunningDesc *prometheus.Desc
summaryDesc *prometheus.Desc summaryDesc *prometheus.Desc
timerLastTriggerDesc *prometheus.Desc
unitWhitelistPattern *regexp.Regexp unitWhitelistPattern *regexp.Regexp
unitBlacklistPattern *regexp.Regexp unitBlacklistPattern *regexp.Regexp
} }
@ -61,6 +63,9 @@ func NewSystemdCollector() (Collector, error) {
summaryDesc := prometheus.NewDesc( summaryDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "units"), prometheus.BuildFQName(namespace, subsystem, "units"),
"Summary of systemd unit states", []string{"state"}, nil) "Summary of systemd unit states", []string{"state"}, nil)
timerLastTriggerDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "timer_last_trigger_seconds"),
"Seconds since epoch of last trigger.", []string{"name"}, nil)
unitWhitelistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitWhitelist)) unitWhitelistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitWhitelist))
unitBlacklistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitBlacklist)) unitBlacklistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitBlacklist))
@ -68,6 +73,7 @@ func NewSystemdCollector() (Collector, error) {
unitDesc: unitDesc, unitDesc: unitDesc,
systemRunningDesc: systemRunningDesc, systemRunningDesc: systemRunningDesc,
summaryDesc: summaryDesc, summaryDesc: summaryDesc,
timerLastTriggerDesc: timerLastTriggerDesc,
unitWhitelistPattern: unitWhitelistPattern, unitWhitelistPattern: unitWhitelistPattern,
unitBlacklistPattern: unitBlacklistPattern, unitBlacklistPattern: unitBlacklistPattern,
}, nil }, nil
@ -84,6 +90,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error {
units := filterUnits(allUnits, c.unitWhitelistPattern, c.unitBlacklistPattern) units := filterUnits(allUnits, c.unitWhitelistPattern, c.unitBlacklistPattern)
c.collectUnitStatusMetrics(ch, units) c.collectUnitStatusMetrics(ch, units)
c.collectTimers(ch, units)
systemState, err := c.getSystemState() systemState, err := c.getSystemState()
if err != nil { if err != nil {
@ -94,7 +101,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error {
return nil return nil
} }
func (c *systemdCollector) collectUnitStatusMetrics(ch chan<- prometheus.Metric, units []dbus.UnitStatus) { func (c *systemdCollector) collectUnitStatusMetrics(ch chan<- prometheus.Metric, units []unit) {
for _, unit := range units { for _, unit := range units {
for _, stateName := range unitStatesName { for _, stateName := range unitStatesName {
isActive := 0.0 isActive := 0.0
@ -108,6 +115,19 @@ func (c *systemdCollector) collectUnitStatusMetrics(ch chan<- prometheus.Metric,
} }
} }
func (c *systemdCollector) collectTimers(ch chan<- prometheus.Metric, units []unit) error {
for _, unit := range units {
if !strings.HasSuffix(unit.Name, ".timer") {
continue
}
ch <- prometheus.MustNewConstMetric(
c.timerLastTriggerDesc, prometheus.GaugeValue,
float64(unit.lastTriggerUsec)/1e6, unit.Name)
}
return nil
}
func (c *systemdCollector) collectSummaryMetrics(ch chan<- prometheus.Metric, summary map[string]float64) { func (c *systemdCollector) collectSummaryMetrics(ch chan<- prometheus.Metric, summary map[string]float64) {
for stateName, count := range summary { for stateName, count := range summary {
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
@ -130,22 +150,45 @@ func (c *systemdCollector) newDbus() (*dbus.Conn, error) {
return dbus.New() return dbus.New()
} }
func (c *systemdCollector) getAllUnits() ([]dbus.UnitStatus, error) { type unit struct {
dbus.UnitStatus
lastTriggerUsec uint64
}
func (c *systemdCollector) getAllUnits() ([]unit, error) {
conn, err := c.newDbus() conn, err := c.newDbus()
if err != nil { if err != nil {
return nil, fmt.Errorf("couldn't get dbus connection: %s", err) return nil, fmt.Errorf("couldn't get dbus connection: %s", err)
} }
allUnits, err := conn.ListUnits() defer conn.Close()
conn.Close()
allUnits, err := conn.ListUnits()
if err != nil { if err != nil {
return []dbus.UnitStatus{}, err return nil, err
}
result := make([]unit, 0, len(allUnits))
for _, status := range allUnits {
unit := unit{
UnitStatus: status,
}
if strings.HasSuffix(unit.Name, ".timer") {
lastTriggerValue, err := conn.GetUnitTypeProperty(unit.Name, "Timer", "LastTriggerUSec")
if err != nil {
return nil, fmt.Errorf("couldn't get unit '%s' LastTriggerUSec: %s", unit.Name, err)
}
unit.lastTriggerUsec = lastTriggerValue.Value.Value().(uint64)
}
result = append(result, unit)
} }
return allUnits, nil return result, nil
} }
func summarizeUnits(units []dbus.UnitStatus) map[string]float64 { func summarizeUnits(units []unit) map[string]float64 {
summarized := make(map[string]float64) summarized := make(map[string]float64)
for _, unitStateName := range unitStatesName { for _, unitStateName := range unitStatesName {
@ -159,8 +202,8 @@ func summarizeUnits(units []dbus.UnitStatus) map[string]float64 {
return summarized return summarized
} }
func filterUnits(units []dbus.UnitStatus, whitelistPattern, blacklistPattern *regexp.Regexp) []dbus.UnitStatus { func filterUnits(units []unit, whitelistPattern, blacklistPattern *regexp.Regexp) []unit {
filtered := make([]dbus.UnitStatus, 0, len(units)) filtered := make([]unit, 0, len(units))
for _, unit := range units { for _, unit := range units {
if whitelistPattern.MatchString(unit.Name) && !blacklistPattern.MatchString(unit.Name) { if whitelistPattern.MatchString(unit.Name) && !blacklistPattern.MatchString(unit.Name) {
filtered = append(filtered, unit) filtered = append(filtered, unit)

@ -22,61 +22,69 @@ import (
) )
// Creates mock UnitLists // Creates mock UnitLists
func getUnitListFixtures() [][]dbus.UnitStatus { func getUnitListFixtures() [][]unit {
fixture1 := []dbus.UnitStatus{ fixture1 := []unit{
{ {
Name: "foo", UnitStatus: dbus.UnitStatus{
Description: "foo desc", Name: "foo",
LoadState: "loaded", Description: "foo desc",
ActiveState: "active", LoadState: "loaded",
SubState: "running", ActiveState: "active",
Followed: "", SubState: "running",
Path: "/org/freedesktop/systemd1/unit/foo", Followed: "",
JobId: 0, Path: "/org/freedesktop/systemd1/unit/foo",
JobType: "", JobId: 0,
JobPath: "/", JobType: "",
JobPath: "/",
},
}, },
{ {
Name: "bar", UnitStatus: dbus.UnitStatus{
Description: "bar desc", Name: "bar",
LoadState: "not-found", Description: "bar desc",
ActiveState: "inactive", LoadState: "not-found",
SubState: "dead", ActiveState: "inactive",
Followed: "", SubState: "dead",
Path: "/org/freedesktop/systemd1/unit/bar", Followed: "",
JobId: 0, Path: "/org/freedesktop/systemd1/unit/bar",
JobType: "", JobId: 0,
JobPath: "/", JobType: "",
JobPath: "/",
},
}, },
{ {
Name: "foobar", UnitStatus: dbus.UnitStatus{
Description: "bar desc", Name: "foobar",
LoadState: "not-found", Description: "bar desc",
ActiveState: "inactive", LoadState: "not-found",
SubState: "dead", ActiveState: "inactive",
Followed: "", SubState: "dead",
Path: "/org/freedesktop/systemd1/unit/bar", Followed: "",
JobId: 0, Path: "/org/freedesktop/systemd1/unit/bar",
JobType: "", JobId: 0,
JobPath: "/", JobType: "",
JobPath: "/",
},
}, },
{ {
Name: "baz", UnitStatus: dbus.UnitStatus{
Description: "bar desc", Name: "baz",
LoadState: "not-found", Description: "bar desc",
ActiveState: "inactive", LoadState: "not-found",
SubState: "dead", ActiveState: "inactive",
Followed: "", SubState: "dead",
Path: "/org/freedesktop/systemd1/unit/bar", Followed: "",
JobId: 0, Path: "/org/freedesktop/systemd1/unit/bar",
JobType: "", JobId: 0,
JobPath: "/", JobType: "",
JobPath: "/",
},
}, },
} }
fixture2 := []dbus.UnitStatus{} fixture2 := []unit{}
return [][]dbus.UnitStatus{fixture1, fixture2} return [][]unit{fixture1, fixture2}
} }
func TestSystemdCollectorDoesntCrash(t *testing.T) { func TestSystemdCollectorDoesntCrash(t *testing.T) {

Loading…
Cancel
Save