mirror of
https://github.com/henrygd/beszel.git
synced 2025-12-17 02:36:17 +01:00
138 lines
4.4 KiB
Go
138 lines
4.4 KiB
Go
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/henrygd/beszel/internal/entities/system"
|
|
psutilNet "github.com/shirou/gopsutil/v4/net"
|
|
)
|
|
|
|
func (a *Agent) updateNetworkStats(systemStats *system.Stats) {
|
|
// network stats
|
|
if len(a.netInterfaces) == 0 {
|
|
// if no network interfaces, initialize again
|
|
// this is a fix if agent started before network is online (#466)
|
|
// maybe refactor this in the future to not cache interface names at all so we
|
|
// don't miss an interface that's been added after agent started in any circumstance
|
|
a.initializeNetIoStats()
|
|
}
|
|
|
|
if systemStats.NetworkInterfaces == nil {
|
|
systemStats.NetworkInterfaces = make(map[string][4]uint64, 0)
|
|
}
|
|
|
|
if netIO, err := psutilNet.IOCounters(true); err == nil {
|
|
msElapsed := uint64(time.Since(a.netIoStats.Time).Milliseconds())
|
|
a.netIoStats.Time = time.Now()
|
|
totalBytesSent := uint64(0)
|
|
totalBytesRecv := uint64(0)
|
|
netInterfaceDeltaTracker.Cycle()
|
|
// sum all bytes sent and received
|
|
for _, v := range netIO {
|
|
// skip if not in valid network interfaces list
|
|
if _, exists := a.netInterfaces[v.Name]; !exists {
|
|
continue
|
|
}
|
|
totalBytesSent += v.BytesSent
|
|
totalBytesRecv += v.BytesRecv
|
|
|
|
// track deltas for each network interface
|
|
netInterfaceDeltaTracker.Set(fmt.Sprintf("%sdown", v.Name), v.BytesRecv)
|
|
netInterfaceDeltaTracker.Set(fmt.Sprintf("%sup", v.Name), v.BytesSent)
|
|
upDelta := netInterfaceDeltaTracker.Delta(fmt.Sprintf("%sup", v.Name)) * 1000 / msElapsed
|
|
downDelta := netInterfaceDeltaTracker.Delta(fmt.Sprintf("%sdown", v.Name)) * 1000 / msElapsed
|
|
// add interface to systemStats
|
|
systemStats.NetworkInterfaces[v.Name] = [4]uint64{upDelta, downDelta, v.BytesSent, v.BytesRecv}
|
|
}
|
|
|
|
// add to systemStats
|
|
var bytesSentPerSecond, bytesRecvPerSecond uint64
|
|
if msElapsed > 0 {
|
|
bytesSentPerSecond = (totalBytesSent - a.netIoStats.BytesSent) * 1000 / msElapsed
|
|
bytesRecvPerSecond = (totalBytesRecv - a.netIoStats.BytesRecv) * 1000 / msElapsed
|
|
}
|
|
networkSentPs := bytesToMegabytes(float64(bytesSentPerSecond))
|
|
networkRecvPs := bytesToMegabytes(float64(bytesRecvPerSecond))
|
|
// add check for issue (#150) where sent is a massive number
|
|
if networkSentPs > 10_000 || networkRecvPs > 10_000 {
|
|
slog.Warn("Invalid net stats. Resetting.", "sent", networkSentPs, "recv", networkRecvPs)
|
|
for _, v := range netIO {
|
|
if _, exists := a.netInterfaces[v.Name]; !exists {
|
|
continue
|
|
}
|
|
slog.Info(v.Name, "recv", v.BytesRecv, "sent", v.BytesSent)
|
|
}
|
|
// reset network I/O stats
|
|
a.initializeNetIoStats()
|
|
} else {
|
|
systemStats.NetworkSent = networkSentPs
|
|
systemStats.NetworkRecv = networkRecvPs
|
|
systemStats.Bandwidth[0], systemStats.Bandwidth[1] = bytesSentPerSecond, bytesRecvPerSecond
|
|
// update netIoStats
|
|
a.netIoStats.BytesSent = totalBytesSent
|
|
a.netIoStats.BytesRecv = totalBytesRecv
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *Agent) initializeNetIoStats() {
|
|
// reset valid network interfaces
|
|
a.netInterfaces = make(map[string]struct{}, 0)
|
|
|
|
// map of network interface names passed in via NICS env var
|
|
var nicsMap map[string]struct{}
|
|
nics, nicsEnvExists := GetEnv("NICS")
|
|
if nicsEnvExists {
|
|
nicsMap = make(map[string]struct{}, 0)
|
|
for nic := range strings.SplitSeq(nics, ",") {
|
|
nicsMap[nic] = struct{}{}
|
|
}
|
|
}
|
|
|
|
// reset network I/O stats
|
|
a.netIoStats.BytesSent = 0
|
|
a.netIoStats.BytesRecv = 0
|
|
|
|
// get intial network I/O stats
|
|
if netIO, err := psutilNet.IOCounters(true); err == nil {
|
|
a.netIoStats.Time = time.Now()
|
|
for _, v := range netIO {
|
|
switch {
|
|
// skip if nics exists and the interface is not in the list
|
|
case nicsEnvExists:
|
|
if _, nameInNics := nicsMap[v.Name]; !nameInNics {
|
|
continue
|
|
}
|
|
// otherwise run the interface name through the skipNetworkInterface function
|
|
default:
|
|
if a.skipNetworkInterface(v) {
|
|
continue
|
|
}
|
|
}
|
|
slog.Info("Detected network interface", "name", v.Name, "sent", v.BytesSent, "recv", v.BytesRecv)
|
|
a.netIoStats.BytesSent += v.BytesSent
|
|
a.netIoStats.BytesRecv += v.BytesRecv
|
|
// store as a valid network interface
|
|
a.netInterfaces[v.Name] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *Agent) skipNetworkInterface(v psutilNet.IOCountersStat) bool {
|
|
switch {
|
|
case strings.HasPrefix(v.Name, "lo"),
|
|
strings.HasPrefix(v.Name, "docker"),
|
|
strings.HasPrefix(v.Name, "br-"),
|
|
strings.HasPrefix(v.Name, "veth"),
|
|
strings.HasPrefix(v.Name, "bond"),
|
|
v.BytesRecv == 0,
|
|
v.BytesSent == 0:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|