From 380eff24c9fc4f4f470f09e6ee11792b7721d196 Mon Sep 17 00:00:00 2001 From: Michael Allen Date: Wed, 7 Oct 2020 22:25:58 -0700 Subject: [PATCH 1/3] Convert the tcp collector to use perflib instead of WMI Using perflib is substantially faster and more reliable than using WMI to retrieve Windows performance counter data. Signed-off-by: Michael Allen --- collector/tcp.go | 53 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/collector/tcp.go b/collector/tcp.go index 0253907a..c486f84e 100644 --- a/collector/tcp.go +++ b/collector/tcp.go @@ -4,13 +4,13 @@ package collector import ( "errors" - "github.com/StackExchange/wmi" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" ) func init() { - registerCollector("tcp", NewTCPCollector) + registerCollector("tcp", NewTCPCollector, "TCPv4") } // A TCPCollector is a Prometheus collector for WMI Win32_PerfRawData_Tcpip_TCPv4 metrics @@ -91,7 +91,7 @@ func NewTCPCollector() (Collector, error) { // Collect sends the metric values for each metric // to the provided prometheus Metric channel. func (c *TCPCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { - if desc, err := c.collect(ch); err != nil { + if desc, err := c.collect(ctx, ch); err != nil { log.Error("failed collecting tcp metrics:", desc, err) return err } @@ -100,74 +100,73 @@ func (c *TCPCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) // Win32_PerfRawData_Tcpip_TCPv4 docs // - https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx -type Win32_PerfRawData_Tcpip_TCPv4 struct { - ConnectionFailures uint64 - ConnectionsActive uint64 - ConnectionsEstablished uint64 - ConnectionsPassive uint64 - ConnectionsReset uint64 - SegmentsPersec uint64 - SegmentsReceivedPersec uint64 - SegmentsRetransmittedPersec uint64 - SegmentsSentPersec uint64 +type tcp struct { + ConnectionFailures float64 `perflib:"Connection Failures"` + ConnectionsActive float64 `perflib:"Connections Active"` + ConnectionsEstablished float64 `perflib:"Connections Established"` + ConnectionsPassive float64 `perflib:"Connections Passive"` + ConnectionsReset float64 `perflib:"Connections Reset"` + SegmentsPersec float64 `perflib:"Segments/sec"` + SegmentsReceivedPersec float64 `perflib:"Segments Received/sec"` + SegmentsRetransmittedPersec float64 `perflib:"Segments Retransmitted/sec"` + SegmentsSentPersec float64 `perflib:"Segments Sent/sec"` } -func (c *TCPCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { - var dst []Win32_PerfRawData_Tcpip_TCPv4 +func (c *TCPCollector) collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []tcp - q := queryAll(&dst) - if err := wmi.Query(q, &dst); err != nil { + if err := unmarshalObject(ctx.perfObjects["TCPv4"], &dst); err != nil { return nil, err } if len(dst) == 0 { - return nil, errors.New("WMI query returned empty result set") + return nil, errors.New("TCPv4 performance object not available") } // Counters ch <- prometheus.MustNewConstMetric( c.ConnectionFailures, prometheus.CounterValue, - float64(dst[0].ConnectionFailures), + dst[0].ConnectionFailures, ) ch <- prometheus.MustNewConstMetric( c.ConnectionsActive, prometheus.CounterValue, - float64(dst[0].ConnectionsActive), + dst[0].ConnectionsActive, ) ch <- prometheus.MustNewConstMetric( c.ConnectionsEstablished, prometheus.GaugeValue, - float64(dst[0].ConnectionsEstablished), + dst[0].ConnectionsEstablished, ) ch <- prometheus.MustNewConstMetric( c.ConnectionsPassive, prometheus.CounterValue, - float64(dst[0].ConnectionsPassive), + dst[0].ConnectionsPassive, ) ch <- prometheus.MustNewConstMetric( c.ConnectionsReset, prometheus.CounterValue, - float64(dst[0].ConnectionsReset), + dst[0].ConnectionsReset, ) ch <- prometheus.MustNewConstMetric( c.SegmentsTotal, prometheus.CounterValue, - float64(dst[0].SegmentsPersec), + dst[0].SegmentsPersec, ) ch <- prometheus.MustNewConstMetric( c.SegmentsReceivedTotal, prometheus.CounterValue, - float64(dst[0].SegmentsReceivedPersec), + dst[0].SegmentsReceivedPersec, ) ch <- prometheus.MustNewConstMetric( c.SegmentsRetransmittedTotal, prometheus.CounterValue, - float64(dst[0].SegmentsRetransmittedPersec), + dst[0].SegmentsRetransmittedPersec, ) ch <- prometheus.MustNewConstMetric( c.SegmentsSentTotal, prometheus.CounterValue, - float64(dst[0].SegmentsSentPersec), + dst[0].SegmentsSentPersec, ) return nil, nil From 94bda6aa79496e89f7cc213bc8dce5209a482bbf Mon Sep 17 00:00:00 2001 From: Michael Allen Date: Thu, 8 Oct 2020 11:43:39 -0700 Subject: [PATCH 2/3] Expose TCPv6 performance counters in the tcp collector Signed-off-by: Michael Allen --- collector/tcp.go | 84 +++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/collector/tcp.go b/collector/tcp.go index c486f84e..e9d2adce 100644 --- a/collector/tcp.go +++ b/collector/tcp.go @@ -3,17 +3,15 @@ package collector import ( - "errors" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" ) func init() { - registerCollector("tcp", NewTCPCollector, "TCPv4") + registerCollector("tcp", NewTCPCollector, "TCPv4", "TCPv6") } -// A TCPCollector is a Prometheus collector for WMI Win32_PerfRawData_Tcpip_TCPv4 metrics +// A TCPCollector is a Prometheus collector for WMI Win32_PerfRawData_Tcpip_TCPv{4,6} metrics type TCPCollector struct { ConnectionFailures *prometheus.Desc ConnectionsActive *prometheus.Desc @@ -34,55 +32,55 @@ func NewTCPCollector() (Collector, error) { ConnectionFailures: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "connection_failures"), "(TCP.ConnectionFailures)", - nil, + []string{"af"}, nil, ), ConnectionsActive: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "connections_active"), "(TCP.ConnectionsActive)", - nil, + []string{"af"}, nil, ), ConnectionsEstablished: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "connections_established"), "(TCP.ConnectionsEstablished)", - nil, + []string{"af"}, nil, ), ConnectionsPassive: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "connections_passive"), "(TCP.ConnectionsPassive)", - nil, + []string{"af"}, nil, ), ConnectionsReset: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "connections_reset"), "(TCP.ConnectionsReset)", - nil, + []string{"af"}, nil, ), SegmentsTotal: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "segments_total"), "(TCP.SegmentsTotal)", - nil, + []string{"af"}, nil, ), SegmentsReceivedTotal: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "segments_received_total"), "(TCP.SegmentsReceivedTotal)", - nil, + []string{"af"}, nil, ), SegmentsRetransmittedTotal: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "segments_retransmitted_total"), "(TCP.SegmentsRetransmittedTotal)", - nil, + []string{"af"}, nil, ), SegmentsSentTotal: prometheus.NewDesc( prometheus.BuildFQName(Namespace, subsystem, "segments_sent_total"), "(TCP.SegmentsSentTotal)", - nil, + []string{"af"}, nil, ), }, nil @@ -100,6 +98,7 @@ func (c *TCPCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) // Win32_PerfRawData_Tcpip_TCPv4 docs // - https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx +// The TCPv6 performance object uses the same fields. type tcp struct { ConnectionFailures float64 `perflib:"Connection Failures"` ConnectionsActive float64 `perflib:"Connections Active"` @@ -112,62 +111,81 @@ type tcp struct { SegmentsSentPersec float64 `perflib:"Segments Sent/sec"` } -func (c *TCPCollector) collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) { - var dst []tcp - - if err := unmarshalObject(ctx.perfObjects["TCPv4"], &dst); err != nil { - return nil, err - } - if len(dst) == 0 { - return nil, errors.New("TCPv4 performance object not available") - } - - // Counters +func writeTCPCounters(metrics tcp, labels []string, c *TCPCollector, ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric( c.ConnectionFailures, prometheus.CounterValue, - dst[0].ConnectionFailures, + metrics.ConnectionFailures, + labels..., ) ch <- prometheus.MustNewConstMetric( c.ConnectionsActive, prometheus.CounterValue, - dst[0].ConnectionsActive, + metrics.ConnectionsActive, + labels..., ) ch <- prometheus.MustNewConstMetric( c.ConnectionsEstablished, prometheus.GaugeValue, - dst[0].ConnectionsEstablished, + metrics.ConnectionsEstablished, + labels..., ) ch <- prometheus.MustNewConstMetric( c.ConnectionsPassive, prometheus.CounterValue, - dst[0].ConnectionsPassive, + metrics.ConnectionsPassive, + labels..., ) ch <- prometheus.MustNewConstMetric( c.ConnectionsReset, prometheus.CounterValue, - dst[0].ConnectionsReset, + metrics.ConnectionsReset, + labels..., ) ch <- prometheus.MustNewConstMetric( c.SegmentsTotal, prometheus.CounterValue, - dst[0].SegmentsPersec, + metrics.SegmentsPersec, + labels..., ) ch <- prometheus.MustNewConstMetric( c.SegmentsReceivedTotal, prometheus.CounterValue, - dst[0].SegmentsReceivedPersec, + metrics.SegmentsReceivedPersec, + labels..., ) ch <- prometheus.MustNewConstMetric( c.SegmentsRetransmittedTotal, prometheus.CounterValue, - dst[0].SegmentsRetransmittedPersec, + metrics.SegmentsRetransmittedPersec, + labels..., ) ch <- prometheus.MustNewConstMetric( c.SegmentsSentTotal, prometheus.CounterValue, - dst[0].SegmentsSentPersec, + metrics.SegmentsSentPersec, + labels..., ) +} + +func (c *TCPCollector) collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []tcp + + // TCPv4 counters + if err := unmarshalObject(ctx.perfObjects["TCPv4"], &dst); err != nil { + return nil, err + } + if len(dst) != 0 { + writeTCPCounters(dst[0], []string{"ipv4"}, c, ch) + } + + // TCPv6 counters + if err := unmarshalObject(ctx.perfObjects["TCPv6"], &dst); err != nil { + return nil, err + } + if len(dst) != 0 { + writeTCPCounters(dst[0], []string{"ipv6"}, c, ch) + } return nil, nil } From be39c1126ac1bc4cf83d3d8ebf457cc6c5b4d67e Mon Sep 17 00:00:00 2001 From: Michael Allen Date: Fri, 9 Oct 2020 20:18:18 -0700 Subject: [PATCH 3/3] Document changes to the tcp collector Note that TCPv6 metrics are now exported, and there is a new label, `af`. Signed-off-by: Michael Allen --- docs/collector.tcp.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/collector.tcp.md b/docs/collector.tcp.md index 081be618..de15eab8 100644 --- a/docs/collector.tcp.md +++ b/docs/collector.tcp.md @@ -5,7 +5,8 @@ The tcp collector exposes metrics about the TCP/IPv4 network stack. ||| -|- Metric name prefix | `tcp` -Classes | [`Win32_PerfRawData_Tcpip_TCPv4`](https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx) +Data source | Perflib +Classes | [`Win32_PerfRawData_Tcpip_TCPv4`](https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx), Win32_PerfRawData_Tcpip_TCPv6 Enabled by default? | No ## Flags @@ -16,15 +17,15 @@ None Name | Description | Type | Labels -----|-------------|------|------- -`windows_tcp_connection_failures` | Number of times TCP connections have made a direct transition to the CLOSED state from the SYN-SENT state or the SYN-RCVD state, plus the number of times TCP connections have made a direct transition from the SYN-RCVD state to the LISTEN state | counter | None -`windows_tcp_connections_active` | Number of times TCP connections have made a direct transition from the CLOSED state to the SYN-SENT state.| counter | None -`windows_tcp_connections_established` | Number of TCP connections for which the current state is either ESTABLISHED or CLOSE-WAIT. | counter | None -`windows_tcp_connections_passive` | Number of times TCP connections have made a direct transition from the LISTEN state to the SYN-RCVD state. | counter | None -`windows_tcp_connections_reset` | Number of times TCP connections have made a direct transition from the LISTEN state to the SYN-RCVD state. | counter | None -`windows_tcp_segments_total` | Total segments sent or received using the TCP protocol | counter | None -`windows_tcp_segments_received_total` | Total segments received, including those received in error. This count includes segments received on currently established connections | counter | None -`windows_tcp_segments_retransmitted_total` | Total segments retransmitted. That is, segments transmitted that contain one or more previously transmitted bytes | counter | None -`windows_tcp_segments_sent_total` | Total segments sent, including those on current connections, but excluding those containing *only* retransmitted bytes | counter | None +`windows_tcp_connection_failures` | Number of times TCP connections have made a direct transition to the CLOSED state from the SYN-SENT state or the SYN-RCVD state, plus the number of times TCP connections have made a direct transition from the SYN-RCVD state to the LISTEN state | counter | af +`windows_tcp_connections_active` | Number of times TCP connections have made a direct transition from the CLOSED state to the SYN-SENT state.| counter | af +`windows_tcp_connections_established` | Number of TCP connections for which the current state is either ESTABLISHED or CLOSE-WAIT. | counter | af +`windows_tcp_connections_passive` | Number of times TCP connections have made a direct transition from the LISTEN state to the SYN-RCVD state. | counter | af +`windows_tcp_connections_reset` | Number of times TCP connections have made a direct transition from the LISTEN state to the SYN-RCVD state. | counter | af +`windows_tcp_segments_total` | Total segments sent or received using the TCP protocol | counter | af +`windows_tcp_segments_received_total` | Total segments received, including those received in error. This count includes segments received on currently established connections | counter | af +`windows_tcp_segments_retransmitted_total` | Total segments retransmitted. That is, segments transmitted that contain one or more previously transmitted bytes | counter | af +`windows_tcp_segments_sent_total` | Total segments sent, including those on current connections, but excluding those containing *only* retransmitted bytes | counter | af ### Example metric _This collector does not yet have explained examples, we would appreciate your help adding them!_