forked from prometheus/node_exporter
/
netdev.go
130 lines (114 loc) · 3.03 KB
/
netdev.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// +build !nonetdev
package collector
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strconv"
"strings"
"github.com/prometheus/client_golang/prometheus"
)
const (
procNetDev = "/proc/net/dev"
netDevSubsystem = "network"
)
var (
fieldSep = regexp.MustCompile("[ :] *")
)
type netDevCollector struct {
metrics map[string]*prometheus.GaugeVec
}
func init() {
Factories["netdev"] = NewNetDevCollector
}
// Takes a prometheus registry and returns a new Collector exposing
// network device stats.
func NewNetDevCollector() (Collector, error) {
return &netDevCollector{
metrics: map[string]*prometheus.GaugeVec{},
}, nil
}
func (c *netDevCollector) Update(ch chan<- prometheus.Metric) (err error) {
netDev, err := getNetDevStats()
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 _, m := range c.metrics {
m.Collect(ch)
}
return err
}
func getNetDevStats() (map[string]map[string]map[string]string, error) {
file, err := os.Open(procNetDev)
if err != nil {
return nil, err
}
defer file.Close()
return parseNetDevStats(file)
}
func parseNetDevStats(r io.Reader) (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{}
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",
procNetDev, scanner.Text())
}
header := strings.Fields(parts[1])
for scanner.Scan() {
line := strings.TrimLeft(string(scanner.Text()), " ")
parts := fieldSep.Split(line, -1)
if len(parts) != 2*len(header)+1 {
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
}
netDev["transmit"][dev] = transmit
netDev["receive"][dev] = receive
}
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
}