Skip to content

Commit

Permalink
feature flag for old/new system metrics collectors on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitaliy Roshchupkin committed Apr 5, 2024
1 parent 7364f95 commit 65609c7
Show file tree
Hide file tree
Showing 12 changed files with 561 additions and 4 deletions.
7 changes: 7 additions & 0 deletions Vostok.Metrics.System/Helpers/VostokSystemMetricsConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Vostok.Metrics.System.Helpers
{
public static class VostokSystemMetricsConstants
{
public const string UseLegacyMetricsCollectorEnvironmentVariable = "VOSTOK_METRICS_SYSTEM_LINUX_USE_LEGACY_COLLECTOR";
}
}
7 changes: 6 additions & 1 deletion Vostok.Metrics.System/Host/HostMetricsCollector.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using Vostok.Logging.Abstractions;
using Vostok.Metrics.System.Helpers;
using Vostok.Metrics.System.Host.Legacy;

namespace Vostok.Metrics.System.Host
{
Expand Down Expand Up @@ -37,7 +39,10 @@ public HostMetricsCollector(HostMetricsSettings settings)

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var collector = new NativeHostMetricsCollector_Linux(this.settings);
var useLegacyCollector = Environment.GetEnvironmentVariable(VostokSystemMetricsConstants.UseLegacyMetricsCollectorEnvironmentVariable) == "TRUE";
INativeHostMetricsCollector_Linux collector = useLegacyCollector
? new LegacyNativeHostMetricsCollector_Linux(this.settings)

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (macos-12, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (ubuntu-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, cement)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete

Check warning on line 44 in Vostok.Metrics.System/Host/HostMetricsCollector.cs

View workflow job for this annotation

GitHub Actions / build / build (windows-latest, nuget)

'LegacyNativeHostMetricsCollector_Linux' is obsolete
: new NativeHostMetricsCollector_Linux(this.settings);
nativeCollector = collector.Collect;
disposeNativeCollector = collector.Dispose;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace Vostok.Metrics.System.Host;

// ReSharper disable once InconsistentNaming
internal interface INativeHostMetricsCollector_Linux : IDisposable
{
void Collect(HostMetrics metrics);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using Vostok.Metrics.System.Helpers;

namespace Vostok.Metrics.System.Host.Legacy
{
internal class LegacyHostCpuUtilizationCollector
{
private readonly Func<int?> coresCountProvider;
private int previousCoresCount = Environment.ProcessorCount;
private ulong previousSystemTime;
private ulong previousIdleTime;

public LegacyHostCpuUtilizationCollector(Func<int?> coresCountProvider)
{
this.coresCountProvider = coresCountProvider;
}

public void Collect(HostMetrics metrics, ulong systemTime, ulong idleTime)
{
var systemTimeDiff = (double)systemTime - previousSystemTime;
var idleTimeDiff = (double)idleTime - previousIdleTime;
var spentTimeDiff = 1 - idleTimeDiff / systemTimeDiff;

metrics.CpuTotalCores = previousCoresCount = coresCountProvider() ?? previousCoresCount;

if (previousSystemTime == 0 || systemTimeDiff <= 0)
{
metrics.CpuUtilizedCores = 0d;
metrics.CpuUtilizedFraction = 0d;
}
else
{
metrics.CpuUtilizedCores = (metrics.CpuTotalCores * spentTimeDiff).Clamp(0, metrics.CpuTotalCores);
metrics.CpuUtilizedFraction = spentTimeDiff.Clamp(0, 1);
}

previousSystemTime = systemTime;
previousIdleTime = idleTime;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Vostok.Metrics.System.Helpers;

// ReSharper disable PossibleInvalidOperationException
namespace Vostok.Metrics.System.Host.Legacy
{
[Obsolete]
internal class LegacyNativeHostMetricsCollector_Linux : INativeHostMetricsCollector_Linux
{
private readonly HostMetricsSettings settings;

private readonly Regex pidRegex = new Regex("[0-9]+$", RegexOptions.Compiled);

// See https://man7.org/linux/man-pages/man5/proc.5.html for information about each file
private readonly ReusableFileReader systemStatReader = new ReusableFileReader("/proc/stat");
private readonly ReusableFileReader memoryReader = new ReusableFileReader("/proc/meminfo");
private readonly ReusableFileReader vmStatReader = new ReusableFileReader("/proc/vmstat");
private readonly ReusableFileReader descriptorInfoReader = new ReusableFileReader("/proc/sys/fs/file-nr");
private readonly ReusableFileReader cpuInfoReader = new ReusableFileReader("/proc/cpuinfo");

private readonly LegacyHostCpuUtilizationCollector cpuCollector;
private readonly NetworkUtilizationCollector_Linux networkCollector = new NetworkUtilizationCollector_Linux();
private readonly DerivativeCollector hardPageFaultCollector = new DerivativeCollector();
private readonly DiskUsageCollector_Linux diskUsageCollector = new DiskUsageCollector_Linux();

public LegacyNativeHostMetricsCollector_Linux(HostMetricsSettings settings)
{
this.settings = settings;
cpuCollector = new LegacyHostCpuUtilizationCollector(GetProcessorCount);
}

public void Dispose()
{
systemStatReader.Dispose();
memoryReader.Dispose();
vmStatReader.Dispose();
descriptorInfoReader.Dispose();
networkCollector.Dispose();
diskUsageCollector.Dispose();
}

public void Collect(HostMetrics metrics)
{
var systemStat = settings.CollectCpuMetrics ? ReadSystemStat() : new SystemStat();
var memInfo = settings.CollectMemoryMetrics ? ReadMemoryInfo() : new MemoryInfo();
var perfInfo = settings.CollectMiscMetrics ? ReadPerformanceInfo() : new PerformanceInfo();

if (systemStat.Filled)
{
var usedTime = systemStat.UserTime.Value + systemStat.NicedTime.Value +
systemStat.SystemTime.Value + systemStat.IdleTime.Value;

cpuCollector.Collect(metrics, usedTime, systemStat.IdleTime.Value);
}

if (memInfo.Filled)
{
metrics.MemoryAvailable = memInfo.AvailableMemory.Value;
metrics.MemoryCached = memInfo.CacheMemory.Value;
metrics.MemoryKernel = memInfo.KernelMemory.Value;
metrics.MemoryTotal = memInfo.TotalMemory.Value;
metrics.MemoryFree = memInfo.FreeMemory.Value;
metrics.PageFaultsPerSecond = (long) hardPageFaultCollector.Collect(memInfo.MajorPageFaultCount.Value);
}

if (perfInfo.Filled)
{
metrics.HandleCount = perfInfo.HandleCount.Value;
metrics.ThreadCount = perfInfo.ThreadCount.Value;
metrics.ProcessCount = perfInfo.ProcessCount.Value;
}

if (settings.CollectNetworkUsageMetrics)
networkCollector.Collect(metrics);

if (settings.CollectDiskUsageMetrics)
diskUsageCollector.Collect(metrics);
}

private SystemStat ReadSystemStat()
{
var result = new SystemStat();

try
{
if (FileParser.TrySplitLine(systemStatReader.ReadFirstLine(), 7, out var parts) && parts[0] == "cpu")
{
if (ulong.TryParse(parts[1], out var utime))
result.UserTime = utime;

if (ulong.TryParse(parts[2], out var ntime))
result.NicedTime = ntime;

if (ulong.TryParse(parts[3], out var stime))
result.SystemTime = stime;

if (ulong.TryParse(parts[4], out var itime))
result.IdleTime = itime;
}
}
catch (Exception error)
{
InternalErrorLogger.Warn(error);
}

return result;
}

private int? GetProcessorCount()
{
try
{
return cpuInfoReader.ReadLines().Count(x => x.Contains("processor"));
}
catch (Exception error)
{
InternalErrorLogger.Warn(error);
return null;
}
}

private MemoryInfo ReadMemoryInfo()
{
var result = new MemoryInfo();

try
{
foreach (var line in memoryReader.ReadLines())
{
if (FileParser.TryParseLong(line, "MemTotal", out var memTotal))
result.TotalMemory = memTotal * 1024;

if (FileParser.TryParseLong(line, "MemAvailable", out var memAvailable))
result.AvailableMemory = memAvailable * 1024;

if (FileParser.TryParseLong(line, "Cached", out var memCached))
result.CacheMemory = memCached * 1024;

if (FileParser.TryParseLong(line, "Slab", out var memKernel))
result.KernelMemory = memKernel * 1024;

if (FileParser.TryParseLong(line, "MemFree", out var memFree))
result.FreeMemory = memFree * 1024;
}

foreach (var line in vmStatReader.ReadLines())
{
if (FileParser.TryParseLong(line, "pgmajfault", out var hardPageFaultCount))
result.MajorPageFaultCount = hardPageFaultCount;
}
}
catch (Exception error)
{
InternalErrorLogger.Warn(error);
}

return result;
}

private PerformanceInfo ReadPerformanceInfo()
{
var result = new PerformanceInfo();

try
{
var processDirectories = Directory.EnumerateDirectories("/proc/")
.Where(x => pidRegex.IsMatch(x));

var processCount = 0;
var threadCount = 0;

foreach (var processDirectory in processDirectories)
{
try
{
threadCount += Directory.EnumerateDirectories(Path.Combine(processDirectory, "task")).Count();
}
catch (DirectoryNotFoundException)
{
// NOTE: Ignored due to process already exited so we don't have to count it's threads and just want to continue.
continue;
}

processCount++;
}

result.ProcessCount = processCount;
result.ThreadCount = threadCount;

if (FileParser.TrySplitLine(descriptorInfoReader.ReadFirstLine(), 3, out var parts) &&
int.TryParse(parts[0], out var allocatedDescriptors) &&
int.TryParse(parts[1], out var freeDescriptors))
result.HandleCount = allocatedDescriptors - freeDescriptors;
}
catch (Exception error)
{
InternalErrorLogger.Warn(error);
}

return result;
}

private class PerformanceInfo
{
public bool Filled => ProcessCount.HasValue && ThreadCount.HasValue && HandleCount.HasValue;

public int? ProcessCount { get; set; }
public int? ThreadCount { get; set; }
public int? HandleCount { get; set; }
}

private class SystemStat
{
public bool Filled => UserTime.HasValue && NicedTime.HasValue && SystemTime.HasValue && IdleTime.HasValue;

public ulong? UserTime { get; set; }
public ulong? NicedTime { get; set; }
public ulong? SystemTime { get; set; }
public ulong? IdleTime { get; set; }
}

private class MemoryInfo
{
public bool Filled => AvailableMemory.HasValue && KernelMemory.HasValue && CacheMemory.HasValue &&
TotalMemory.HasValue && FreeMemory.HasValue && MajorPageFaultCount.HasValue;

public long? AvailableMemory { get; set; }
public long? KernelMemory { get; set; }
public long? CacheMemory { get; set; }
public long? FreeMemory { get; set; }
public long? TotalMemory { get; set; }
public long? MajorPageFaultCount { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Vostok.Metrics.System.Host
{
internal class NativeHostMetricsCollector_Linux : IDisposable
internal class NativeHostMetricsCollector_Linux : INativeHostMetricsCollector_Linux
{
private readonly HostMetricsSettings settings;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Vostok.Metrics.System.Dns;
using Vostok.Metrics.System.Helpers;
using Vostok.Metrics.System.Host;
using Vostok.Metrics.System.Process.Legacy;

namespace Vostok.Metrics.System.Process
{
Expand Down Expand Up @@ -90,7 +91,10 @@ public CurrentProcessMetricsCollector(CurrentProcessMetricsSettings settings)

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var collector = new NativeProcessMetricsCollector_Linux(this.settings.LinuxSettings);
var useLegacyCollector = Environment.GetEnvironmentVariable(VostokSystemMetricsConstants.UseLegacyMetricsCollectorEnvironmentVariable) == "TRUE";
INativeProcessMetricsCollector_Linux collector = useLegacyCollector
? new LegacyNativeMetricsCollector_Linux()
: new NativeProcessMetricsCollector_Linux(this.settings.LinuxSettings);
nativeCollector = collector.Collect;
disposeNativeCollector = collector.Dispose;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Vostok.Metrics.System.Process;

// ReSharper disable once InconsistentNaming
internal interface INativeProcessMetricsCollector_Linux
{
void Collect(CurrentProcessMetrics metrics);
void Dispose();
}
Loading

0 comments on commit 65609c7

Please sign in to comment.