feat(core): add process list collector
This commit is contained in:
1 parent
90b7d72e68
commit
5c8c6d565f
5 files changed
+125
No files matched your search
@@ -0,0 +1,8 @@
|
||||
using Vantage.Core.Models;
|
||||
|
||||
namespace Vantage.Core.Collectors.Processes;
|
||||
|
||||
public interface IProcessProvider
|
||||
{
|
||||
Task<IReadOnlyList<ProcessInfo>> GetTopProcessesAsync(HostConfig host, int count, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Vantage.Core.Models;
|
||||
|
||||
namespace Vantage.Core.Collectors.Processes;
|
||||
|
||||
public sealed class MockProcessProvider : IProcessProvider
|
||||
{
|
||||
private static readonly (string User, string Command, double Cpu, double Mem)[] Catalog =
|
||||
{
|
||||
("root", "/usr/lib/systemd/systemd", 0.3, 0.4),
|
||||
("postgres", "postgres: writer", 3.1, 8.7),
|
||||
("root", "dockerd", 5.4, 2.1),
|
||||
("www-data", "nginx: worker", 1.2, 0.9),
|
||||
("root", "containerd", 2.8, 1.5),
|
||||
("node", "node server.js", 12.6, 6.3),
|
||||
("root", "sshd: foksiu", 0.1, 0.2),
|
||||
("prometheus", "prometheus", 4.7, 3.9),
|
||||
("grafana", "grafana-server", 1.9, 4.2),
|
||||
("root", "journald", 0.2, 0.3)
|
||||
};
|
||||
|
||||
private readonly Random random;
|
||||
|
||||
public MockProcessProvider(int seed = 1337)
|
||||
{
|
||||
random = new Random(seed);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<ProcessInfo>> GetTopProcessesAsync(HostConfig host, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var pid = 100;
|
||||
var processes = Catalog
|
||||
.Select(entry => new ProcessInfo(
|
||||
pid += random.Next(1, 900),
|
||||
entry.User,
|
||||
Math.Round(entry.Cpu + random.NextDouble() * 4, 1),
|
||||
Math.Round(entry.Mem + random.NextDouble() * 2, 1),
|
||||
entry.Command))
|
||||
.OrderByDescending(p => p.CpuPercent)
|
||||
.Take(count)
|
||||
.ToArray();
|
||||
|
||||
return Task.FromResult<IReadOnlyList<ProcessInfo>>(processes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Vantage.Core.Collectors.Processes;
|
||||
|
||||
public readonly record struct ProcessInfo(
|
||||
int Pid,
|
||||
string User,
|
||||
double CpuPercent,
|
||||
double MemoryPercent,
|
||||
string Command);
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace Vantage.Core.Collectors.Processes;
|
||||
|
||||
public static class ProcessListParser
|
||||
{
|
||||
public static IReadOnlyList<ProcessInfo> Parse(string psOutput)
|
||||
{
|
||||
var processes = new List<ProcessInfo>();
|
||||
|
||||
foreach (var rawLine in psOutput.Replace("\r\n", "\n").Split('\n'))
|
||||
{
|
||||
var line = rawLine.Trim();
|
||||
if (line.Length == 0)
|
||||
continue;
|
||||
|
||||
var fields = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (fields.Length < 5)
|
||||
continue;
|
||||
|
||||
if (!int.TryParse(fields[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var pid))
|
||||
continue;
|
||||
|
||||
if (!TryParsePercent(fields[2], out var cpu) || !TryParsePercent(fields[3], out var mem))
|
||||
continue;
|
||||
|
||||
var command = string.Join(' ', fields[4..]);
|
||||
processes.Add(new ProcessInfo(pid, fields[1], cpu, mem, command));
|
||||
}
|
||||
|
||||
return processes;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<ProcessInfo> TopByCpu(string psOutput, int count) =>
|
||||
Parse(psOutput).OrderByDescending(p => p.CpuPercent).Take(count).ToArray();
|
||||
|
||||
private static bool TryParsePercent(string value, out double parsed) =>
|
||||
double.TryParse(value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out parsed);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Vantage.Core.Models;
|
||||
using Vantage.Core.Ssh;
|
||||
|
||||
namespace Vantage.Core.Collectors.Processes;
|
||||
|
||||
public sealed class SshProcessProvider : IProcessProvider
|
||||
{
|
||||
private readonly ISshCommandRunner runner;
|
||||
|
||||
public SshProcessProvider(ISshCommandRunner runner)
|
||||
{
|
||||
this.runner = runner;
|
||||
}
|
||||
|
||||
public static string BuildCommand(int count)
|
||||
{
|
||||
var limit = Math.Max(1, count);
|
||||
return $"ps -eo pid,user:20,pcpu,pmem,comm --sort=-pcpu --no-headers | head -n {limit}";
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<ProcessInfo>> GetTopProcessesAsync(HostConfig host, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var output = await runner.RunAsync(host, BuildCommand(count), cancellationToken).ConfigureAwait(false);
|
||||
return ProcessListParser.TopByCpu(output, count);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user