From 96eaff8c7e9bd99cec62559cdbb1a07fca5c0ba4 Mon Sep 17 00:00:00 2001 From: Brian Brazil Date: Tue, 11 Nov 2014 15:24:35 +0000 Subject: [PATCH] Add an exporter for /proc/net/netstat, enabled by default. This catches things like listen overflows, retransmits and other things that are very useful for retroactive debugging thus I think it's justified to have it on by default. --- collector/netdev.go | 38 ++++++++-------- collector/netstat.go | 102 +++++++++++++++++++++++++++++++++++++++++++ node_exporter.go | 2 +- 3 files changed, 122 insertions(+), 20 deletions(-) create mode 100644 collector/netstat.go diff --git a/collector/netdev.go b/collector/netdev.go index 2d2a58ab..348d1bbd 100644 --- a/collector/netdev.go +++ b/collector/netdev.go @@ -14,12 +14,12 @@ import ( ) const ( - procNetDev = "/proc/net/dev" - netStatsSubsystem = "network" + procNetDev = "/proc/net/dev" + netDevSubsystem = "network" ) var ( - netStatsMetrics = map[string]*prometheus.GaugeVec{} + netDevMetrics = map[string]*prometheus.GaugeVec{} ) type netDevCollector struct { @@ -40,19 +40,19 @@ func NewNetDevCollector(config Config) (Collector, error) { } func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { - netStats, err := getNetStats() + netDev, err := getNetDevStats() if err != nil { return fmt.Errorf("Couldn't get netstats: %s", err) } - for direction, devStats := range netStats { + for direction, devStats := range netDev { for dev, stats := range devStats { for t, value := range stats { key := direction + "_" + t - if _, ok := netStatsMetrics[key]; !ok { - netStatsMetrics[key] = prometheus.NewGaugeVec( + if _, ok := netDevMetrics[key]; !ok { + netDevMetrics[key] = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Namespace: Namespace, - Subsystem: netStatsSubsystem, + Subsystem: netDevSubsystem, Name: key, Help: fmt.Sprintf("%s %s from /proc/net/dev.", t, direction), }, @@ -63,29 +63,29 @@ func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) { if err != nil { return fmt.Errorf("Invalid value %s in netstats: %s", value, err) } - netStatsMetrics[key].WithLabelValues(dev).Set(v) + netDevMetrics[key].WithLabelValues(dev).Set(v) } } } - for _, m := range netStatsMetrics { + for _, m := range netDevMetrics { m.Collect(ch) } return err } -func getNetStats() (map[string]map[string]map[string]string, error) { +func getNetDevStats() (map[string]map[string]map[string]string, error) { file, err := os.Open(procNetDev) if err != nil { return nil, err } - return parseNetStats(file) + return parseNetDevStats(file) } -func parseNetStats(r io.ReadCloser) (map[string]map[string]map[string]string, error) { +func parseNetDevStats(r io.ReadCloser) (map[string]map[string]map[string]string, error) { defer r.Close() - netStats := map[string]map[string]map[string]string{} - netStats["transmit"] = map[string]map[string]string{} - netStats["receive"] = map[string]map[string]string{} + netDev := map[string]map[string]map[string]string{} + netDev["transmit"] = map[string]map[string]string{} + netDev["receive"] = map[string]map[string]string{} scanner := bufio.NewScanner(r) scanner.Scan() // skip first header @@ -113,10 +113,10 @@ func parseNetStats(r io.ReadCloser) (map[string]map[string]map[string]string, er if err != nil { return nil, err } - netStats["transmit"][dev] = transmit - netStats["receive"][dev] = receive + netDev["transmit"][dev] = transmit + netDev["receive"][dev] = receive } - return netStats, nil + return netDev, nil } func parseNetDevLine(parts []string, header []string) (map[string]string, error) { diff --git a/collector/netstat.go b/collector/netstat.go new file mode 100644 index 00000000..66e9d778 --- /dev/null +++ b/collector/netstat.go @@ -0,0 +1,102 @@ +// +build !nonetStat + +package collector + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" + + "github.com/prometheus/client_golang/prometheus" +) + +const ( + procNetStat = "/proc/net/netstat" + netStatsSubsystem = "netstat" +) + +var ( + netStatsMetrics = map[string]prometheus.Gauge{} +) + +type netStatCollector struct { + config Config +} + +func init() { + Factories["netstat"] = NewNetStatCollector +} + +// NewNetStatCollector takes a config struct and returns +// a new Collector exposing network stats. +func NewNetStatCollector(config Config) (Collector, error) { + c := netStatCollector{ + config: config, + } + return &c, nil +} + +func (c *netStatCollector) Update(ch chan<- prometheus.Metric) (err error) { + netStats, err := getNetStats() + if err != nil { + return fmt.Errorf("couldn't get netstats: %s", err) + } + for protocol, protocolStats := range netStats { + for name, value := range protocolStats { + key := protocol + "_" + name + if _, ok := netStatsMetrics[key]; !ok { + netStatsMetrics[key] = prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: Namespace, + Subsystem: netStatsSubsystem, + Name: key, + Help: fmt.Sprintf("%s %s from /proc/net/netstat.", protocol, name), + }, + ) + } + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf("invalid value %s in netstats: %s", value, err) + } + netStatsMetrics[key].Set(v) + } + } + for _, m := range netStatsMetrics { + m.Collect(ch) + } + return err +} + +func getNetStats() (map[string]map[string]string, error) { + file, err := os.Open(procNetStat) + if err != nil { + return nil, err + } + return parseNetStats(file) +} + +func parseNetStats(r io.ReadCloser) (map[string]map[string]string, error) { + defer r.Close() + netStats := map[string]map[string]string{} + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + nameParts := strings.Split(string(scanner.Text()), " ") + scanner.Scan() + valueParts := strings.Split(string(scanner.Text()), " ") + // Remove trailing :. + protocol := nameParts[0][:len(nameParts[0])-1] + netStats[protocol] = map[string]string{} + if len(nameParts) != len(valueParts) { + return nil, fmt.Errorf("mismatch field count mismatch in %s: %s", + procNetStat, protocol) + } + for i := 1; i < len(nameParts); i++ { + netStats[protocol][nameParts[i]] = valueParts[i] + } + } + return netStats, nil +} diff --git a/node_exporter.go b/node_exporter.go index 5c3a9cd3..86b4100a 100644 --- a/node_exporter.go +++ b/node_exporter.go @@ -26,7 +26,7 @@ var ( configFile = flag.String("config", "node_exporter.conf", "config file.") memProfile = flag.String("memprofile", "", "write memory profile to this file") listeningAddress = flag.String("listen", ":8080", "address to listen on") - enabledCollectors = flag.String("enabledCollectors", "attributes,diskstats,filesystem,loadavg,meminfo,stat,time,netdev", "comma-seperated list of collectors to use") + enabledCollectors = flag.String("enabledCollectors", "attributes,diskstats,filesystem,loadavg,meminfo,stat,time,netdev,netstat", "comma-seperated list of collectors to use") printCollectors = flag.Bool("printCollectors", false, "If true, print available collectors and exit") collectorLabelNames = []string{"collector", "result"}