Implemented node system statistics
This commit is contained in:
101
MoonlightServers.Daemon/Helpers/SystemMetrics.Network.cs
Normal file
101
MoonlightServers.Daemon/Helpers/SystemMetrics.Network.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
public partial class SystemMetrics
|
||||
{
|
||||
/// <summary>Network throughput for a single interface, computed between two samples.</summary>
|
||||
/// <param name="Name">Interface name, e.g. <c>eth0</c>, <c>ens3</c>.</param>
|
||||
/// <param name="RxBytesPerSec">Received bytes per second.</param>
|
||||
/// <param name="TxBytesPerSec">Transmitted bytes per second.</param>
|
||||
/// <param name="RxPacketsPerSec">Received packets per second.</param>
|
||||
/// <param name="TxPacketsPerSec">Transmitted packets per second.</param>
|
||||
/// <param name="RxErrors">Cumulative receive error count (not a rate).</param>
|
||||
/// <param name="TxErrors">Cumulative transmit error count (not a rate).</param>
|
||||
public record NetworkInterfaceInfo(
|
||||
string Name,
|
||||
long RxBytesPerSec,
|
||||
long TxBytesPerSec,
|
||||
long RxPacketsPerSec,
|
||||
long TxPacketsPerSec,
|
||||
long RxErrors,
|
||||
long TxErrors
|
||||
);
|
||||
|
||||
// Network
|
||||
private record RawNetLine(
|
||||
string Iface,
|
||||
long RxBytes,
|
||||
long RxPackets,
|
||||
long RxErrors,
|
||||
long TxBytes,
|
||||
long TxPackets,
|
||||
long TxErrors
|
||||
);
|
||||
|
||||
private static async Task<List<RawNetLine>> ReadRawNetStatsAsync()
|
||||
{
|
||||
var lines = await File.ReadAllLinesAsync("/proc/net/dev");
|
||||
var result = new List<RawNetLine>();
|
||||
|
||||
// The first two lines are the column-header banner.
|
||||
foreach (var line in lines.Skip(2))
|
||||
{
|
||||
var colon = line.IndexOf(':');
|
||||
if (colon < 0)
|
||||
continue;
|
||||
|
||||
var iface = line[..colon].Trim();
|
||||
|
||||
// Skip loopback and ephemeral veth pairs created by container runtimes.
|
||||
if (iface == "lo" || iface.StartsWith("veth", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
var f = line[(colon + 1)..].Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (f.Length < 16)
|
||||
continue;
|
||||
|
||||
// Column layout (after the colon, 0-based):
|
||||
// RX: [0]bytes [1]packets [2]errs [3]drop [4]fifo [5]frame [6]compressed [7]multicast
|
||||
// TX: [8]bytes [9]packets [10]errs [11]drop [12]fifo [13]colls [14]carrier [15]compressed
|
||||
result.Add(new RawNetLine(
|
||||
Iface: iface,
|
||||
RxBytes: long.Parse(f[0]),
|
||||
RxPackets: long.Parse(f[1]),
|
||||
RxErrors: long.Parse(f[2]),
|
||||
TxBytes: long.Parse(f[8]),
|
||||
TxPackets: long.Parse(f[9]),
|
||||
TxErrors: long.Parse(f[10])
|
||||
));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<NetworkInterfaceInfo> ComputeNetworkRates(
|
||||
List<RawNetLine> s1,
|
||||
List<RawNetLine> s2,
|
||||
double intervalSecs
|
||||
)
|
||||
{
|
||||
var prev = s1.ToDictionary(x => x.Iface);
|
||||
var result = new List<NetworkInterfaceInfo>();
|
||||
var div = intervalSecs > 0 ? intervalSecs : 1.0;
|
||||
|
||||
foreach (var cur in s2)
|
||||
{
|
||||
if (!prev.TryGetValue(cur.Iface, out var p))
|
||||
continue;
|
||||
|
||||
result.Add(new NetworkInterfaceInfo(
|
||||
Name: cur.Iface,
|
||||
RxBytesPerSec: (long)((cur.RxBytes - p.RxBytes) / div),
|
||||
TxBytesPerSec: (long)((cur.TxBytes - p.TxBytes) / div),
|
||||
RxPacketsPerSec: (long)((cur.RxPackets - p.RxPackets) / div),
|
||||
TxPacketsPerSec: (long)((cur.TxPackets - p.TxPackets) / div),
|
||||
RxErrors: cur.RxErrors,
|
||||
TxErrors: cur.TxErrors
|
||||
));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user