diff --git a/README.md b/README.md index ba9e63e9..99764fb8 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ megacli | Exposes RAID statistics from MegaCLI. ntp | Exposes time drift from an NTP server. runit | Exposes service status from [runit](http://smarden.org/runit/). supervisord | Exposes service status from [supervisord](http://supervisord.org/). +systemd | Exposes service and system status from [systemd](http://www.freedesktop.org/wiki/Software/systemd/). tcpstat | Exposes TCP connection status information from `/proc/net/tcp` and `/proc/net/tcp6`. (Warning: the current version has potential performance issues in high load situations.) ## Textfile Collector diff --git a/collector/systemd_linux.go b/collector/systemd_linux.go new file mode 100644 index 00000000..10f239a0 --- /dev/null +++ b/collector/systemd_linux.go @@ -0,0 +1,81 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !nosystemd + +package collector + +import ( + "fmt" + + "github.com/coreos/go-systemd/dbus" + "github.com/prometheus/client_golang/prometheus" +) + +type systemdCollector struct { + unitDesc *prometheus.Desc +} + +var unitStatesName = []string{"active", "activating", "deactivating", "inactive", "failed"} + +func init() { + Factories["systemd"] = NewSystemdCollector +} + +// Takes a prometheus registry and returns a new Collector exposing +// systemd statistics. +func NewSystemdCollector() (Collector, error) { + unitDesc := prometheus.NewDesc( + prometheus.BuildFQName(Namespace, "systemd", "unit_state"), + "Systemd unit", []string{"name", "state"}, nil, + ) + + return &systemdCollector{ + unitDesc: unitDesc, + }, nil +} + +func (c *systemdCollector) Update(ch chan<- prometheus.Metric) (err error) { + units, err := c.listUnits() + if err != nil { + return fmt.Errorf("couldn't get units states: %s", err) + } + + c.collectMetrics(ch, units) + + return nil +} + +func (c *systemdCollector) collectMetrics(ch chan<- prometheus.Metric, units []dbus.UnitStatus) { + for _, unit := range units { + for _, stateName := range unitStatesName { + isActive := 0.0 + if stateName == unit.ActiveState { + isActive = 1.0 + } + ch <- prometheus.MustNewConstMetric( + c.unitDesc, prometheus.GaugeValue, isActive, + unit.Name, stateName) + } + } +} + +func (c *systemdCollector) listUnits() ([]dbus.UnitStatus, error) { + conn, err := dbus.New() + if err != nil { + return nil, fmt.Errorf("couldn't get dbus connection: %s", err) + } + units, err := conn.ListUnits() + conn.Close() + return units, err +} diff --git a/collector/systemd_linux_test.go b/collector/systemd_linux_test.go new file mode 100644 index 00000000..f5390140 --- /dev/null +++ b/collector/systemd_linux_test.go @@ -0,0 +1,74 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collector + +import ( + "testing" + + "github.com/coreos/go-systemd/dbus" + "github.com/prometheus/client_golang/prometheus" +) + +// Creates mock UnitLists +func getUnitListFixtures() [][]dbus.UnitStatus { + fixture1 := []dbus.UnitStatus{ + dbus.UnitStatus{ + Name: "foo", + Description: "foo desc", + LoadState: "loaded", + ActiveState: "active", + SubState: "running", + Followed: "", + Path: "/org/freedesktop/systemd1/unit/foo", + JobId: 0, + JobType: "", + JobPath: "/", + }, + dbus.UnitStatus{ + Name: "bar", + Description: "bar desc", + LoadState: "not-found", + ActiveState: "inactive", + SubState: "dead", + Followed: "", + Path: "/org/freedesktop/systemd1/unit/bar", + JobId: 0, + JobType: "", + JobPath: "/", + }, + } + + fixture2 := []dbus.UnitStatus{} + + return [][]dbus.UnitStatus{fixture1, fixture2} +} + +func TestSystemdCollectorDoesntCrash(t *testing.T) { + c, err := NewSystemdCollector() + if err != nil { + t.Fatal(err) + } + sink := make(chan prometheus.Metric) + go func() { + for { + <-sink + } + }() + + fixtures := getUnitListFixtures() + collector := (c).(*systemdCollector) + for _, units := range fixtures { + collector.collectMetrics(sink, units) + } +}