2015-11-02 20:31:34 -08:00
// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2021-09-08 05:50:25 -07:00
//go:build !nonetdev && (linux || freebsd || openbsd || dragonfly || darwin)
2015-11-02 20:31:34 -08:00
// +build !nonetdev
2016-12-29 09:23:34 -08:00
// +build linux freebsd openbsd dragonfly darwin
2015-11-02 20:31:34 -08:00
package collector
import (
2019-05-31 08:55:50 -07:00
"errors"
2015-11-02 20:31:34 -08:00
"fmt"
2024-09-11 01:51:28 -07:00
"log/slog"
2021-09-08 05:50:25 -07:00
"net"
"strconv"
2022-01-17 04:28:13 -08:00
"sync"
2015-11-02 20:31:34 -08:00
2023-03-07 00:25:05 -08:00
"github.com/alecthomas/kingpin/v2"
2015-11-02 20:31:34 -08:00
"github.com/prometheus/client_golang/prometheus"
)
var (
2020-06-11 23:24:29 -07:00
netdevDeviceInclude = kingpin . Flag ( "collector.netdev.device-include" , "Regexp of net devices to include (mutually exclusive to device-exclude)." ) . String ( )
oldNetdevDeviceInclude = kingpin . Flag ( "collector.netdev.device-whitelist" , "DEPRECATED: Use collector.netdev.device-include" ) . Hidden ( ) . String ( )
netdevDeviceExclude = kingpin . Flag ( "collector.netdev.device-exclude" , "Regexp of net devices to exclude (mutually exclusive to device-include)." ) . String ( )
oldNetdevDeviceExclude = kingpin . Flag ( "collector.netdev.device-blacklist" , "DEPRECATED: Use collector.netdev.device-exclude" ) . Hidden ( ) . String ( )
2021-09-08 05:50:25 -07:00
netdevAddressInfo = kingpin . Flag ( "collector.netdev.address-info" , "Collect address-info for every device" ) . Bool ( )
2021-07-09 02:44:03 -07:00
netdevDetailedMetrics = kingpin . Flag ( "collector.netdev.enable-detailed-metrics" , "Use (incompatible) metric names that provide more detailed stats on Linux" ) . Bool ( )
2015-11-02 20:31:34 -08:00
)
type netDevCollector struct {
2022-01-17 04:28:13 -08:00
subsystem string
2022-05-19 01:31:48 -07:00
deviceFilter deviceFilter
2022-01-17 04:28:13 -08:00
metricDescsMutex sync . Mutex
metricDescs map [ string ] * prometheus . Desc
2024-09-11 01:51:28 -07:00
logger * slog . Logger
2015-11-02 20:31:34 -08:00
}
2020-08-24 08:43:27 -07:00
type netDevStats map [ string ] map [ string ] uint64
2015-11-02 20:31:34 -08:00
func init ( ) {
2017-09-28 06:06:26 -07:00
registerCollector ( "netdev" , defaultEnabled , NewNetDevCollector )
2015-11-02 20:31:34 -08:00
}
// NewNetDevCollector returns a new Collector exposing network device stats.
2024-09-11 01:51:28 -07:00
func NewNetDevCollector ( logger * slog . Logger ) ( Collector , error ) {
2020-06-11 23:24:29 -07:00
if * oldNetdevDeviceInclude != "" {
if * netdevDeviceInclude == "" {
2024-09-11 01:51:28 -07:00
logger . Warn ( "--collector.netdev.device-whitelist is DEPRECATED and will be removed in 2.0.0, use --collector.netdev.device-include" )
2020-06-11 23:24:29 -07:00
* netdevDeviceInclude = * oldNetdevDeviceInclude
} else {
return nil , errors . New ( "--collector.netdev.device-whitelist and --collector.netdev.device-include are mutually exclusive" )
}
}
if * oldNetdevDeviceExclude != "" {
if * netdevDeviceExclude == "" {
2024-09-11 01:51:28 -07:00
logger . Warn ( "--collector.netdev.device-blacklist is DEPRECATED and will be removed in 2.0.0, use --collector.netdev.device-exclude" )
2020-06-11 23:24:29 -07:00
* netdevDeviceExclude = * oldNetdevDeviceExclude
} else {
return nil , errors . New ( "--collector.netdev.device-blacklist and --collector.netdev.device-exclude are mutually exclusive" )
}
}
if * netdevDeviceExclude != "" && * netdevDeviceInclude != "" {
return nil , errors . New ( "device-exclude & device-include are mutually exclusive" )
2019-05-31 08:55:50 -07:00
}
2020-06-11 23:24:29 -07:00
if * netdevDeviceExclude != "" {
2024-09-11 01:51:28 -07:00
logger . Info ( "Parsed flag --collector.netdev.device-exclude" , "flag" , * netdevDeviceExclude )
2019-05-31 08:55:50 -07:00
}
2020-06-11 23:24:29 -07:00
if * netdevDeviceInclude != "" {
2024-09-11 01:51:28 -07:00
logger . Info ( "Parsed Flag --collector.netdev.device-include" , "flag" , * netdevDeviceInclude )
2019-05-31 08:55:50 -07:00
}
2015-11-02 20:31:34 -08:00
return & netDevCollector {
2019-12-02 06:16:00 -08:00
subsystem : "network" ,
2022-05-19 01:31:48 -07:00
deviceFilter : newDeviceFilter ( * netdevDeviceExclude , * netdevDeviceInclude ) ,
2019-12-02 06:16:00 -08:00
metricDescs : map [ string ] * prometheus . Desc { } ,
logger : logger ,
2015-11-02 20:31:34 -08:00
} , nil
}
2024-09-11 00:26:34 -07:00
func ( c * netDevCollector ) metricDesc ( key string , labels [ ] string ) * prometheus . Desc {
2022-01-17 04:28:13 -08:00
c . metricDescsMutex . Lock ( )
defer c . metricDescsMutex . Unlock ( )
if _ , ok := c . metricDescs [ key ] ; ! ok {
c . metricDescs [ key ] = prometheus . NewDesc (
prometheus . BuildFQName ( namespace , c . subsystem , key + "_total" ) ,
fmt . Sprintf ( "Network device statistic %s." , key ) ,
2024-09-11 00:26:34 -07:00
labels ,
2022-01-17 04:28:13 -08:00
nil ,
)
}
return c . metricDescs [ key ]
}
2017-02-28 10:47:20 -08:00
func ( c * netDevCollector ) Update ( ch chan <- prometheus . Metric ) error {
2019-12-02 06:16:00 -08:00
netDev , err := getNetDevStats ( & c . deviceFilter , c . logger )
2015-11-02 20:31:34 -08:00
if err != nil {
2020-06-15 13:27:14 -07:00
return fmt . Errorf ( "couldn't get netstats: %w" , err )
2015-11-02 20:31:34 -08:00
}
2024-09-11 00:26:34 -07:00
netDevLabels , err := getNetDevLabels ( )
if err != nil {
return fmt . Errorf ( "couldn't get netdev labels: %w" , err )
}
2015-11-02 20:31:34 -08:00
for dev , devStats := range netDev {
2021-07-09 02:44:03 -07:00
if ! * netdevDetailedMetrics {
legacy ( devStats )
}
2024-09-11 00:26:34 -07:00
labels := [ ] string { "device" }
labelValues := [ ] string { dev }
if devLabels , exists := netDevLabels [ dev ] ; exists {
for labelName , labelValue := range devLabels {
labels = append ( labels , labelName )
labelValues = append ( labelValues , labelValue )
}
}
2015-11-02 20:31:34 -08:00
for key , value := range devStats {
2024-09-11 00:26:34 -07:00
desc := c . metricDesc ( key , labels )
ch <- prometheus . MustNewConstMetric ( desc , prometheus . CounterValue , float64 ( value ) , labelValues ... )
2015-11-02 20:31:34 -08:00
}
}
2021-09-08 05:50:25 -07:00
if * netdevAddressInfo {
interfaces , err := net . Interfaces ( )
if err != nil {
return fmt . Errorf ( "could not get network interfaces: %w" , err )
}
desc := prometheus . NewDesc ( prometheus . BuildFQName ( namespace , "network_address" ,
"info" ) , "node network address by device" ,
[ ] string { "device" , "address" , "netmask" , "scope" } , nil )
for _ , addr := range getAddrsInfo ( interfaces ) {
ch <- prometheus . MustNewConstMetric ( desc , prometheus . GaugeValue , 1 ,
addr . device , addr . addr , addr . netmask , addr . scope )
}
}
2015-11-02 20:31:34 -08:00
return nil
}
2021-09-08 05:50:25 -07:00
type addrInfo struct {
device string
addr string
scope string
netmask string
}
func scope ( ip net . IP ) string {
if ip . IsLoopback ( ) || ip . IsLinkLocalUnicast ( ) || ip . IsLinkLocalMulticast ( ) {
return "link-local"
}
if ip . IsInterfaceLocalMulticast ( ) {
return "interface-local"
}
if ip . IsGlobalUnicast ( ) {
return "global"
}
return ""
}
// getAddrsInfo returns interface name, address, scope and netmask for all interfaces.
func getAddrsInfo ( interfaces [ ] net . Interface ) [ ] addrInfo {
var res [ ] addrInfo
for _ , ifs := range interfaces {
addrs , _ := ifs . Addrs ( )
for _ , addr := range addrs {
ip , ipNet , err := net . ParseCIDR ( addr . String ( ) )
if err != nil {
continue
}
size , _ := ipNet . Mask . Size ( )
res = append ( res , addrInfo {
device : ifs . Name ,
addr : ip . String ( ) ,
scope : scope ( ip ) ,
netmask : strconv . Itoa ( size ) ,
} )
}
}
return res
}
2021-07-09 02:44:03 -07:00
// https://github.com/torvalds/linux/blob/master/net/core/net-procfs.c#L75-L97
func legacy ( metrics map [ string ] uint64 ) {
if metric , ok := pop ( metrics , "receive_errors" ) ; ok {
metrics [ "receive_errs" ] = metric
}
if metric , ok := pop ( metrics , "receive_dropped" ) ; ok {
metrics [ "receive_drop" ] = metric + popz ( metrics , "receive_missed_errors" )
}
if metric , ok := pop ( metrics , "receive_fifo_errors" ) ; ok {
metrics [ "receive_fifo" ] = metric
}
if metric , ok := pop ( metrics , "receive_frame_errors" ) ; ok {
metrics [ "receive_frame" ] = metric + popz ( metrics , "receive_length_errors" ) + popz ( metrics , "receive_over_errors" ) + popz ( metrics , "receive_crc_errors" )
}
if metric , ok := pop ( metrics , "multicast" ) ; ok {
metrics [ "receive_multicast" ] = metric
}
if metric , ok := pop ( metrics , "transmit_errors" ) ; ok {
metrics [ "transmit_errs" ] = metric
}
if metric , ok := pop ( metrics , "transmit_dropped" ) ; ok {
metrics [ "transmit_drop" ] = metric
}
if metric , ok := pop ( metrics , "transmit_fifo_errors" ) ; ok {
metrics [ "transmit_fifo" ] = metric
}
if metric , ok := pop ( metrics , "multicast" ) ; ok {
metrics [ "receive_multicast" ] = metric
}
if metric , ok := pop ( metrics , "collisions" ) ; ok {
metrics [ "transmit_colls" ] = metric
}
if metric , ok := pop ( metrics , "transmit_carrier_errors" ) ; ok {
metrics [ "transmit_carrier" ] = metric + popz ( metrics , "transmit_aborted_errors" ) + popz ( metrics , "transmit_heartbeat_errors" ) + popz ( metrics , "transmit_window_errors" )
}
}
func pop ( m map [ string ] uint64 , key string ) ( uint64 , bool ) {
value , ok := m [ key ]
delete ( m , key )
return value , ok
}
func popz ( m map [ string ] uint64 , key string ) uint64 {
if value , ok := m [ key ] ; ok {
delete ( m , key )
return value
}
return 0
}