From 65609c740a623d092420bad1c1c130cb152d726a Mon Sep 17 00:00:00 2001 From: Vitaliy Roshchupkin Date: Fri, 5 Apr 2024 10:53:50 +0500 Subject: [PATCH] feature flag for old/new system metrics collectors on linux --- .../Helpers/VostokSystemMetricsConstants.cs | 7 + .../Host/HostMetricsCollector.cs | 7 +- .../Host/INativeHostMetricsCollector_Linux.cs | 9 + .../LegacyHostCpuUtilizationCollector.cs | 41 +++ .../LegacyNativeHostMetricsCollector_Linux.cs | 238 ++++++++++++++++++ .../Host/NativeHostMetricsCollector_Linux.cs | 2 +- .../Process/CurrentProcessMetricsCollector.cs | 6 +- .../INativeProcessMetricsCollector_Linux.cs | 8 + ...cyCurrentProcessCpuUtilizationCollector.cs | 35 +++ ...gacyNativeProcessMetricsCollector_Linux.cs | 208 +++++++++++++++ .../NativeProcessMetricsCollector_Linux.cs | 2 +- Vostok.Metrics.System/PublicAPI.Unshipped.txt | 2 + 12 files changed, 561 insertions(+), 4 deletions(-) create mode 100644 Vostok.Metrics.System/Helpers/VostokSystemMetricsConstants.cs create mode 100644 Vostok.Metrics.System/Host/INativeHostMetricsCollector_Linux.cs create mode 100644 Vostok.Metrics.System/Host/Legacy/LegacyHostCpuUtilizationCollector.cs create mode 100644 Vostok.Metrics.System/Host/Legacy/LegacyNativeHostMetricsCollector_Linux.cs create mode 100644 Vostok.Metrics.System/Process/INativeProcessMetricsCollector_Linux.cs create mode 100644 Vostok.Metrics.System/Process/Legacy/LegacyCurrentProcessCpuUtilizationCollector.cs create mode 100644 Vostok.Metrics.System/Process/Legacy/LegacyNativeProcessMetricsCollector_Linux.cs diff --git a/Vostok.Metrics.System/Helpers/VostokSystemMetricsConstants.cs b/Vostok.Metrics.System/Helpers/VostokSystemMetricsConstants.cs new file mode 100644 index 0000000..c7a191b --- /dev/null +++ b/Vostok.Metrics.System/Helpers/VostokSystemMetricsConstants.cs @@ -0,0 +1,7 @@ +namespace Vostok.Metrics.System.Helpers +{ + public static class VostokSystemMetricsConstants + { + public const string UseLegacyMetricsCollectorEnvironmentVariable = "VOSTOK_METRICS_SYSTEM_LINUX_USE_LEGACY_COLLECTOR"; + } +} \ No newline at end of file diff --git a/Vostok.Metrics.System/Host/HostMetricsCollector.cs b/Vostok.Metrics.System/Host/HostMetricsCollector.cs index f308cb1..52b719e 100644 --- a/Vostok.Metrics.System/Host/HostMetricsCollector.cs +++ b/Vostok.Metrics.System/Host/HostMetricsCollector.cs @@ -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 { @@ -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) + : new NativeHostMetricsCollector_Linux(this.settings); nativeCollector = collector.Collect; disposeNativeCollector = collector.Dispose; } diff --git a/Vostok.Metrics.System/Host/INativeHostMetricsCollector_Linux.cs b/Vostok.Metrics.System/Host/INativeHostMetricsCollector_Linux.cs new file mode 100644 index 0000000..0906f0a --- /dev/null +++ b/Vostok.Metrics.System/Host/INativeHostMetricsCollector_Linux.cs @@ -0,0 +1,9 @@ +using System; + +namespace Vostok.Metrics.System.Host; + +// ReSharper disable once InconsistentNaming +internal interface INativeHostMetricsCollector_Linux : IDisposable +{ + void Collect(HostMetrics metrics); +} \ No newline at end of file diff --git a/Vostok.Metrics.System/Host/Legacy/LegacyHostCpuUtilizationCollector.cs b/Vostok.Metrics.System/Host/Legacy/LegacyHostCpuUtilizationCollector.cs new file mode 100644 index 0000000..cc7abaf --- /dev/null +++ b/Vostok.Metrics.System/Host/Legacy/LegacyHostCpuUtilizationCollector.cs @@ -0,0 +1,41 @@ +using System; +using Vostok.Metrics.System.Helpers; + +namespace Vostok.Metrics.System.Host.Legacy +{ + internal class LegacyHostCpuUtilizationCollector + { + private readonly Func coresCountProvider; + private int previousCoresCount = Environment.ProcessorCount; + private ulong previousSystemTime; + private ulong previousIdleTime; + + public LegacyHostCpuUtilizationCollector(Func 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; + } + } +} \ No newline at end of file diff --git a/Vostok.Metrics.System/Host/Legacy/LegacyNativeHostMetricsCollector_Linux.cs b/Vostok.Metrics.System/Host/Legacy/LegacyNativeHostMetricsCollector_Linux.cs new file mode 100644 index 0000000..870641a --- /dev/null +++ b/Vostok.Metrics.System/Host/Legacy/LegacyNativeHostMetricsCollector_Linux.cs @@ -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; } + } + } +} \ No newline at end of file diff --git a/Vostok.Metrics.System/Host/NativeHostMetricsCollector_Linux.cs b/Vostok.Metrics.System/Host/NativeHostMetricsCollector_Linux.cs index e111875..b7870e7 100644 --- a/Vostok.Metrics.System/Host/NativeHostMetricsCollector_Linux.cs +++ b/Vostok.Metrics.System/Host/NativeHostMetricsCollector_Linux.cs @@ -9,7 +9,7 @@ namespace Vostok.Metrics.System.Host { - internal class NativeHostMetricsCollector_Linux : IDisposable + internal class NativeHostMetricsCollector_Linux : INativeHostMetricsCollector_Linux { private readonly HostMetricsSettings settings; diff --git a/Vostok.Metrics.System/Process/CurrentProcessMetricsCollector.cs b/Vostok.Metrics.System/Process/CurrentProcessMetricsCollector.cs index 8a66612..0e77833 100644 --- a/Vostok.Metrics.System/Process/CurrentProcessMetricsCollector.cs +++ b/Vostok.Metrics.System/Process/CurrentProcessMetricsCollector.cs @@ -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 { @@ -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; } diff --git a/Vostok.Metrics.System/Process/INativeProcessMetricsCollector_Linux.cs b/Vostok.Metrics.System/Process/INativeProcessMetricsCollector_Linux.cs new file mode 100644 index 0000000..bc54941 --- /dev/null +++ b/Vostok.Metrics.System/Process/INativeProcessMetricsCollector_Linux.cs @@ -0,0 +1,8 @@ +namespace Vostok.Metrics.System.Process; + +// ReSharper disable once InconsistentNaming +internal interface INativeProcessMetricsCollector_Linux +{ + void Collect(CurrentProcessMetrics metrics); + void Dispose(); +} \ No newline at end of file diff --git a/Vostok.Metrics.System/Process/Legacy/LegacyCurrentProcessCpuUtilizationCollector.cs b/Vostok.Metrics.System/Process/Legacy/LegacyCurrentProcessCpuUtilizationCollector.cs new file mode 100644 index 0000000..39624ef --- /dev/null +++ b/Vostok.Metrics.System/Process/Legacy/LegacyCurrentProcessCpuUtilizationCollector.cs @@ -0,0 +1,35 @@ +using System; +using Vostok.Metrics.System.Helpers; + +namespace Vostok.Metrics.System.Process.Legacy +{ + internal class LegacyCpuUtilizationCollector + { + private static readonly int DefaultCoresCount = Environment.ProcessorCount; + + private ulong previousSystemTime; + private ulong previousProcessTime; + + public void Collect(CurrentProcessMetrics metrics, ulong systemTime, ulong processTime, int? systemCores = null) + { + var systemTimeDiff = (double) systemTime - previousSystemTime; + var processTimeDiff = (double) processTime - previousProcessTime; + + if (previousSystemTime == 0 || systemTimeDiff <= 0) + { + metrics.CpuUtilizedCores = 0d; + metrics.CpuUtilizedFraction = 0d; + } + else + { + var cores = systemCores ?? DefaultCoresCount; + + metrics.CpuUtilizedCores = (cores * processTimeDiff / systemTimeDiff).Clamp(0, cores); + metrics.CpuUtilizedFraction = (processTimeDiff / systemTimeDiff).Clamp(0, 1); + } + + previousSystemTime = systemTime; + previousProcessTime = processTime; + } + } +} \ No newline at end of file diff --git a/Vostok.Metrics.System/Process/Legacy/LegacyNativeProcessMetricsCollector_Linux.cs b/Vostok.Metrics.System/Process/Legacy/LegacyNativeProcessMetricsCollector_Linux.cs new file mode 100644 index 0000000..bcb52d4 --- /dev/null +++ b/Vostok.Metrics.System/Process/Legacy/LegacyNativeProcessMetricsCollector_Linux.cs @@ -0,0 +1,208 @@ +using System; +using System.IO; +using System.Linq; +using Vostok.Metrics.System.Helpers; + +// ReSharper disable PossibleInvalidOperationException + +namespace Vostok.Metrics.System.Process.Legacy +{ + internal class LegacyNativeMetricsCollector_Linux : INativeProcessMetricsCollector_Linux + { + private const string cgroupMemoryLimitFileName = "/sys/fs/cgroup/memory/memory.limit_in_bytes"; + private const string cgroupCpuCfsQuotaFileName = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"; + private const string cgroupCpuCfsPeriodFileName = "/sys/fs/cgroup/cpu/cpu.cfs_period_us"; + + private const string cgroupNoMemoryLimitValue = "9223372036854771712"; + private const string cgroupNoCpuLimitValue = "-1"; + + private readonly ReusableFileReader systemStatReader = new ReusableFileReader("/proc/stat"); + private readonly ReusableFileReader processStatReader = new ReusableFileReader("/proc/self/stat"); + private readonly ReusableFileReader processStatusReader = new ReusableFileReader("/proc/self/status"); + private readonly ReusableFileReader cgroupMemoryLimitReader = new ReusableFileReader(cgroupMemoryLimitFileName); + private readonly ReusableFileReader cgroupCpuCfsQuotaReader = new ReusableFileReader(cgroupCpuCfsQuotaFileName); + private readonly ReusableFileReader cgroupCpuCfsPeriodReader = new ReusableFileReader(cgroupCpuCfsPeriodFileName); + private readonly LegacyCpuUtilizationCollector cpuCollector = new LegacyCpuUtilizationCollector(); + + public void Dispose() + { + systemStatReader.Dispose(); + processStatReader.Dispose(); + processStatusReader.Dispose(); + } + + public void Collect(CurrentProcessMetrics metrics) + { + var systemStat = ReadSystemStat(); + var processStat = ReadProcessStat(); + var processStatus = ReadProcessStatus(); + var cgroupStatus = ReadCgroupStatus(); + + if (processStatus.FileDescriptorsCount.HasValue) + metrics.HandlesCount = processStatus.FileDescriptorsCount.Value; + + if (processStatus.VirtualMemoryResident.HasValue) + metrics.MemoryResident = processStatus.VirtualMemoryResident.Value; + + if (processStatus.VirtualMemoryData.HasValue) + metrics.MemoryPrivate = processStatus.VirtualMemoryData.Value; + + if (systemStat.Filled && processStat.Filled) + { + var systemTime = systemStat.SystemTime.Value + systemStat.UserTime.Value + systemStat.IdleTime.Value; + var processTime = processStat.SystemTime.Value + processStat.UserTime.Value; + + cpuCollector.Collect(metrics, systemTime, processTime, systemStat.CpuCount); + } + + metrics.CgroupCpuLimitCores = cgroupStatus.CpuLimit; + metrics.CgroupMemoryLimit = cgroupStatus.MemoryLimit; + } + + private SystemStat ReadSystemStat() + { + var result = new SystemStat(); + + try + { + if (FileParser.TrySplitLine(systemStatReader.ReadFirstLine(), 5, out var parts) && parts[0] == "cpu") + { + if (ulong.TryParse(parts[1], out var utime)) + result.UserTime = utime; + + if (ulong.TryParse(parts[3], out var stime)) + result.SystemTime = stime; + + if (ulong.TryParse(parts[4], out var itime)) + result.IdleTime = itime; + } + + result.CpuCount = systemStatReader.ReadLines().Count(line => line.StartsWith("cpu")) - 1; + } + catch (Exception error) + { + InternalErrorLogger.Warn(error); + } + + return result; + } + + private ProcessStat ReadProcessStat() + { + var result = new ProcessStat(); + + try + { + if (FileParser.TrySplitLine(processStatReader.ReadFirstLine(), 15, out var parts)) + { + if (ulong.TryParse(parts[13], out var utime)) + result.UserTime = utime; + + if (ulong.TryParse(parts[14], out var stime)) + result.SystemTime = stime; + } + } + catch (Exception error) + { + InternalErrorLogger.Warn(error); + } + + return result; + } + + private ProcessStatus ReadProcessStatus() + { + var result = new ProcessStatus(); + + try + { + result.FileDescriptorsCount = Directory.EnumerateFiles("/proc/self/fd/").Count(); + } + catch (DirectoryNotFoundException) + { + // NOTE: Ignored due to process already exited so we don't have to count it's file descriptors count. + result.FileDescriptorsCount = 0; + } + + try + { + foreach (var line in processStatusReader.ReadLines()) + { + if (FileParser.TryParseLong(line, "RssAnon", out var vmRss)) + result.VirtualMemoryResident = vmRss * 1024L; + + if (FileParser.TryParseLong(line, "VmData", out var vmData)) + result.VirtualMemoryData = vmData * 1024L; + + if (result.Filled) + break; + } + } + catch (Exception error) + { + InternalErrorLogger.Warn(error); + } + + return result; + } + + // See https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu + // and https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-memory + // for details + private ProcessCgroupStatus ReadCgroupStatus() + { + var result = new ProcessCgroupStatus(); + + if (cgroupMemoryLimitReader.TryReadFirstLine(out var memoryLimitLine) + && memoryLimitLine != cgroupNoMemoryLimitValue + && long.TryParse(memoryLimitLine, out var memoryLimit)) + { + result.MemoryLimit = memoryLimit; + } + + if (cgroupCpuCfsPeriodReader.TryReadFirstLine(out var cpuPeriodLine) + && cgroupCpuCfsQuotaReader.TryReadFirstLine(out var cpuQuotaLine) + && cpuQuotaLine != cgroupNoCpuLimitValue + && long.TryParse(cpuPeriodLine, out var cpuPeriod) + && long.TryParse(cpuQuotaLine, out var cpuQuota)) + { + result.CpuLimit = (double)cpuQuota / cpuPeriod; + } + + return result; + } + + private class SystemStat + { + public bool Filled => IdleTime.HasValue && UserTime.HasValue && SystemTime.HasValue; + + public int? CpuCount { get; set; } + public ulong? IdleTime { get; set; } + public ulong? UserTime { get; set; } + public ulong? SystemTime { get; set; } + } + + private class ProcessStat + { + public bool Filled => UserTime.HasValue && SystemTime.HasValue; + + public ulong? UserTime { get; set; } + public ulong? SystemTime { get; set; } + } + + private class ProcessStatus + { + public bool Filled => FileDescriptorsCount.HasValue && VirtualMemoryResident.HasValue && VirtualMemoryData.HasValue; + + public int? FileDescriptorsCount { get; set; } + public long? VirtualMemoryResident { get; set; } + public long? VirtualMemoryData { get; set; } + } + + private class ProcessCgroupStatus + { + public double? CpuLimit { get; set; } + public long? MemoryLimit { get; set; } + } + } +} \ No newline at end of file diff --git a/Vostok.Metrics.System/Process/NativeProcessMetricsCollector_Linux.cs b/Vostok.Metrics.System/Process/NativeProcessMetricsCollector_Linux.cs index 20db517..f9abae9 100644 --- a/Vostok.Metrics.System/Process/NativeProcessMetricsCollector_Linux.cs +++ b/Vostok.Metrics.System/Process/NativeProcessMetricsCollector_Linux.cs @@ -8,7 +8,7 @@ namespace Vostok.Metrics.System.Process { - internal class NativeProcessMetricsCollector_Linux : IDisposable + internal class NativeProcessMetricsCollector_Linux : INativeProcessMetricsCollector_Linux { private const string cgroupMemoryLimitFileName = "/sys/fs/cgroup/memory/memory.limit_in_bytes"; private const string cgroupCpuCfsQuotaFileName = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"; diff --git a/Vostok.Metrics.System/PublicAPI.Unshipped.txt b/Vostok.Metrics.System/PublicAPI.Unshipped.txt index c274f2a..0e33386 100644 --- a/Vostok.Metrics.System/PublicAPI.Unshipped.txt +++ b/Vostok.Metrics.System/PublicAPI.Unshipped.txt @@ -1,7 +1,9 @@ +const Vostok.Metrics.System.Helpers.VostokSystemMetricsConstants.UseLegacyMetricsCollectorEnvironmentVariable = "VOSTOK_METRICS_SYSTEM_LINUX_USE_LEGACY_COLLECTOR" -> string static Vostok.Metrics.System.Host.HostMetricsExtensions.LogMetrics(this Vostok.Metrics.System.Host.HostMetrics metrics, Vostok.Logging.Abstractions.ILog log, System.TimeSpan period) -> void static Vostok.Metrics.System.Host.HostMetricsExtensions.ToDataPoints(this Vostok.Metrics.System.Host.HostMetrics metrics, System.DateTimeOffset? timestamp = null) -> System.Collections.Generic.IEnumerable static Vostok.Metrics.System.Process.CurrentProcessMetricsExtensions.LogMetrics(this Vostok.Metrics.System.Process.CurrentProcessMetrics metrics, Vostok.Logging.Abstractions.ILog log, System.TimeSpan period) -> void static Vostok.Metrics.System.Process.CurrentProcessMetricsExtensions.ToDataPoints(this Vostok.Metrics.System.Process.CurrentProcessMetrics metrics, System.DateTimeOffset? timestamp = null) -> System.Collections.Generic.IEnumerable +Vostok.Metrics.System.Helpers.VostokSystemMetricsConstants Vostok.Metrics.System.Host.HostMetricsExtensions Vostok.Metrics.System.Process.CurrentProcessMetricsExtensions Vostok.Metrics.System.Process.CurrentProcessMetricsSettings.LinuxSettings.get -> Vostok.Metrics.System.Process.LinuxProcessMetricsSettings