mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-31 16:37:31 -08:00
Merge pull request #807 from tomwilkie/systemd-timers
Export systemd timers last trigger seconds.
This commit is contained in:
commit
05d14ef9ee
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
return allUnits, nil
|
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 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…
Reference in a new issue