@ -17,86 +17,60 @@
package collector
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strconv"
"strings"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
)
var (
procNetDevInterfaceRE = regexp . MustCompile ( ` ^(.+): *(.+)$ ` )
procNetDevFieldSep = regexp . MustCompile ( ` + ` )
"github.com/jsimonetti/rtnetlink"
)
func getNetDevStats ( filter * deviceFilter , logger log . Logger ) ( netDevStats , error ) {
file , err := os . Open ( procFilePath ( "net/dev" ) )
conn , err := rtnetlink . Dial ( nil )
if err != nil {
return nil , err
}
defer file . Close ( )
defer conn . Close ( )
return parseNetDevStats ( file , filter , logger )
}
func parseNetDevStats ( r io . Reader , filter * deviceFilter , logger log . Logger ) ( netDevStats , error ) {
scanner := bufio . NewScanner ( r )
scanner . Scan ( ) // skip first header
scanner . Scan ( )
parts := strings . Split ( scanner . Text ( ) , "|" )
if len ( parts ) != 3 { // interface + receive + transmit
return nil , fmt . Errorf ( "invalid header line in net/dev: %s" ,
scanner . Text ( ) )
links , err := conn . Link . List ( )
if err != nil {
return nil , err
}
receiveHeader := strings . Fields ( parts [ 1 ] )
transmitHeader := strings . Fields ( parts [ 2 ] )
headerLength := len ( receiveHeader ) + len ( transmitHeader )
netDev := netDevStats { }
for scanner . Scan ( ) {
line := strings . TrimLeft ( scanner . Text ( ) , " " )
parts := procNetDevInterfaceRE . FindStringSubmatch ( line )
if len ( parts ) != 3 {
return nil , fmt . Errorf ( "couldn't get interface name, invalid line in net/dev: %q" , line )
}
dev := parts [ 1 ]
if filter . ignored ( dev ) {
level . Debug ( logger ) . Log ( "msg" , "Ignoring device" , "device" , dev )
continue
}
return netlinkStats ( links , filter , logger ) , nil
}
values := procNetDevFieldSep . Split ( strings . TrimLeft ( parts [ 2 ] , " " ) , - 1 )
if len ( values ) != headerLength {
return nil , fmt . Errorf ( "couldn't get values, invalid line in net/dev: %q" , parts [ 2 ] )
}
func netlinkStats ( links [ ] rtnetlink . LinkMessage , filter * deviceFilter , logger log . Logger ) netDevStats {
metrics := netDevStats { }
devStats := map [ string ] uint64 { }
addStats := func ( key , value string ) {
v , err := strconv . ParseUint ( value , 0 , 64 )
if err != nil {
level . Debug ( logger ) . Log ( "msg" , "invalid value in netstats" , "key" , key , "value" , value , "err" , err )
return
}
for _ , msg := range links {
name := msg . Attributes . Name
stats := msg . Attributes . Stats64
devStats [ key ] = v
}
for i := 0 ; i < len ( receiveHeader ) ; i ++ {
addStats ( "receive_" + receiveHeader [ i ] , values [ i ] )
if filter . ignored ( name ) {
level . Debug ( logger ) . Log ( "msg" , "Ignoring device" , "device" , name )
continue
}
for i := 0 ; i < len ( transmitHeader ) ; i ++ {
addStats ( "transmit_" + transmitHeader [ i ] , values [ i + len ( receiveHeader ) ] )
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/if_link.h#L42-L246
// https://github.com/torvalds/linux/blob/master/net/core/net-procfs.c#L75-L97
metrics [ name ] = map [ string ] uint64 {
"receive_packets" : stats . RXPackets ,
"transmit_packets" : stats . TXPackets ,
"receive_bytes" : stats . RXBytes ,
"transmit_bytes" : stats . TXBytes ,
"receive_errs" : stats . RXErrors ,
"transmit_errs" : stats . TXErrors ,
"receive_drop" : stats . RXDropped + stats . RXMissedErrors ,
"transmit_drop" : stats . TXDropped ,
"receive_multicast" : stats . Multicast ,
"transmit_colls" : stats . Collisions ,
"receive_frame" : stats . RXLengthErrors + stats . RXOverErrors + stats . RXCRCErrors + stats . RXFrameErrors ,
"receive_fifo" : stats . RXFIFOErrors ,
"transmit_carrier" : stats . TXAbortedErrors + stats . TXCarrierErrors + stats . TXHeartbeatErrors + stats . TXWindowErrors ,
"transmit_fifo" : stats . TXFIFOErrors ,
"receive_compressed" : stats . RXCompressed ,
"transmit_compressed" : stats . TXCompressed ,
}
netDev [ dev ] = devStats
}
return netDev , scanner . Err ( )
return metrics
}