mirror of
				https://github.com/prometheus/node_exporter.git
				synced 2025-08-20 18:33:52 -07:00 
			
		
		
		
	Revamp the netdev collector to use throw-away ConstMetrics.
This commit is contained in:
		
							parent
							
								
									e180b882d6
								
							
						
					
					
						commit
						13672ced48
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue