130 lines
4.2 KiB
Go
130 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type NetworkMetrics struct {
|
|
Interface string `json:"interface"`
|
|
ReceiveBytes uint64 `json:"receiveBytes"`
|
|
TransmitBytes uint64 `json:"transmitBytes"`
|
|
ReceivePerSec float64 `json:"receivePerSec"`
|
|
TransmitPerSec float64 `json:"transmitPerSec"`
|
|
Interfaces []NetworkInterface `json:"interfaces"`
|
|
}
|
|
|
|
type NetworkInterface struct {
|
|
Name string `json:"name"`
|
|
ReceiveBytes uint64 `json:"receiveBytes"`
|
|
TransmitBytes uint64 `json:"transmitBytes"`
|
|
ReceiveErrors uint64 `json:"receiveErrors"`
|
|
TransmitErrors uint64 `json:"transmitErrors"`
|
|
ReceiveDrops uint64 `json:"receiveDrops"`
|
|
TransmitDrops uint64 `json:"transmitDrops"`
|
|
ReceivePerSec float64 `json:"receivePerSec"`
|
|
TransmitPerSec float64 `json:"transmitPerSec"`
|
|
VMID int `json:"vmid,omitempty"`
|
|
GuestName string `json:"guestName,omitempty"`
|
|
GuestType string `json:"guestType,omitempty"`
|
|
}
|
|
|
|
type networkSnapshot struct {
|
|
at time.Time
|
|
received uint64
|
|
sent uint64
|
|
}
|
|
|
|
type networkCollector struct {
|
|
mu sync.Mutex
|
|
path string
|
|
previous networkSnapshot
|
|
previousInterfaces map[string]NetworkInterface
|
|
}
|
|
|
|
func newNetworkCollector(path string) (*networkCollector, error) {
|
|
snapshot, _, _, err := readNetworkSnapshot(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, _, details, _ := readNetworkSnapshot(path)
|
|
previous := map[string]NetworkInterface{}
|
|
for _, v := range details {
|
|
previous[v.Name] = v
|
|
}
|
|
return &networkCollector{path: path, previous: snapshot, previousInterfaces: previous}, nil
|
|
}
|
|
|
|
func (c *networkCollector) collect() (NetworkMetrics, error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
current, interfaceNames, details, err := readNetworkSnapshot(c.path)
|
|
if err != nil {
|
|
return NetworkMetrics{}, err
|
|
}
|
|
elapsed := current.at.Sub(c.previous.at).Seconds()
|
|
var receivedRate, sentRate float64
|
|
if elapsed > 0 && current.received >= c.previous.received && current.sent >= c.previous.sent {
|
|
receivedRate = float64(current.received-c.previous.received) / elapsed
|
|
sentRate = float64(current.sent-c.previous.sent) / elapsed
|
|
}
|
|
for i := range details {
|
|
v := &details[i]
|
|
if old, ok := c.previousInterfaces[v.Name]; ok && elapsed > 0 && v.ReceiveBytes >= old.ReceiveBytes && v.TransmitBytes >= old.TransmitBytes {
|
|
v.ReceivePerSec = float64(v.ReceiveBytes-old.ReceiveBytes) / elapsed
|
|
v.TransmitPerSec = float64(v.TransmitBytes-old.TransmitBytes) / elapsed
|
|
}
|
|
}
|
|
c.previousInterfaces = map[string]NetworkInterface{}
|
|
for _, v := range details {
|
|
c.previousInterfaces[v.Name] = v
|
|
}
|
|
c.previous = current
|
|
return NetworkMetrics{
|
|
Interface: strings.Join(interfaceNames, ", "), Interfaces: details, ReceiveBytes: current.received,
|
|
TransmitBytes: current.sent, ReceivePerSec: receivedRate, TransmitPerSec: sentRate,
|
|
}, nil
|
|
}
|
|
|
|
func readNetworkSnapshot(path string) (networkSnapshot, []string, []NetworkInterface, error) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return networkSnapshot{}, nil, nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
var received, sent uint64
|
|
var interfaces []string
|
|
var details []NetworkInterface
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
name, values, found := strings.Cut(line, ":")
|
|
if !found || strings.TrimSpace(name) == "lo" {
|
|
continue
|
|
}
|
|
fields := strings.Fields(values)
|
|
if len(fields) < 9 {
|
|
continue
|
|
}
|
|
rx, errRX := strconv.ParseUint(fields[0], 10, 64)
|
|
tx, errTX := strconv.ParseUint(fields[8], 10, 64)
|
|
if errRX == nil && errTX == nil {
|
|
received += rx
|
|
sent += tx
|
|
interfaces = append(interfaces, strings.TrimSpace(name))
|
|
rxErr, _ := strconv.ParseUint(fields[2], 10, 64)
|
|
rxDrop, _ := strconv.ParseUint(fields[3], 10, 64)
|
|
txErr, _ := strconv.ParseUint(fields[10], 10, 64)
|
|
txDrop, _ := strconv.ParseUint(fields[11], 10, 64)
|
|
details = append(details, NetworkInterface{Name: strings.TrimSpace(name), ReceiveBytes: rx, TransmitBytes: tx, ReceiveErrors: rxErr, ReceiveDrops: rxDrop, TransmitErrors: txErr, TransmitDrops: txDrop})
|
|
}
|
|
}
|
|
return networkSnapshot{at: time.Now(), received: received, sent: sent}, interfaces, details, scanner.Err()
|
|
}
|