Merge pull request #111 from chosenken/sockstat
Implementation of /proc/net/sockstat collector
This commit is contained in:
commit
dd5fc6a3ca
|
@ -0,0 +1,6 @@
|
|||
sockets: used 229
|
||||
TCP: inuse 4 orphan 0 tw 4 alloc 17 mem 1
|
||||
UDP: inuse 0 mem 0
|
||||
UDPLITE: inuse 0
|
||||
RAW: inuse 0
|
||||
FRAG: inuse 0 memory 0
|
|
@ -0,0 +1,118 @@
|
|||
// +build !nosockstat
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
procSockStat = "/proc/net/sockstat"
|
||||
sockStatSubsystem = "sockstat"
|
||||
)
|
||||
|
||||
// Used for calculating the total memory bytes on TCP and UDP.
|
||||
var pageSize = os.Getpagesize()
|
||||
|
||||
type sockStatCollector struct {
|
||||
metrics map[string]prometheus.Gauge
|
||||
}
|
||||
|
||||
func init() {
|
||||
Factories[sockStatSubsystem] = NewSockStatCollector
|
||||
}
|
||||
|
||||
// NewSockStatCollector returns a new Collector exposing socket stats.
|
||||
func NewSockStatCollector() (Collector, error) {
|
||||
return &sockStatCollector{
|
||||
metrics: map[string]prometheus.Gauge{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
|
||||
sockStats, err := getSockStats(procSockStat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get sockstats: %s", err)
|
||||
}
|
||||
for protocol, protocolStats := range sockStats {
|
||||
for name, value := range protocolStats {
|
||||
key := protocol + "_" + name
|
||||
if _, ok := c.metrics[key]; !ok {
|
||||
c.metrics[key] = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Subsystem: sockStatSubsystem,
|
||||
Name: key,
|
||||
Help: fmt.Sprintf("%s %s from /proc/net/sockstat.", protocol, name),
|
||||
},
|
||||
)
|
||||
}
|
||||
v, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid value %s in sockstats: %s", value, err)
|
||||
}
|
||||
c.metrics[key].Set(v)
|
||||
}
|
||||
}
|
||||
for _, m := range c.metrics {
|
||||
m.Collect(ch)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getSockStats(fileName string) (map[string]map[string]string, error) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return parseSockStats(file, fileName)
|
||||
}
|
||||
|
||||
func parseSockStats(r io.Reader, fileName string) (map[string]map[string]string, error) {
|
||||
var (
|
||||
sockStat = map[string]map[string]string{}
|
||||
scanner = bufio.NewScanner(r)
|
||||
)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.Split(string(scanner.Text()), " ")
|
||||
// Remove trailing :.
|
||||
protocol := line[0][:len(line[0])-1]
|
||||
sockStat[protocol] = map[string]string{}
|
||||
|
||||
for i := 1; i < len(line) && i+1 < len(line); i++ {
|
||||
sockStat[protocol][line[i]] = line[i+1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The mem metrics is the count of pages used.
|
||||
Multiply the mem metrics by the page size from the kernal to get the number
|
||||
of bytes used.
|
||||
|
||||
Update the TCP mem from page count to bytes.
|
||||
*/
|
||||
pageCount, err := strconv.Atoi(sockStat["TCP"]["mem"])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value %s in sockstats: %s", sockStat["TCP"]["mem"], err)
|
||||
}
|
||||
sockStat["TCP"]["mem_bytes"] = strconv.Itoa(pageCount * pageSize)
|
||||
|
||||
// Update the UDP mem from page count to bytes.
|
||||
pageCount, err = strconv.Atoi(sockStat["UDP"]["mem"])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value %s in sockstats: %s", sockStat["UDP"]["mem"], err)
|
||||
}
|
||||
sockStat["UDP"]["mem_bytes"] = strconv.Itoa(pageCount * pageSize)
|
||||
|
||||
return sockStat, nil
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package collector
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSockStats(t *testing.T) {
|
||||
file, err := os.Open("fixtures/sockstat")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
sockStats, err := parseSockStats(file, fileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want, got := "229", sockStats["sockets"]["used"]; want != got {
|
||||
t.Errorf("want sockstat sockets used %s, got %s", want, got)
|
||||
}
|
||||
|
||||
if want, got := "4", sockStats["TCP"]["tw"]; want != got {
|
||||
t.Errorf("want sockstat sockets used %s, got %s", want, got)
|
||||
}
|
||||
|
||||
if want, got := "17", sockStats["TCP"]["alloc"]; want != got {
|
||||
t.Errorf("want sockstat sockets used %s, got %s", want, got)
|
||||
}
|
||||
|
||||
// The test file has 1 for TCP mem, which is one page. So we should get the
|
||||
// page size in bytes back from sockstat_linux. We get the page size from
|
||||
// os here because this value can change from system to system. The value is
|
||||
// 4096 by default from linux 2.4 onward.
|
||||
if want, got := strconv.Itoa(os.Getpagesize()), sockStats["TCP"]["mem_bytes"]; want != got {
|
||||
t.Errorf("want sockstat sockets used %s, got %s", want, got)
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ var (
|
|||
memProfile = flag.String("debug.memprofile-file", "", "Write memory profile to this file upon receipt of SIGUSR1.")
|
||||
listenAddress = flag.String("web.listen-address", ":9100", "Address on which to expose metrics and web interface.")
|
||||
metricsPath = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
|
||||
enabledCollectors = flag.String("collectors.enabled", "diskstats,filesystem,loadavg,meminfo,stat,textfile,time,netdev,netstat", "Comma-separated list of collectors to use.")
|
||||
enabledCollectors = flag.String("collectors.enabled", "diskstats,filesystem,loadavg,meminfo,stat,textfile,time,netdev,netstat,sockstat", "Comma-separated list of collectors to use.")
|
||||
printCollectors = flag.Bool("collectors.print", false, "If true, print available collectors and exit.")
|
||||
authUser = flag.String("auth.user", "", "Username for basic auth.")
|
||||
authPass = flag.String("auth.pass", "", "Password for basic auth.")
|
||||
|
|
Loading…
Reference in New Issue