aboutsummaryrefslogtreecommitdiff
path: root/collector/sockstat_linux.go
diff options
context:
space:
mode:
authorMatt Layher <mdlayher@gmail.com>2019-11-25 14:41:38 -0500
committerBen Kochie <superq@gmail.com>2019-11-25 13:41:38 -0600
commitda6b66371f70822bc75303c79c85b80c46bcfae6 (patch)
tree09dc8c777daa4d93dbbff5597941ba5cc69a3138 /collector/sockstat_linux.go
parent3c2c4e7b3c473777b13e79cc84cc04e519fd3a9a (diff)
downloadprometheus_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.go184
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 @@
16package collector 16package collector
17 17
18import ( 18import (
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
29const ( 27const (
@@ -45,78 +43,138 @@ func NewSockStatCollector() (Collector, error) {
45} 43}
46 44
47func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { 45func (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
71func 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
81func parseSockStats(r io.Reader, fileName string) (map[string]map[string]string, error) { 90func (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}