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>
pull/1821/head
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 ( import (
"errors" "errors"
"regexp" "regexp"
"strconv"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level" "github.com/go-kit/kit/log/level"
@ -35,8 +34,8 @@ import (
*/ */
import "C" import "C"
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) {
netDev := map[string]map[string]string{} netDev := netDevStats{}
var ifap, ifa *C.struct_ifaddrs var ifap, ifa *C.struct_ifaddrs
if C.getifaddrs(&ifap) == -1 { if C.getifaddrs(&ifap) == -1 {
@ -45,37 +44,35 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Log
defer C.freeifaddrs(ifap) defer C.freeifaddrs(ifap)
for ifa = ifap; ifa != nil; ifa = ifa.ifa_next { for ifa = ifap; ifa != nil; ifa = ifa.ifa_next {
if ifa.ifa_addr.sa_family == C.AF_LINK { if ifa.ifa_addr.sa_family != C.AF_LINK {
dev := C.GoString(ifa.ifa_name) continue
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 := map[string]string{} dev := C.GoString(ifa.ifa_name)
data := (*C.struct_if_data)(ifa.ifa_data) 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)) data := (*C.struct_if_data)(ifa.ifa_data)
devStats["transmit_packets"] = convertFreeBSDCPUTime(uint64(data.ifi_opackets))
devStats["receive_errs"] = convertFreeBSDCPUTime(uint64(data.ifi_ierrors)) netDev[dev] = map[string]uint64{
devStats["transmit_errs"] = convertFreeBSDCPUTime(uint64(data.ifi_oerrors)) "receive_packets": uint64(data.ifi_ipackets),
devStats["receive_bytes"] = convertFreeBSDCPUTime(uint64(data.ifi_ibytes)) "transmit_packets": uint64(data.ifi_opackets),
devStats["transmit_bytes"] = convertFreeBSDCPUTime(uint64(data.ifi_obytes)) "receive_errs": uint64(data.ifi_ierrors),
devStats["receive_multicast"] = convertFreeBSDCPUTime(uint64(data.ifi_imcasts)) "transmit_errs": uint64(data.ifi_oerrors),
devStats["transmit_multicast"] = convertFreeBSDCPUTime(uint64(data.ifi_omcasts)) "receive_bytes": uint64(data.ifi_ibytes),
devStats["receive_drop"] = convertFreeBSDCPUTime(uint64(data.ifi_iqdrops)) "transmit_bytes": uint64(data.ifi_obytes),
devStats["transmit_drop"] = convertFreeBSDCPUTime(uint64(data.ifi_oqdrops)) "receive_multicast": uint64(data.ifi_imcasts),
netDev[dev] = devStats "transmit_multicast": uint64(data.ifi_omcasts),
"receive_drop": uint64(data.ifi_iqdrops),
"transmit_drop": uint64(data.ifi_oqdrops),
} }
} }
return netDev, nil 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" "errors"
"fmt" "fmt"
"regexp" "regexp"
"strconv"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level" "github.com/go-kit/kit/log/level"
@ -43,6 +42,8 @@ type netDevCollector struct {
logger log.Logger logger log.Logger
} }
type netDevStats map[string]map[string]uint64
func init() { func init() {
registerCollector("netdev", defaultEnabled, NewNetDevCollector) registerCollector("netdev", defaultEnabled, NewNetDevCollector)
} }
@ -109,11 +110,7 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error {
) )
c.metricDescs[key] = desc c.metricDescs[key] = desc
} }
v, err := strconv.ParseFloat(value, 64) ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, float64(value), dev)
if err != nil {
return fmt.Errorf("invalid value %s in netstats: %w", value, err)
}
ch <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, v, dev)
} }
} }
return nil return nil

View File

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

View File

@ -21,6 +21,7 @@ import (
"io" "io"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
@ -32,7 +33,7 @@ var (
procNetDevFieldSep = regexp.MustCompile(` +`) 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")) file, err := os.Open(procFilePath("net/dev"))
if err != nil { if err != nil {
return nil, err return nil, err
@ -42,7 +43,7 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Log
return parseNetDevStats(file, ignore, accept, logger) 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 := bufio.NewScanner(r)
scanner.Scan() // skip first header scanner.Scan() // skip first header
scanner.Scan() scanner.Scan()
@ -56,7 +57,7 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp,
transmitHeader := strings.Fields(parts[2]) transmitHeader := strings.Fields(parts[2])
headerLength := len(receiveHeader) + len(transmitHeader) headerLength := len(receiveHeader) + len(transmitHeader)
netDev := map[string]map[string]string{} netDev := netDevStats{}
for scanner.Scan() { for scanner.Scan() {
line := strings.TrimLeft(scanner.Text(), " ") line := strings.TrimLeft(scanner.Text(), " ")
parts := procNetDevInterfaceRE.FindStringSubmatch(line) 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]) 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++ { 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++ { 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() return netDev, scanner.Err()
} }

View File

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

View File

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