diff options
author | Matt Layher <mdlayher@gmail.com> | 2019-11-25 14:41:38 -0500 |
---|---|---|
committer | Ben Kochie <superq@gmail.com> | 2019-11-25 13:41:38 -0600 |
commit | da6b66371f70822bc75303c79c85b80c46bcfae6 (patch) | |
tree | 09dc8c777daa4d93dbbff5597941ba5cc69a3138 /collector/sockstat_linux.go | |
parent | 3c2c4e7b3c473777b13e79cc84cc04e519fd3a9a (diff) | |
download | prometheus_node_collector-da6b66371f70822bc75303c79c85b80c46bcfae6.tar.bz2 prometheus_node_collector-da6b66371f70822bc75303c79c85b80c46bcfae6.tar.xz prometheus_node_collector-da6b66371f70822bc75303c79c85b80c46bcfae6.zip |
collector: reimplement sockstat collector with procfs (#1552)
* collector: reimplement sockstat collector with procfs
* collector: handle sockstat IPv4 disabled, debug logging
Signed-off-by: Matt Layher <mdlayher@gmail.com>
Diffstat (limited to 'collector/sockstat_linux.go')
-rw-r--r-- | collector/sockstat_linux.go | 184 |
1 files changed, 121 insertions, 63 deletions
diff --git a/collector/sockstat_linux.go b/collector/sockstat_linux.go index 92847be..d6c4c72 100644 --- a/collector/sockstat_linux.go +++ b/collector/sockstat_linux.go | |||
@@ -16,14 +16,12 @@ | |||
16 | package collector | 16 | package collector |
17 | 17 | ||
18 | import ( | 18 | import ( |
19 | "bufio" | ||
20 | "fmt" | 19 | "fmt" |
21 | "io" | ||
22 | "os" | 20 | "os" |
23 | "strconv" | ||
24 | "strings" | ||
25 | 21 | ||
26 | "github.com/prometheus/client_golang/prometheus" | 22 | "github.com/prometheus/client_golang/prometheus" |
23 | "github.com/prometheus/common/log" | ||
24 | "github.com/prometheus/procfs" | ||
27 | ) | 25 | ) |
28 | 26 | ||
29 | const ( | 27 | const ( |
@@ -45,78 +43,138 @@ func NewSockStatCollector() (Collector, error) { | |||
45 | } | 43 | } |
46 | 44 | ||
47 | func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { | 45 | func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { |
48 | sockStats, err := getSockStats(procFilePath("net/sockstat")) | 46 | fs, err := procfs.NewFS(*procPath) |
49 | if err != nil { | 47 | if err != nil { |
50 | return fmt.Errorf("couldn't get sockstats: %s", err) | 48 | return fmt.Errorf("failed to open procfs: %v", err) |
51 | } | 49 | } |
52 | for protocol, protocolStats := range sockStats { | 50 | |
53 | for name, value := range protocolStats { | 51 | // If IPv4 and/or IPv6 are disabled on this kernel, handle it gracefully. |
54 | v, err := strconv.ParseFloat(value, 64) | 52 | stat4, err := fs.NetSockstat() |
55 | if err != nil { | 53 | switch { |
56 | return fmt.Errorf("invalid value %s in sockstats: %s", value, err) | 54 | case err == nil: |
57 | } | 55 | case os.IsNotExist(err): |
58 | ch <- prometheus.MustNewConstMetric( | 56 | log.Debug("IPv4 sockstat statistics not found, skipping") |
59 | prometheus.NewDesc( | 57 | default: |
60 | prometheus.BuildFQName(namespace, sockStatSubsystem, protocol+"_"+name), | 58 | return fmt.Errorf("failed to get IPv4 sockstat data: %v", err) |
61 | fmt.Sprintf("Number of %s sockets in state %s.", protocol, name), | ||
62 | nil, nil, | ||
63 | ), | ||
64 | prometheus.GaugeValue, v, | ||
65 | ) | ||
66 | } | ||
67 | } | 59 | } |
68 | return err | ||
69 | } | ||
70 | 60 | ||
71 | func getSockStats(fileName string) (map[string]map[string]string, error) { | 61 | stat6, err := fs.NetSockstat6() |
72 | file, err := os.Open(fileName) | 62 | switch { |
73 | if err != nil { | 63 | case err == nil: |
74 | return nil, err | 64 | case os.IsNotExist(err): |
65 | log.Debug("IPv6 sockstat statistics not found, skipping") | ||
66 | default: | ||
67 | return fmt.Errorf("failed to get IPv6 sockstat data: %v", err) | ||
68 | } | ||
69 | |||
70 | stats := []struct { | ||
71 | isIPv6 bool | ||
72 | stat *procfs.NetSockstat | ||
73 | }{ | ||
74 | { | ||
75 | stat: stat4, | ||
76 | }, | ||
77 | { | ||
78 | isIPv6: true, | ||
79 | stat: stat6, | ||
80 | }, | ||
75 | } | 81 | } |
76 | defer file.Close() | ||
77 | 82 | ||
78 | return parseSockStats(file, fileName) | 83 | for _, s := range stats { |
84 | c.update(ch, s.isIPv6, s.stat) | ||
85 | } | ||
86 | |||
87 | return nil | ||
79 | } | 88 | } |
80 | 89 | ||
81 | func parseSockStats(r io.Reader, fileName string) (map[string]map[string]string, error) { | 90 | func (c *sockStatCollector) update(ch chan<- prometheus.Metric, isIPv6 bool, s *procfs.NetSockstat) { |
82 | var ( | 91 | if s == nil { |
83 | sockStat = map[string]map[string]string{} | 92 | // IPv6 disabled or similar; nothing to do. |
84 | scanner = bufio.NewScanner(r) | 93 | return |
85 | ) | ||
86 | |||
87 | for scanner.Scan() { | ||
88 | line := strings.Split(scanner.Text(), " ") | ||
89 | // Remove trailing ':'. | ||
90 | protocol := line[0][:len(line[0])-1] | ||
91 | sockStat[protocol] = map[string]string{} | ||
92 | |||
93 | for i := 1; i < len(line) && i+1 < len(line); i++ { | ||
94 | sockStat[protocol][line[i]] = line[i+1] | ||
95 | i++ | ||
96 | } | ||
97 | } | 94 | } |
98 | if err := scanner.Err(); err != nil { | 95 | |
99 | return nil, err | 96 | // If sockstat contains the number of used sockets, export it. |
97 | if !isIPv6 && s.Used != nil { | ||
98 | // TODO: this must be updated if sockstat6 ever exports this data. | ||
99 | ch <- prometheus.MustNewConstMetric( | ||
100 | prometheus.NewDesc( | ||
101 | prometheus.BuildFQName(namespace, sockStatSubsystem, "sockets_used"), | ||
102 | "Number of IPv4 sockets in use.", | ||
103 | nil, | ||
104 | nil, | ||
105 | ), | ||
106 | prometheus.GaugeValue, | ||
107 | float64(*s.Used), | ||
108 | ) | ||
100 | } | 109 | } |
101 | 110 | ||
102 | // The mem metrics is the count of pages used. Multiply the mem metrics by | 111 | // A name and optional value for a sockstat metric. |
103 | // the page size from the kernel to get the number of bytes used. | 112 | type ssPair struct { |
104 | // | 113 | name string |
105 | // Update the TCP mem from page count to bytes. | 114 | v *int |
106 | pageCount, err := strconv.Atoi(sockStat["TCP"]["mem"]) | ||
107 | if err != nil { | ||
108 | return nil, fmt.Errorf("invalid value %s in sockstats: %s", sockStat["TCP"]["mem"], err) | ||
109 | } | 115 | } |
110 | sockStat["TCP"]["mem_bytes"] = strconv.Itoa(pageCount * pageSize) | ||
111 | 116 | ||
112 | // Update the UDP mem from page count to bytes. | 117 | // Previously these metric names were generated directly from the file output. |
113 | if udpMem := sockStat["UDP"]["mem"]; udpMem != "" { | 118 | // In order to keep the same level of compatibility, we must map the fields |
114 | pageCount, err = strconv.Atoi(udpMem) | 119 | // to their correct names. |
115 | if err != nil { | 120 | for _, p := range s.Protocols { |
116 | return nil, fmt.Errorf("invalid value %s in sockstats: %s", sockStat["UDP"]["mem"], err) | 121 | pairs := []ssPair{ |
122 | { | ||
123 | name: "inuse", | ||
124 | v: &p.InUse, | ||
125 | }, | ||
126 | { | ||
127 | name: "orphan", | ||
128 | v: p.Orphan, | ||
129 | }, | ||
130 | { | ||
131 | name: "tw", | ||
132 | v: p.TW, | ||
133 | }, | ||
134 | { | ||
135 | name: "alloc", | ||
136 | v: p.Alloc, | ||
137 | }, | ||
138 | { | ||
139 | name: "mem", | ||
140 | v: p.Mem, | ||
141 | }, | ||
142 | { | ||
143 | name: "memory", | ||
144 | v: p.Memory, | ||
145 | }, | ||
117 | } | 146 | } |
118 | sockStat["UDP"]["mem_bytes"] = strconv.Itoa(pageCount * pageSize) | ||
119 | } | ||
120 | 147 | ||
121 | return sockStat, nil | 148 | // Also export mem_bytes values for sockets which have a mem value |
149 | // stored in pages. | ||
150 | if p.Mem != nil { | ||
151 | v := *p.Mem * pageSize | ||
152 | pairs = append(pairs, ssPair{ | ||
153 | name: "mem_bytes", | ||
154 | v: &v, | ||
155 | }) | ||
156 | } | ||
157 | |||
158 | for _, pair := range pairs { | ||
159 | if pair.v == nil { | ||
160 | // This value is not set for this protocol; nothing to do. | ||
161 | continue | ||
162 | } | ||
163 | |||
164 | ch <- prometheus.MustNewConstMetric( | ||
165 | prometheus.NewDesc( | ||
166 | prometheus.BuildFQName( | ||
167 | namespace, | ||
168 | sockStatSubsystem, | ||
169 | fmt.Sprintf("%s_%s", p.Protocol, pair.name), | ||
170 | ), | ||
171 | fmt.Sprintf("Number of %s sockets in state %s.", p.Protocol, pair.name), | ||
172 | nil, | ||
173 | nil, | ||
174 | ), | ||
175 | prometheus.GaugeValue, | ||
176 | float64(*pair.v), | ||
177 | ) | ||
178 | } | ||
179 | } | ||
122 | } | 180 | } |