mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-26 22:19:44 -08:00
Merge pull request #56 from kjmkznr/add-tcp-stat-collector
Add TCP state collector
This commit is contained in:
commit
3ab0b1f915
|
@ -48,6 +48,7 @@ lastlogin | Exposes the last time there was a login.
|
|||
megacli | Exposes RAID statistics from MegaCLI.
|
||||
ntp | Exposes time drift from an NTP server.
|
||||
runit | Exposes service status from [runit](http://smarden.org/runit/).
|
||||
tcpstat | Exposes TCP connection status information from /proc/net/tcp and /proc/net/tcp6. (Warning: the current version has potential performance issues in high load situations.)
|
||||
|
||||
## Textfile Collector
|
||||
|
||||
|
|
3
collector/fixtures/tcpstat
Normal file
3
collector/fixtures/tcpstat
Normal file
|
@ -0,0 +1,3 @@
|
|||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 2740 1 ffff88003d3af3c0 100 0 0 10 0
|
||||
1: 0F02000A:0016 0202000A:8B6B 01 00000000:00000000 02:000AC99B 00000000 0 0 3652 4 ffff88003d3ae040 21 4 31 47 46
|
150
collector/tcpstat.go
Normal file
150
collector/tcpstat.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
// +build !notcpstat
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
procTCPStat = "/proc/net/tcp"
|
||||
procTCP6Stat = "/proc/net/tcp6"
|
||||
)
|
||||
|
||||
type TCPConnectionState int
|
||||
|
||||
const (
|
||||
TCP_ESTABLISHED TCPConnectionState = iota + 1
|
||||
TCP_SYN_SENT
|
||||
TCP_SYN_RECV
|
||||
TCP_FIN_WAIT1
|
||||
TCP_FIN_WAIT2
|
||||
TCP_TIME_WAIT
|
||||
TCP_CLOSE
|
||||
TCP_CLOSE_WAIT
|
||||
TCP_LAST_ACK
|
||||
TCP_LISTEN
|
||||
TCP_CLOSING
|
||||
)
|
||||
|
||||
type tcpStatCollector struct {
|
||||
config Config
|
||||
metric *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
func init() {
|
||||
Factories["tcpstat"] = NewTCPStatCollector
|
||||
}
|
||||
|
||||
// NewTCPStatCollector takes a config struct and returns
|
||||
// a new Collector exposing network stats.
|
||||
func NewTCPStatCollector(config Config) (Collector, error) {
|
||||
return &tcpStatCollector{
|
||||
config: config,
|
||||
metric: prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: Namespace,
|
||||
Name: "tcp_connection_states",
|
||||
Help: "Number of connection states.",
|
||||
},
|
||||
[]string{"state"},
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *tcpStatCollector) Update(ch chan<- prometheus.Metric) (err error) {
|
||||
tcpStats, err := getTCPStats(procTCPStat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get tcpstats: %s", err)
|
||||
}
|
||||
|
||||
// if enabled ipv6 system
|
||||
if _, hasIPv6 := os.Stat(procTCP6Stat); hasIPv6 == nil {
|
||||
tcp6Stats, err := getTCPStats(procTCP6Stat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get tcp6stats: %s", err)
|
||||
}
|
||||
|
||||
for st, value := range tcp6Stats {
|
||||
tcpStats[st] += value
|
||||
}
|
||||
}
|
||||
|
||||
for st, value := range tcpStats {
|
||||
c.metric.WithLabelValues(st.String()).Set(value)
|
||||
}
|
||||
|
||||
c.metric.Collect(ch)
|
||||
return err
|
||||
}
|
||||
|
||||
func getTCPStats(statsFile string) (map[TCPConnectionState]float64, error) {
|
||||
file, err := os.Open(statsFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return parseTCPStats(file)
|
||||
}
|
||||
|
||||
func parseTCPStats(r io.Reader) (map[TCPConnectionState]float64, error) {
|
||||
var (
|
||||
tcpStats = map[TCPConnectionState]float64{}
|
||||
scanner = bufio.NewScanner(r)
|
||||
)
|
||||
|
||||
for scanner.Scan() {
|
||||
parts := strings.Fields(scanner.Text())
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(parts[0], "sl") {
|
||||
continue
|
||||
}
|
||||
st, err := strconv.ParseInt(parts[3], 16, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tcpStats[TCPConnectionState(st)]++
|
||||
}
|
||||
|
||||
return tcpStats, nil
|
||||
}
|
||||
|
||||
func (st TCPConnectionState) String() string {
|
||||
switch st {
|
||||
case TCP_ESTABLISHED:
|
||||
return "established"
|
||||
case TCP_SYN_SENT:
|
||||
return "syn_sent"
|
||||
case TCP_SYN_RECV:
|
||||
return "syn_recv"
|
||||
case TCP_FIN_WAIT1:
|
||||
return "fin_wait1"
|
||||
case TCP_FIN_WAIT2:
|
||||
return "fin_wait2"
|
||||
case TCP_TIME_WAIT:
|
||||
return "time_wait"
|
||||
case TCP_CLOSE:
|
||||
return "close"
|
||||
case TCP_CLOSE_WAIT:
|
||||
return "close_wait"
|
||||
case TCP_LAST_ACK:
|
||||
return "last_ack"
|
||||
case TCP_LISTEN:
|
||||
return "listen"
|
||||
case TCP_CLOSING:
|
||||
return "closing"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
27
collector/tcpstat_test.go
Normal file
27
collector/tcpstat_test.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package collector
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTCPStat(t *testing.T) {
|
||||
file, err := os.Open("fixtures/tcpstat")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
tcpStats, err := parseTCPStats(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want, got := 1, int(tcpStats[TCP_ESTABLISHED]); want != got {
|
||||
t.Errorf("want tcpstat number of established state %d, got %d", want, got)
|
||||
}
|
||||
|
||||
if want, got := 1, int(tcpStats[TCP_LISTEN]); want != got {
|
||||
t.Errorf("want tcpstat number of listen state %d, got %d", want, got)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue