Netdev tweaks (#1558)

* Check interface name before loading interface data
* Reduce indentation
* Optimize nested netdev map

This change avoids conversion to strings and back.

Signed-off-by: Julian Kornberger <jk+github@digineo.de>
This commit is contained in:
Julian Kornberger 2020-08-24 17:43:27 +02:00 committed by GitHub
parent 0478ddef69
commit 66fb6762bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 148 deletions

View file

@ -19,7 +19,6 @@ package collector
import (
"errors"
"regexp"
"strconv"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
@ -35,8 +34,8 @@ import (
*/
import "C"
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) {
netDev := map[string]map[string]string{}
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (netDevStats, error) {
netDev := netDevStats{}
var ifap, ifa *C.struct_ifaddrs
if C.getifaddrs(&ifap) == -1 {
@ -45,37 +44,35 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Log
defer C.freeifaddrs(ifap)
for ifa = ifap; ifa != nil; ifa = ifa.ifa_next {
if ifa.ifa_addr.sa_family == C.AF_LINK {
dev := C.GoString(ifa.ifa_name)
if ignore != nil && ignore.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
if accept != nil && !accept.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
if ifa.ifa_addr.sa_family != C.AF_LINK {
continue
}
devStats := map[string]string{}
data := (*C.struct_if_data)(ifa.ifa_data)
dev := C.GoString(ifa.ifa_name)
if ignore != nil && ignore.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
if accept != nil && !accept.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
devStats["receive_packets"] = convertFreeBSDCPUTime(uint64(data.ifi_ipackets))
devStats["transmit_packets"] = convertFreeBSDCPUTime(uint64(data.ifi_opackets))
devStats["receive_errs"] = convertFreeBSDCPUTime(uint64(data.ifi_ierrors))
devStats["transmit_errs"] = convertFreeBSDCPUTime(uint64(data.ifi_oerrors))
devStats["receive_bytes"] = convertFreeBSDCPUTime(uint64(data.ifi_ibytes))
devStats["transmit_bytes"] = convertFreeBSDCPUTime(uint64(data.ifi_obytes))
devStats["receive_multicast"] = convertFreeBSDCPUTime(uint64(data.ifi_imcasts))
devStats["transmit_multicast"] = convertFreeBSDCPUTime(uint64(data.ifi_omcasts))
devStats["receive_drop"] = convertFreeBSDCPUTime(uint64(data.ifi_iqdrops))
devStats["transmit_drop"] = convertFreeBSDCPUTime(uint64(data.ifi_oqdrops))
netDev[dev] = devStats
data := (*C.struct_if_data)(ifa.ifa_data)
netDev[dev] = map[string]uint64{
"receive_packets": uint64(data.ifi_ipackets),
"transmit_packets": uint64(data.ifi_opackets),
"receive_errs": uint64(data.ifi_ierrors),
"transmit_errs": uint64(data.ifi_oerrors),
"receive_bytes": uint64(data.ifi_ibytes),
"transmit_bytes": uint64(data.ifi_obytes),
"receive_multicast": uint64(data.ifi_imcasts),
"transmit_multicast": uint64(data.ifi_omcasts),
"receive_drop": uint64(data.ifi_iqdrops),
"transmit_drop": uint64(data.ifi_oqdrops),
}
}
return netDev, nil
}
func convertFreeBSDCPUTime(counter uint64) string {
return strconv.FormatUint(counter, 10)
}

View file

@ -1,53 +0,0 @@
// Copyright 2016 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 !nonetdev
// +build freebsd dragonfly
package collector
import "testing"
type uintToStringTest struct {
in uint64
out string
}
var uinttostringtests = []uintToStringTest{
// Copied base10 values from strconv's tests:
{0, "0"},
{1, "1"},
{12345678, "12345678"},
{1<<31 - 1, "2147483647"},
{1 << 31, "2147483648"},
{1<<31 + 1, "2147483649"},
{1<<32 - 1, "4294967295"},
{1 << 32, "4294967296"},
{1<<32 + 1, "4294967297"},
{1 << 50, "1125899906842624"},
{1<<63 - 1, "9223372036854775807"},
// Some values that convert correctly on amd64, but not on i386.
{0x1bf0c640a, "7500227594"},
{0xbee5df75, "3202735989"},
}
func TestUintToString(t *testing.T) {
for _, test := range uinttostringtests {
is := convertFreeBSDCPUTime(test.in)
if is != test.out {
t.Errorf("convertFreeBSDCPUTime(%v) = %v want %v",
test.in, is, test.out)
}
}
}

View file

@ -20,7 +20,6 @@ import (
"errors"
"fmt"
"regexp"
"strconv"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
@ -43,6 +42,8 @@ type netDevCollector struct {
logger log.Logger
}
type netDevStats map[string]map[string]uint64
func init() {
registerCollector("netdev", defaultEnabled, NewNetDevCollector)
}
@ -109,11 +110,7 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error {
)
c.metricDescs[key] = desc
}
v, err := strconv.ParseFloat(value, 64)
if err != nil {
return fmt.Errorf("invalid value %s in netstats: %w", value, err)
}
ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, v, dev)
ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(value), dev)
}
}
return nil

View file

@ -21,15 +21,14 @@ import (
"fmt"
"net"
"regexp"
"strconv"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"golang.org/x/sys/unix"
)
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) {
netDev := map[string]map[string]string{}
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (netDevStats, error) {
netDev := netDevStats{}
ifs, err := net.Interfaces()
if err != nil {
@ -37,12 +36,6 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Log
}
for _, iface := range ifs {
ifaceData, err := getIfaceData(iface.Index)
if err != nil {
level.Debug(logger).Log("msg", "failed to load data for interface", "device", iface.Name, "err", err)
continue
}
if ignore != nil && ignore.MatchString(iface.Name) {
level.Debug(logger).Log("msg", "Ignoring device", "device", iface.Name)
continue
@ -52,16 +45,22 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Log
continue
}
devStats := map[string]string{}
devStats["receive_packets"] = strconv.FormatUint(ifaceData.Data.Ipackets, 10)
devStats["transmit_packets"] = strconv.FormatUint(ifaceData.Data.Opackets, 10)
devStats["receive_errs"] = strconv.FormatUint(ifaceData.Data.Ierrors, 10)
devStats["transmit_errs"] = strconv.FormatUint(ifaceData.Data.Oerrors, 10)
devStats["receive_bytes"] = strconv.FormatUint(ifaceData.Data.Ibytes, 10)
devStats["transmit_bytes"] = strconv.FormatUint(ifaceData.Data.Obytes, 10)
devStats["receive_multicast"] = strconv.FormatUint(ifaceData.Data.Imcasts, 10)
devStats["transmit_multicast"] = strconv.FormatUint(ifaceData.Data.Omcasts, 10)
netDev[iface.Name] = devStats
ifaceData, err := getIfaceData(iface.Index)
if err != nil {
level.Debug(logger).Log("msg", "failed to load data for interface", "device", iface.Name, "err", err)
continue
}
netDev[iface.Name] = map[string]uint64{
"receive_packets": ifaceData.Data.Ipackets,
"transmit_packets": ifaceData.Data.Opackets,
"receive_errs": ifaceData.Data.Ierrors,
"transmit_errs": ifaceData.Data.Oerrors,
"receive_bytes": ifaceData.Data.Ibytes,
"transmit_bytes": ifaceData.Data.Obytes,
"receive_multicast": ifaceData.Data.Imcasts,
"transmit_multicast": ifaceData.Data.Omcasts,
}
}
return netDev, nil

View file

@ -21,6 +21,7 @@ import (
"io"
"os"
"regexp"
"strconv"
"strings"
"github.com/go-kit/kit/log"
@ -32,7 +33,7 @@ var (
procNetDevFieldSep = regexp.MustCompile(` +`)
)
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) {
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (netDevStats, error) {
file, err := os.Open(procFilePath("net/dev"))
if err != nil {
return nil, err
@ -42,7 +43,7 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Log
return parseNetDevStats(file, ignore, accept, logger)
}
func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) {
func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (netDevStats, error) {
scanner := bufio.NewScanner(r)
scanner.Scan() // skip first header
scanner.Scan()
@ -56,7 +57,7 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp,
transmitHeader := strings.Fields(parts[2])
headerLength := len(receiveHeader) + len(transmitHeader)
netDev := map[string]map[string]string{}
netDev := netDevStats{}
for scanner.Scan() {
line := strings.TrimLeft(scanner.Text(), " ")
parts := procNetDevInterfaceRE.FindStringSubmatch(line)
@ -79,14 +80,26 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp,
return nil, fmt.Errorf("couldn't get values, invalid line in net/dev: %q", parts[2])
}
netDev[dev] = map[string]string{}
devStats := map[string]uint64{}
addStats := func(key, value string) {
v, err := strconv.ParseUint(value, 0, 64)
if err != nil {
level.Debug(logger).Log("msg", "invalid value in netstats", "key", key, "value", value, "err", err)
return
}
devStats[key] = v
}
for i := 0; i < len(receiveHeader); i++ {
netDev[dev]["receive_"+receiveHeader[i]] = values[i]
addStats("receive_"+receiveHeader[i], values[i])
}
for i := 0; i < len(transmitHeader); i++ {
netDev[dev]["transmit_"+transmitHeader[i]] = values[i+len(receiveHeader)]
addStats("transmit_"+transmitHeader[i], values[i+len(receiveHeader)])
}
netDev[dev] = devStats
}
return netDev, scanner.Err()
}

View file

@ -32,16 +32,16 @@ func TestNetDevStatsIgnore(t *testing.T) {
t.Fatal(err)
}
if want, got := "10437182923", netStats["wlan0"]["receive_bytes"]; want != got {
t.Errorf("want netstat wlan0 bytes %s, got %s", want, got)
if want, got := uint64(10437182923), netStats["wlan0"]["receive_bytes"]; want != got {
t.Errorf("want netstat wlan0 bytes %v, got %v", 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 := uint64(68210035552), netStats["eth0"]["receive_bytes"]; want != got {
t.Errorf("want netstat eth0 bytes %v, got %v", 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 := uint64(934), netStats["tun0"]["transmit_packets"]; want != got {
t.Errorf("want netstat tun0 packets %v, got %v", want, got)
}
if want, got := 9, len(netStats); want != got {
@ -52,11 +52,11 @@ func TestNetDevStatsIgnore(t *testing.T) {
t.Error("want fixture interface veth4B09XN to not exist, but it does")
}
if want, got := "0", netStats["ibr10:30"]["receive_fifo"]; want != got {
if want, got := uint64(0), netStats["ibr10:30"]["receive_fifo"]; want != got {
t.Error("want fixture interface ibr10:30 to exist, but it does not")
}
if want, got := "72", netStats["💩0"]["receive_multicast"]; want != got {
if want, got := uint64(72), netStats["💩0"]["receive_multicast"]; want != got {
t.Error("want fixture interface 💩0 to exist, but it does not")
}
}
@ -76,7 +76,7 @@ func TestNetDevStatsAccept(t *testing.T) {
if want, got := 1, len(netStats); want != got {
t.Errorf("want count of devices to be %d, got %d", want, got)
}
if want, got := "72", netStats["💩0"]["receive_multicast"]; want != got {
if want, got := uint64(72), netStats["💩0"]["receive_multicast"]; want != got {
t.Error("want fixture interface 💩0 to exist, but it does not")
}
}

View file

@ -18,7 +18,6 @@ package collector
import (
"errors"
"regexp"
"strconv"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
@ -32,8 +31,8 @@ import (
*/
import "C"
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) {
netDev := map[string]map[string]string{}
func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (netDevStats, error) {
netDev := netDevStats{}
var ifap, ifa *C.struct_ifaddrs
if C.getifaddrs(&ifap) == -1 {
@ -42,30 +41,32 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Log
defer C.freeifaddrs(ifap)
for ifa = ifap; ifa != nil; ifa = ifa.ifa_next {
if ifa.ifa_addr.sa_family == C.AF_LINK {
dev := C.GoString(ifa.ifa_name)
if ignore != nil && ignore.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
if accept != nil && !accept.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
if ifa.ifa_addr.sa_family != C.AF_LINK {
continue
}
devStats := map[string]string{}
data := (*C.struct_if_data)(ifa.ifa_data)
dev := C.GoString(ifa.ifa_name)
if ignore != nil && ignore.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
if accept != nil && !accept.MatchString(dev) {
level.Debug(logger).Log("msg", "Ignoring device", "device", dev)
continue
}
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))
netDev[dev] = devStats
data := (*C.struct_if_data)(ifa.ifa_data)
netDev[dev] = map[string]uint64{
"receive_packets": uint64(data.ifi_ipackets),
"transmit_packets": uint64(data.ifi_opackets),
"receive_errs": uint64(data.ifi_ierrors),
"transmit_errs": uint64(data.ifi_oerrors),
"receive_bytes": uint64(data.ifi_ibytes),
"transmit_bytes": uint64(data.ifi_obytes),
"receive_multicast": uint64(data.ifi_imcasts),
"transmit_multicast": uint64(data.ifi_omcasts),
"receive_drop": uint64(data.ifi_iqdrops),
}
}