mirror of
				https://github.com/prometheus/node_exporter.git
				synced 2025-08-20 18:33:52 -07:00 
			
		
		
		
	Add collector for /proc/net/tcp
This commit is contained in:
		
							parent
							
								
									aaf01e52e2
								
							
						
					
					
						commit
						e4da771b21
					
				|  | @ -46,6 +46,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