diff --git a/collector/netdev_freebsd.go b/collector/netdev_freebsd.go index 68a96bd3..d0d7be44 100644 --- a/collector/netdev_freebsd.go +++ b/collector/netdev_freebsd.go @@ -20,12 +20,9 @@ import ( */ import "C" -const ( - netDevSubsystem = "network" -) - type netDevCollector struct { - metrics map[string]*prometheus.CounterVec + subsystem string + metricDescs map[string]*prometheus.Desc } func init() { @@ -36,7 +33,8 @@ func init() { // Network device stats. func NewNetDevCollector() (Collector, error) { return &netDevCollector{ - metrics: map[string]*prometheus.CounterVec{}, + subsystem: "network", + metricDescs: map[string]*prometheus.Desc{}, }, nil } @@ -45,39 +43,30 @@ 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, 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) + } + 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 +76,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..f82d50e1 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" ) var ( - fieldSep = regexp.MustCompile("[ :] *") - netdevIgnoredDevices = flag.String("collector.netdev.ignored-devices", "^$", "Regexp of net devices to ignore for netdev collector.") + 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 - - metrics map[string]*prometheus.GaugeVec + metricDescs map[string]*prometheus.Desc } func init() { @@ -39,47 +40,42 @@ 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{}, + subsystem: "network", + ignoredDevicesPattern: pattern, + 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 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, 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) + } + 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,53 +85,35 @@ 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() 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()) } + 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) + 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])] - 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") } }