From 13672ced48320179ebc52ba79159c33c2c8c5848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Ferrari?= Date: Tue, 15 Sep 2015 15:31:51 +0000 Subject: [PATCH 1/2] Revamp the netdev collector to use throw-away ConstMetrics. --- collector/netdev_freebsd.go | 84 ++++++++++++++--------------- collector/netdev_linux.go | 99 ++++++++++++++-------------------- collector/netdev_linux_test.go | 10 ++-- 3 files changed, 85 insertions(+), 108 deletions(-) diff --git a/collector/netdev_freebsd.go b/collector/netdev_freebsd.go index 68a96bd3..13bc1140 100644 --- a/collector/netdev_freebsd.go +++ b/collector/netdev_freebsd.go @@ -21,11 +21,11 @@ import ( import "C" const ( - netDevSubsystem = "network" + subsystem = "network" ) type netDevCollector struct { - metrics map[string]*prometheus.CounterVec + metricDescs map[string]*prometheus.Desc } func init() { @@ -36,7 +36,7 @@ func init() { // Network device stats. func NewNetDevCollector() (Collector, error) { return &netDevCollector{ - metrics: map[string]*prometheus.CounterVec{}, + metricDescs: map[string]*prometheus.Desc{}, }, nil } @@ -45,39 +45,35 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { if err != nil { return fmt.Errorf("couldn't get netstats: %s", err) } - for direction, devStats := range netDev { - for dev, stats := range devStats { - for t, value := range stats { - key := direction + "_" + t - if _, ok := c.metrics[key]; !ok { - c.metrics[key] = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: Namespace, - Subsystem: netDevSubsystem, - Name: key, - Help: fmt.Sprintf("%s %s from getifaddrs().", t, direction), - }, - []string{"device"}, - ) - } - v, err := strconv.ParseFloat(value, 64) - if err != nil { - return fmt.Errorf("invalid value %s in netstats: %s", value, err) - } - c.metrics[key].WithLabelValues(dev).Set(v) + for dev, devStats := range netDev { + for key, value := range devStats { + desc, ok := c.metricDescs[key] + if !ok { + desc = prometheus.NewDesc( + prometheus.BuildFQName( + Namespace, subsystem, key), + fmt.Sprintf( + "%s from getifaddrs().", key), + []string{"device"}, + nil, + ) + c.metricDescs[key] = desc + } + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf( + "Invalid value %s in netstats: %s", + value, err) } + ch <- prometheus.MustNewConstMetric( + desc, prometheus.GaugeValue, v, dev) } } - for _, m := range c.metrics { - m.Collect(ch) - } - return err + return nil } -func getNetDevStats() (map[string]map[string]map[string]string, error) { - netDev := map[string]map[string]map[string]string{} - netDev["transmit"] = map[string]map[string]string{} - netDev["receive"] = map[string]map[string]string{} +func getNetDevStats() (map[string]map[string]string, error) { + netDev := map[string]map[string]string{} var ifap, ifa *C.struct_ifaddrs if C.getifaddrs(&ifap) == -1 { @@ -87,22 +83,20 @@ func getNetDevStats() (map[string]map[string]map[string]string, error) { for ifa = ifap; ifa != nil; ifa = ifa.ifa_next { if ifa.ifa_addr.sa_family == C.AF_LINK { - receive := map[string]string{} - transmit := map[string]string{} + devStats := map[string]string{} data := (*C.struct_if_data)(ifa.ifa_data) - receive["packets"] = strconv.Itoa(int(data.ifi_ipackets)) - transmit["packets"] = strconv.Itoa(int(data.ifi_opackets)) - receive["errs"] = strconv.Itoa(int(data.ifi_ierrors)) - transmit["errs"] = strconv.Itoa(int(data.ifi_oerrors)) - receive["bytes"] = strconv.Itoa(int(data.ifi_ibytes)) - transmit["bytes"] = strconv.Itoa(int(data.ifi_obytes)) - receive["multicast"] = strconv.Itoa(int(data.ifi_imcasts)) - transmit["multicast"] = strconv.Itoa(int(data.ifi_omcasts)) - receive["drop"] = strconv.Itoa(int(data.ifi_iqdrops)) - transmit["drop"] = strconv.Itoa(int(data.ifi_oqdrops)) - netDev["receive"][C.GoString(ifa.ifa_name)] = receive - netDev["transmit"][C.GoString(ifa.ifa_name)] = transmit + devStats["receive_packets"] = strconv.Itoa(int(data.ifi_ipackets)) + devStats["transmit_packets"] = strconv.Itoa(int(data.ifi_opackets)) + devStats["receive_errs"] = strconv.Itoa(int(data.ifi_ierrors)) + devStats["transmit_errs"] = strconv.Itoa(int(data.ifi_oerrors)) + devStats["receive_bytes"] = strconv.Itoa(int(data.ifi_ibytes)) + devStats["transmit_bytes"] = strconv.Itoa(int(data.ifi_obytes)) + devStats["receive_multicast"] = strconv.Itoa(int(data.ifi_imcasts)) + devStats["transmit_multicast"] = strconv.Itoa(int(data.ifi_omcasts)) + devStats["receive_drop"] = strconv.Itoa(int(data.ifi_iqdrops)) + devStats["transmit_drop"] = strconv.Itoa(int(data.ifi_oqdrops)) + netDev[C.GoString(ifa.ifa_name)] = devStats } } diff --git a/collector/netdev_linux.go b/collector/netdev_linux.go index ee1de005..c9ee3c3e 100644 --- a/collector/netdev_linux.go +++ b/collector/netdev_linux.go @@ -17,19 +17,20 @@ import ( ) const ( - procNetDev = "/proc/net/dev" - netDevSubsystem = "network" + procNetDev = "/proc/net/dev" + subsystem = "network" ) var ( - fieldSep = regexp.MustCompile("[ :] *") - netdevIgnoredDevices = flag.String("collector.netdev.ignored-devices", "^$", "Regexp of net devices to ignore for netdev collector.") + fieldSep = regexp.MustCompile("[ :] *") + netdevIgnoredDevices = flag.String( + "collector.netdev.ignored-devices", "^$", + "Regexp of net devices to ignore for netdev collector.") ) type netDevCollector struct { ignoredDevicesPattern *regexp.Regexp - - metrics map[string]*prometheus.GaugeVec + metricDescs map[string]*prometheus.Desc } func init() { @@ -39,9 +40,10 @@ func init() { // Takes a prometheus registry and returns a new Collector exposing // network device stats. func NewNetDevCollector() (Collector, error) { + pattern := regexp.MustCompile(*netdevIgnoredDevices) return &netDevCollector{ - ignoredDevicesPattern: regexp.MustCompile(*netdevIgnoredDevices), - metrics: map[string]*prometheus.GaugeVec{}, + ignoredDevicesPattern: pattern, + metricDescs: map[string]*prometheus.Desc{}, }, nil } @@ -50,36 +52,34 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { if err != nil { return fmt.Errorf("Couldn't get netstats: %s", err) } - for direction, devStats := range netDev { - for dev, stats := range devStats { - for t, value := range stats { - key := direction + "_" + t - if _, ok := c.metrics[key]; !ok { - c.metrics[key] = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: Namespace, - Subsystem: netDevSubsystem, - Name: key, - Help: fmt.Sprintf("%s %s from /proc/net/dev.", t, direction), - }, - []string{"device"}, - ) - } - v, err := strconv.ParseFloat(value, 64) - if err != nil { - return fmt.Errorf("Invalid value %s in netstats: %s", value, err) - } - c.metrics[key].WithLabelValues(dev).Set(v) + for dev, devStats := range netDev { + for key, value := range devStats { + desc, ok := c.metricDescs[key] + if !ok { + desc = prometheus.NewDesc( + prometheus.BuildFQName( + Namespace, subsystem, key), + fmt.Sprintf( + "%s from /proc/net/dev.", key), + []string{"device"}, + nil, + ) + c.metricDescs[key] = desc + } + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf( + "Invalid value %s in netstats: %s", + value, err) } + ch <- prometheus.MustNewConstMetric( + desc, prometheus.GaugeValue, v, dev) } } - for _, m := range c.metrics { - m.Collect(ch) - } - return err + return nil } -func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]map[string]string, error) { +func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error) { file, err := os.Open(procNetDev) if err != nil { return nil, err @@ -89,11 +89,7 @@ func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]map[string]str return parseNetDevStats(file, ignore) } -func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string]map[string]string, error) { - netDev := map[string]map[string]map[string]string{} - netDev["transmit"] = map[string]map[string]string{} - netDev["receive"] = map[string]map[string]string{} - +func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string]string, error) { scanner := bufio.NewScanner(r) scanner.Scan() // skip first header scanner.Scan() @@ -102,7 +98,9 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string return nil, fmt.Errorf("Invalid header line in %s: %s", procNetDev, scanner.Text()) } + header := strings.Fields(parts[1]) + netDev := map[string]map[string]string{} for scanner.Scan() { line := strings.TrimLeft(string(scanner.Text()), " ") parts := fieldSep.Split(line, -1) @@ -112,30 +110,15 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string } dev := parts[0][:len(parts[0])] - receive, err := parseNetDevLine(parts[1:len(header)+1], header) - if err != nil { - return nil, err - } - - transmit, err := parseNetDevLine(parts[len(header)+1:], header) - if err != nil { - return nil, err - } - if ignore.MatchString(dev) { log.Debugf("Ignoring device: %s", dev) continue } - netDev["transmit"][dev] = transmit - netDev["receive"][dev] = receive + netDev[dev] = map[string]string{} + for i, v := range header { + netDev[dev]["receive_" + v] = parts[i+1] + netDev[dev]["transmit_" + v] = parts[i+1+len(header)] + } } return netDev, nil } - -func parseNetDevLine(parts []string, header []string) (map[string]string, error) { - devStats := map[string]string{} - for i, v := range parts { - devStats[header[i]] = v - } - return devStats, nil -} diff --git a/collector/netdev_linux_test.go b/collector/netdev_linux_test.go index ce60ca3b..ef0b5186 100644 --- a/collector/netdev_linux_test.go +++ b/collector/netdev_linux_test.go @@ -18,23 +18,23 @@ func TestNetDevStats(t *testing.T) { t.Fatal(err) } - if want, got := "10437182923", netStats["receive"]["wlan0"]["bytes"]; want != got { + if want, got := "10437182923", netStats["wlan0"]["receive_bytes"]; want != got { t.Errorf("want netstat wlan0 bytes %s, got %s", want, got) } - if want, got := "68210035552", netStats["receive"]["eth0"]["bytes"]; want != got { + if want, got := "68210035552", netStats["eth0"]["receive_bytes"]; want != got { t.Errorf("want netstat eth0 bytes %s, got %s", want, got) } - if want, got := "934", netStats["transmit"]["tun0"]["packets"]; want != got { + if want, got := "934", netStats["tun0"]["transmit_packets"]; want != got { t.Errorf("want netstat tun0 packets %s, got %s", want, got) } - if want, got := 6, len(netStats["transmit"]); want != got { + if want, got := 6, len(netStats); want != got { t.Errorf("want count of devices to be %d, got %d", want, got) } - if _, ok := netStats["transmit"]["veth4B09XN"]; ok != false { + if _, ok := netStats["veth4B09XN"]["transmit_bytes"]; ok != false { t.Error("want fixture interface veth4B09XN to not exist, but it does") } } From 0161a36fa56b4c619cc3820ef0ff205e720996d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Ferrari?= Date: Wed, 16 Sep 2015 17:06:59 +0300 Subject: [PATCH 2/2] Fix formatting and variable names. --- collector/netdev_freebsd.go | 37 +++++++++++---------------- collector/netdev_linux.go | 51 +++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 50 deletions(-) diff --git a/collector/netdev_freebsd.go b/collector/netdev_freebsd.go index 13bc1140..d0d7be44 100644 --- a/collector/netdev_freebsd.go +++ b/collector/netdev_freebsd.go @@ -20,11 +20,8 @@ import ( */ import "C" -const ( - subsystem = "network" -) - type netDevCollector struct { + subsystem string metricDescs map[string]*prometheus.Desc } @@ -36,6 +33,7 @@ func init() { // Network device stats. func NewNetDevCollector() (Collector, error) { return &netDevCollector{ + subsystem: "network", metricDescs: map[string]*prometheus.Desc{}, }, nil } @@ -47,26 +45,21 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { } for dev, devStats := range netDev { for key, value := range devStats { - desc, ok := c.metricDescs[key] - if !ok { - desc = prometheus.NewDesc( - prometheus.BuildFQName( - Namespace, subsystem, key), - fmt.Sprintf( - "%s from getifaddrs().", key), - []string{"device"}, - nil, - ) - c.metricDescs[key] = desc - } + desc, ok := c.metricDescs[key] + if !ok { + desc = prometheus.NewDesc( + prometheus.BuildFQName(Namespace, c.subsystem, key), + fmt.Sprintf("%s from getifaddrs().", key), + []string{"device"}, + nil, + ) + c.metricDescs[key] = desc + } v, err := strconv.ParseFloat(value, 64) if err != nil { - return fmt.Errorf( - "Invalid value %s in netstats: %s", - value, err) + return fmt.Errorf("invalid value %s in netstats: %s", value, err) } - ch <- prometheus.MustNewConstMetric( - desc, prometheus.GaugeValue, v, dev) + ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, v, dev) } } return nil @@ -96,7 +89,7 @@ func getNetDevStats() (map[string]map[string]string, error) { devStats["transmit_multicast"] = strconv.Itoa(int(data.ifi_omcasts)) devStats["receive_drop"] = strconv.Itoa(int(data.ifi_iqdrops)) devStats["transmit_drop"] = strconv.Itoa(int(data.ifi_oqdrops)) - netDev[C.GoString(ifa.ifa_name)] = devStats + netDev[C.GoString(ifa.ifa_name)] = devStats } } diff --git a/collector/netdev_linux.go b/collector/netdev_linux.go index c9ee3c3e..f82d50e1 100644 --- a/collector/netdev_linux.go +++ b/collector/netdev_linux.go @@ -18,19 +18,19 @@ import ( const ( procNetDev = "/proc/net/dev" - subsystem = "network" ) var ( - fieldSep = regexp.MustCompile("[ :] *") + procNetDevFieldSep = regexp.MustCompile("[ :] *") netdevIgnoredDevices = flag.String( "collector.netdev.ignored-devices", "^$", "Regexp of net devices to ignore for netdev collector.") ) type netDevCollector struct { + subsystem string ignoredDevicesPattern *regexp.Regexp - metricDescs map[string]*prometheus.Desc + metricDescs map[string]*prometheus.Desc } func init() { @@ -42,38 +42,34 @@ func init() { func NewNetDevCollector() (Collector, error) { pattern := regexp.MustCompile(*netdevIgnoredDevices) return &netDevCollector{ + subsystem: "network", ignoredDevicesPattern: pattern, - metricDescs: map[string]*prometheus.Desc{}, + metricDescs: map[string]*prometheus.Desc{}, }, nil } func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { netDev, err := getNetDevStats(c.ignoredDevicesPattern) if err != nil { - return fmt.Errorf("Couldn't get netstats: %s", err) + return fmt.Errorf("couldn't get netstats: %s", err) } for dev, devStats := range netDev { for key, value := range devStats { - desc, ok := c.metricDescs[key] - if !ok { - desc = prometheus.NewDesc( - prometheus.BuildFQName( - Namespace, subsystem, key), - fmt.Sprintf( - "%s from /proc/net/dev.", key), - []string{"device"}, - nil, - ) - c.metricDescs[key] = desc - } + desc, ok := c.metricDescs[key] + if !ok { + desc = prometheus.NewDesc( + prometheus.BuildFQName(Namespace, c.subsystem, key), + fmt.Sprintf("%s from /proc/net/dev.", key), + []string{"device"}, + nil, + ) + c.metricDescs[key] = desc + } v, err := strconv.ParseFloat(value, 64) if err != nil { - return fmt.Errorf( - "Invalid value %s in netstats: %s", - value, err) + return fmt.Errorf("invalid value %s in netstats: %s", value, err) } - ch <- prometheus.MustNewConstMetric( - desc, prometheus.GaugeValue, v, dev) + ch <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, v, dev) } } return nil @@ -95,7 +91,7 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string scanner.Scan() parts := strings.Split(string(scanner.Text()), "|") if len(parts) != 3 { // interface + receive + transmit - return nil, fmt.Errorf("Invalid header line in %s: %s", + return nil, fmt.Errorf("invalid header line in %s: %s", procNetDev, scanner.Text()) } @@ -103,10 +99,9 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string netDev := map[string]map[string]string{} for scanner.Scan() { line := strings.TrimLeft(string(scanner.Text()), " ") - parts := fieldSep.Split(line, -1) + parts := procNetDevFieldSep.Split(line, -1) if len(parts) != 2*len(header)+1 { - return nil, fmt.Errorf("Invalid line in %s: %s", - procNetDev, scanner.Text()) + return nil, fmt.Errorf("invalid line in %s: %s", procNetDev, scanner.Text()) } dev := parts[0][:len(parts[0])] @@ -116,8 +111,8 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string } netDev[dev] = map[string]string{} for i, v := range header { - netDev[dev]["receive_" + v] = parts[i+1] - netDev[dev]["transmit_" + v] = parts[i+1+len(header)] + netDev[dev]["receive_"+v] = parts[i+1] + netDev[dev]["transmit_"+v] = parts[i+1+len(header)] } } return netDev, nil