-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature flag for old/new system metrics collectors on linux
- Loading branch information
Vitaliy Roshchupkin
committed
Apr 5, 2024
1 parent
7364f95
commit 65609c7
Showing
12 changed files
with
561 additions
and
4 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
Vostok.Metrics.System/Helpers/VostokSystemMetricsConstants.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
Vostok.Metrics.System/Host/INativeHostMetricsCollector_Linux.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
41 changes: 41 additions & 0 deletions
41
Vostok.Metrics.System/Host/Legacy/LegacyHostCpuUtilizationCollector.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
238 changes: 238 additions & 0 deletions
238
Vostok.Metrics.System/Host/Legacy/LegacyNativeHostMetricsCollector_Linux.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
Vostok.Metrics.System/Process/INativeProcessMetricsCollector_Linux.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
Oops, something went wrong.