diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..6526176c304 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/CompatibilitySuppressions.xml @@ -0,0 +1,46 @@ + + + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_EnableDiskIoMetrics + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_EnableDiskIoMetrics(System.Boolean) + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net462/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_EnableDiskIoMetrics + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_EnableDiskIoMetrics(System.Boolean) + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net8.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.get_EnableDiskIoMetrics + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + + CP0002 + M:Microsoft.Extensions.Diagnostics.ResourceMonitoring.ResourceMonitoringOptions.set_EnableDiskIoMetrics(System.Boolean) + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + lib/net9.0/Microsoft.Extensions.Diagnostics.ResourceMonitoring.dll + true + + \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/DiskStats.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/DiskStats.cs new file mode 100644 index 00000000000..5b1315e7a50 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/DiskStats.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk; + +/// +/// Represents one line of statistics from "/proc/diskstats" +/// See https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats for details. +/// +internal sealed class DiskStats +{ + public int MajorNumber { get; set; } + public int MinorNumber { get; set; } + public string DeviceName { get; set; } = string.Empty; + public ulong ReadsCompleted { get; set; } + public ulong ReadsMerged { get; set; } + public ulong SectorsRead { get; set; } + public uint TimeReadingMs { get; set; } + public ulong WritesCompleted { get; set; } + public ulong WritesMerged { get; set; } + public ulong SectorsWritten { get; set; } + public uint TimeWritingMs { get; set; } + public uint IoInProgress { get; set; } + public uint TimeIoMs { get; set; } + public uint WeightedTimeIoMs { get; set; } + + // The following fields are available starting from kernel 4.18; if absent, remain 0 + public ulong DiscardsCompleted { get; set; } + public ulong DiscardsMerged { get; set; } + public ulong SectorsDiscarded { get; set; } + public uint TimeDiscardingMs { get; set; } + + // The following fields are available starting from kernel 5.5; if absent, remain 0 + public ulong FlushRequestsCompleted { get; set; } + public uint TimeFlushingMs { get; set; } +} diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/DiskStatsReader.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/DiskStatsReader.cs new file mode 100644 index 00000000000..11a6f72ccc8 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/DiskStatsReader.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using Microsoft.Extensions.ObjectPool; +using Microsoft.Shared.Pools; + +namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk; + +/// +/// Handles reading and parsing of Linux procfs-diskstats file(/proc/diskstats). +/// +internal sealed class DiskStatsReader(IFileSystem fileSystem) : IDiskStatsReader +{ + private static readonly FileInfo _diskStatsFile = new("/proc/diskstats"); + private static readonly ObjectPool> _sharedBufferWriterPool = BufferWriterPool.CreateBufferWriterPool(); + + /// + /// Reads and returns all disk statistics entries. + /// + /// List of . + public List ReadAll() + { + var diskStatsList = new List(); + + using ReturnableBufferWriter bufferWriter = new(_sharedBufferWriterPool); + using IEnumerator> enumerableLines = fileSystem.ReadAllByLines(_diskStatsFile, bufferWriter.Buffer).GetEnumerator(); + + while (enumerableLines.MoveNext()) + { + string line = enumerableLines.Current.Trim().ToString(); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + try + { + DiskStats stat = DiskStatsReader.ParseLine(line); + diskStatsList.Add(stat); + } +#pragma warning disable CA1031 + catch (Exception) +#pragma warning restore CA1031 + { + // ignore parsing errors + } + } + + return diskStatsList; + } + + /// + /// Parses one line of text into a DiskStats object. + /// + /// one line in "/proc/diskstats". + /// parsed DiskStats object. + [SuppressMessage("Major Code Smell", "S109:Magic numbers should not be used", Justification = "These numbers represent fixed field indices in the Linux /proc/diskstats format")] + private static DiskStats ParseLine(string line) + { + // Split by any whitespace and remove empty entries +#pragma warning disable EA0009 + string[] parts = line.Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); +#pragma warning restore EA0009 + + if (parts.Length < 14) + { + throw new FormatException($"Not enough fields: expected at least 14, got {parts.Length}"); + } + + // See https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats + var diskStats = new DiskStats + { + MajorNumber = int.Parse(parts[0], CultureInfo.InvariantCulture), + MinorNumber = int.Parse(parts[1], CultureInfo.InvariantCulture), + DeviceName = parts[2], + ReadsCompleted = ulong.Parse(parts[3], CultureInfo.InvariantCulture), + ReadsMerged = ulong.Parse(parts[4], CultureInfo.InvariantCulture), + SectorsRead = ulong.Parse(parts[5], CultureInfo.InvariantCulture), + TimeReadingMs = uint.Parse(parts[6], CultureInfo.InvariantCulture), + WritesCompleted = ulong.Parse(parts[7], CultureInfo.InvariantCulture), + WritesMerged = ulong.Parse(parts[8], CultureInfo.InvariantCulture), + SectorsWritten = ulong.Parse(parts[9], CultureInfo.InvariantCulture), + TimeWritingMs = uint.Parse(parts[10], CultureInfo.InvariantCulture), + IoInProgress = uint.Parse(parts[11], CultureInfo.InvariantCulture), + TimeIoMs = uint.Parse(parts[12], CultureInfo.InvariantCulture), + WeightedTimeIoMs = uint.Parse(parts[13], CultureInfo.InvariantCulture) + }; + + // Parse additional fields if present + if (parts.Length >= 18) + { + diskStats.DiscardsCompleted = ulong.Parse(parts[14], CultureInfo.InvariantCulture); + diskStats.DiscardsMerged = ulong.Parse(parts[15], CultureInfo.InvariantCulture); + diskStats.SectorsDiscarded = ulong.Parse(parts[16], CultureInfo.InvariantCulture); + diskStats.TimeDiscardingMs = uint.Parse(parts[17], CultureInfo.InvariantCulture); + } + + if (parts.Length >= 20) + { + diskStats.FlushRequestsCompleted = ulong.Parse(parts[18], CultureInfo.InvariantCulture); + diskStats.TimeFlushingMs = uint.Parse(parts[19], CultureInfo.InvariantCulture); + } + + return diskStats; + } +} diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/IDiskStatsReader.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/IDiskStatsReader.cs new file mode 100644 index 00000000000..df9d0d7c020 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/IDiskStatsReader.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk; + +/// +/// An interface for reading disk statistics. +/// +internal interface IDiskStatsReader +{ + /// + /// Gets all the disk statistics from the system. + /// + /// List of instances. + List ReadAll(); +} diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/LinuxSystemDiskMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/LinuxSystemDiskMetrics.cs new file mode 100644 index 00000000000..d70a65ed1b0 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Disk/LinuxSystemDiskMetrics.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Metrics; +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Microsoft.Shared.Instruments; + +namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk; + +internal sealed class LinuxSystemDiskMetrics +{ + // The kernel's block layer always reports counts in 512-byte "sectors" regardless of the underlying device's real block size + // https://docs.kernel.org/block/stat.html#read-sectors-write-sectors-discard-sectors + private const int LinuxDiskSectorSize = 512; + private const int MinimumDiskStatsRefreshIntervalInSeconds = 10; + private const string DeviceKey = "system.device"; + private const string DirectionKey = "disk.io.direction"; + + private static readonly KeyValuePair _directionReadTag = new(DirectionKey, "read"); + private static readonly KeyValuePair _directionWriteTag = new(DirectionKey, "write"); + private readonly ILogger _logger; + private readonly TimeProvider _timeProvider; + private readonly IDiskStatsReader _diskStatsReader; + private readonly object _lock = new(); + private readonly Dictionary _baselineDiskStatsDict = []; + private List _diskStatsSnapshot = []; + private DateTimeOffset _lastRefreshTime = DateTimeOffset.MinValue; + + public LinuxSystemDiskMetrics( + ILogger? logger, + IMeterFactory meterFactory, + IOptions options, + TimeProvider timeProvider, + IDiskStatsReader diskStatsReader) + { + _logger = logger ?? NullLogger.Instance; + _timeProvider = timeProvider; + _diskStatsReader = diskStatsReader; + if (!options.Value.EnableSystemDiskIoMetrics) + { + return; + } + + // We need to read the disk stats once to get the baseline values + _baselineDiskStatsDict = GetAllDiskStats().ToDictionary(d => d.DeviceName); + +#pragma warning disable CA2000 // Dispose objects before losing scope + // We don't dispose the meter because IMeterFactory handles that + // It's a false-positive, see: https://github.com/dotnet/roslyn-analyzers/issues/6912. + // Related documentation: https://github.com/dotnet/docs/pull/37170 + Meter meter = meterFactory.Create(ResourceUtilizationInstruments.MeterName); +#pragma warning restore CA2000 // Dispose objects before losing scope + + // The metric is aligned with + // https://opentelemetry.io/docs/specs/semconv/system/system-metrics/#metric-systemdiskio + _ = meter.CreateObservableCounter( + ResourceUtilizationInstruments.SystemDiskIo, + GetDiskIoMeasurements, + unit: "By", + description: "Disk bytes transferred"); + + // The metric is aligned with + // https://opentelemetry.io/docs/specs/semconv/system/system-metrics/#metric-systemdiskoperations + _ = meter.CreateObservableCounter( + ResourceUtilizationInstruments.SystemDiskOperations, + GetDiskOperationMeasurements, + unit: "{operation}", + description: "Disk operations"); + + // The metric is aligned with + // https://opentelemetry.io/docs/specs/semconv/system/system-metrics/#metric-systemdiskio_time + _ = meter.CreateObservableCounter( + ResourceUtilizationInstruments.SystemDiskIoTime, + GetDiskIoTimeMeasurements, + unit: "s", + description: "Time disk spent activated"); + } + + private IEnumerable> GetDiskIoMeasurements() + { + List> measurements = []; + List diskStatsSnapshot = GetDiskStatsSnapshot(); + + foreach (DiskStats diskStats in diskStatsSnapshot) + { + _ = _baselineDiskStatsDict.TryGetValue(diskStats.DeviceName, out DiskStats? baselineDiskStats); + long readBytes = (long)(diskStats.SectorsRead - baselineDiskStats?.SectorsRead ?? 0L) * LinuxDiskSectorSize; + long writeBytes = (long)(diskStats.SectorsWritten - baselineDiskStats?.SectorsWritten ?? 0L) * LinuxDiskSectorSize; + measurements.Add(new Measurement(readBytes, new TagList { _directionReadTag, new(DeviceKey, diskStats.DeviceName) })); + measurements.Add(new Measurement(writeBytes, new TagList { _directionWriteTag, new(DeviceKey, diskStats.DeviceName) })); + } + + return measurements; + } + + private IEnumerable> GetDiskOperationMeasurements() + { + List> measurements = []; + List diskStatsSnapshot = GetDiskStatsSnapshot(); + + foreach (DiskStats diskStats in diskStatsSnapshot) + { + _ = _baselineDiskStatsDict.TryGetValue(diskStats.DeviceName, out DiskStats? baselineDiskStats); + long readCount = (long)(diskStats.ReadsCompleted - baselineDiskStats?.ReadsCompleted ?? 0L); + long writeCount = (long)(diskStats.WritesCompleted - baselineDiskStats?.WritesCompleted ?? 0L); + measurements.Add(new Measurement(readCount, new TagList { _directionReadTag, new(DeviceKey, diskStats.DeviceName) })); + measurements.Add(new Measurement(writeCount, new TagList { _directionWriteTag, new(DeviceKey, diskStats.DeviceName) })); + } + + return measurements; + } + + private IEnumerable> GetDiskIoTimeMeasurements() + { + List> measurements = []; + List diskStatsSnapshot = GetDiskStatsSnapshot(); + + foreach (DiskStats diskStats in diskStatsSnapshot) + { + _ = _baselineDiskStatsDict.TryGetValue(diskStats.DeviceName, out DiskStats? baselineDiskStats); + double ioTimeSeconds = (diskStats.TimeIoMs - baselineDiskStats?.TimeIoMs ?? 0) / 1000.0; // Convert to seconds + measurements.Add(new Measurement(ioTimeSeconds, new TagList { new(DeviceKey, diskStats.DeviceName) })); + } + + return measurements; + } + + private List GetDiskStatsSnapshot() + { + lock (_lock) + { + DateTimeOffset now = _timeProvider.GetUtcNow(); + if (_diskStatsSnapshot.Count == 0 || (now - _lastRefreshTime).TotalSeconds > MinimumDiskStatsRefreshIntervalInSeconds) + { + _diskStatsSnapshot = GetAllDiskStats(); + _lastRefreshTime = now; + } + } + + return _diskStatsSnapshot; + } + + private List GetAllDiskStats() + { + try + { + List diskStatsList = _diskStatsReader.ReadAll(); + + // We should not include ram, loop, or dm(device-mapper) devices in the disk stats, should we? + diskStatsList = diskStatsList + .Where(d => !d.DeviceName.StartsWith("ram", StringComparison.OrdinalIgnoreCase) + && !d.DeviceName.StartsWith("loop", StringComparison.OrdinalIgnoreCase) + && !d.DeviceName.StartsWith("dm-", StringComparison.OrdinalIgnoreCase)) + .ToList(); + return diskStatsList; + } +#pragma warning disable CA1031 + catch (Exception ex) +#pragma warning restore CA1031 + { + Log.HandleDiskStatsException(_logger, ex.Message); + } + + return []; + } +} diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs index 918087b1b78..d2f9c8f5070 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Linux/Log.cs @@ -56,4 +56,8 @@ public static partial void CounterMessage100( public static partial void CounterMessage110( ILogger logger, long counterValue); + + [LoggerMessage(7, LogLevel.Warning, + "Error while getting disk stats: Error={errorMessage}")] + public static partial void HandleDiskStatsException(ILogger logger, string errorMessage); } diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.Windows.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.Windows.cs index f042d892ab1..9e8636506c7 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.Windows.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.Windows.cs @@ -10,12 +10,6 @@ namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring; public partial class ResourceMonitoringOptions { - /// - /// Gets or sets a value indicating whether disk I/O metrics should be enabled. - /// - [Experimental(diagnosticId: DiagnosticIds.Experiments.ResourceMonitoring, UrlFormat = DiagnosticIds.UrlFormat)] - public bool EnableDiskIoMetrics { get; set; } - /// /// Gets or sets the list of source IPv4 addresses to track the connections for in telemetry. /// diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs index 3946add711d..420d6001f57 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringOptions.cs @@ -117,4 +117,11 @@ public partial class ResourceMonitoringOptions /// [Experimental(diagnosticId: DiagnosticIds.Experiments.ResourceMonitoring, UrlFormat = DiagnosticIds.UrlFormat)] public bool UseDeltaNrPeriodsForCpuCalculation { get; set; } + + /// + /// Gets or sets a value indicating whether disk I/O metrics should be enabled. + /// + /// Previously EnableDiskIoMetrics. + [Experimental(diagnosticId: DiagnosticIds.Experiments.ResourceMonitoring, UrlFormat = DiagnosticIds.UrlFormat)] + public bool EnableSystemDiskIoMetrics { get; set; } } diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs index f018038c614..541984db78d 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/ResourceMonitoringServiceCollectionExtensions.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Diagnostics.ResourceMonitoring; #if !NETFRAMEWORK using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux; +using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk; using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Network; #endif @@ -129,6 +130,7 @@ private static ResourceMonitorBuilder AddLinuxProvider(this ResourceMonitorBuild builder.Services.TryAddActivatedSingleton(); + builder.Services.TryAddSingleton(TimeProvider.System); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.PickLinuxParser(); @@ -136,7 +138,9 @@ private static ResourceMonitorBuilder AddLinuxProvider(this ResourceMonitorBuild _ = builder.Services .AddActivatedSingleton() .AddActivatedSingleton() - .AddActivatedSingleton(); + .AddActivatedSingleton() + .AddActivatedSingleton() + .AddActivatedSingleton(); return builder; } diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Disk/WindowsDiskMetrics.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Disk/WindowsDiskMetrics.cs index 47706b3ce6a..2927fa657e3 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Disk/WindowsDiskMetrics.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring/Windows/Disk/WindowsDiskMetrics.cs @@ -34,7 +34,7 @@ public WindowsDiskMetrics( IOptions options) { _logger = logger ?? NullLogger.Instance; - if (!options.Value.EnableDiskIoMetrics) + if (!options.Value.EnableSystemDiskIoMetrics) { return; } @@ -73,6 +73,7 @@ public WindowsDiskMetrics( description: "Time disk spent activated"); } +#pragma warning disable CA1031 // Do not catch general exception types private void InitializeDiskCounters(IPerformanceCounterFactory performanceCounterFactory, TimeProvider timeProvider) { const string DiskCategoryName = "LogicalDisk"; @@ -96,9 +97,7 @@ private void InitializeDiskCounters(IPerformanceCounterFactory performanceCounte ioTimePerfCounter.InitializeDiskCounters(); _diskIoTimePerfCounter = ioTimePerfCounter; } -#pragma warning disable CA1031 catch (Exception ex) -#pragma warning restore CA1031 { Log.DiskIoPerfCounterException(_logger, WindowsDiskPerfCounterNames.DiskIdleTimeCounter, ex.Message); } @@ -124,14 +123,13 @@ private void InitializeDiskCounters(IPerformanceCounterFactory performanceCounte ratePerfCounter.InitializeDiskCounters(); _diskIoRateCounters.Add(counterName, ratePerfCounter); } -#pragma warning disable CA1031 catch (Exception ex) -#pragma warning restore CA1031 { Log.DiskIoPerfCounterException(_logger, counterName, ex.Message); } } } +#pragma warning restore CA1031 // Do not catch general exception types private IEnumerable> GetDiskIoMeasurements() { diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/DiskStatsReaderTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/DiskStatsReaderTests.cs new file mode 100644 index 00000000000..c5098b2d284 --- /dev/null +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/DiskStatsReaderTests.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Test; +using Microsoft.TestUtilities; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk.Test; + +[OSSkipCondition(OperatingSystems.Windows | OperatingSystems.MacOSX, SkipReason = "Linux specific tests")] +public class DiskStatsReaderTests +{ + [Fact] + public void Test_ReadAll_Valid_DiskStats() + { + string diskStatsFileContent = + " 7 0 loop0 269334 0 12751202 147117 11604772 0 97447664 1402945 0 12193892 2255752 0 0 0 0 1206808 705690\n" + + " 7 1 loop1 965348 0 28605866 474103 73636257 0 1211288288 14086242 0 60580032 24777643 0 0 0 0 18723136 10217297\n" + + " 7 2 loop2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + + " 259 1 nvme1n1 4180498 5551 247430002 746099 96474435 12677267 2160066791 23514624 0 68786140 29777259 0 0 0 0 22111407 5516535\n" + + " 259 2 nvme1n1p1 4180387 5551 247422458 746080 96474435 12677267 2160066791 23514624 0 68786108 24260705 0 0 0 0 0 0\n" + + " 259 0 nvme0n1 6090587 689465 1120208521 1810566 19069165 8947684 406356430 3897150 0 38134844 6246643 69106 0 271818368 23139 1659742 515787\n" + + " 259 3 nvme0n1p1 378 0 26406 96 0 0 0 0 0 760 96 0 0 0 0 0 0\n" + + " 259 4 nvme0n1p2 7301 26408 116617 3628 600 47 59970 98 0 1196 3767 48 0 33106424 40 0 0\n" + + " 259 5 nvme0n1p3 6079544 663057 1119819306 1806337 19068535 8947637 406296460 3897045 0 38130316 5726482 69058 0 238711944 23098 0 0\n" + + " 252 0 dm-0 1303410 0 10434296 166616 1812455 0 14879824 1213588 0 397256 1380204 0 0 0 0 0 0\n" + + " 252 1 dm-1 712122 0 38299466 140852 18159197 0 286348832 1552768 0 14182384 1716692 69058 0 238711944 23072 0 0\n" + + " 252 5 dm-5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + + " 252 7 dm-7 6828 0 360325 2100 14438 0 1149672 1508 0 7524 3608 0 0 0 0 0 0\n" + + " 8 0 sda 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + + " 252 8 dm-8 100601 0 2990980 23940 3097278 0 32037680 1410540 0 5488608 1434496 513 0 67108872 16 0 0\n"; + + var fileSystem = new HardcodedValueFileSystem(new Dictionary + { + { new FileInfo("/proc/diskstats"), diskStatsFileContent } + }); + + var reader = new DiskStatsReader(fileSystem); + var dictionary = reader.ReadAll().ToDictionary(x => x.DeviceName); + Assert.Equal(15, dictionary.Count); + + var disk1 = dictionary["nvme0n1"]; + Assert.Equal(6_090_587u, disk1.ReadsCompleted); + Assert.Equal(689_465u, disk1.ReadsMerged); + Assert.Equal(1_120_208_521u, disk1.SectorsRead); + Assert.Equal(1_810_566u, disk1.TimeReadingMs); + Assert.Equal(19_069_165u, disk1.WritesCompleted); + Assert.Equal(8_947_684u, disk1.WritesMerged); + Assert.Equal(406_356_430u, disk1.SectorsWritten); + Assert.Equal(3_897_150u, disk1.TimeWritingMs); + Assert.Equal(0u, disk1.IoInProgress); + Assert.Equal(38_134_844u, disk1.TimeIoMs); + Assert.Equal(6_246_643u, disk1.WeightedTimeIoMs); + Assert.Equal(69_106u, disk1.DiscardsCompleted); + Assert.Equal(0u, disk1.DiscardsMerged); + Assert.Equal(271_818_368u, disk1.SectorsDiscarded); + Assert.Equal(23_139u, disk1.TimeDiscardingMs); + Assert.Equal(1_659_742u, disk1.FlushRequestsCompleted); + Assert.Equal(515_787u, disk1.TimeFlushingMs); + + var disk2 = dictionary["dm-8"]; + Assert.Equal(100_601u, disk2.ReadsCompleted); + Assert.Equal(0u, disk2.ReadsMerged); + Assert.Equal(2_990_980u, disk2.SectorsRead); + Assert.Equal(23_940u, disk2.TimeReadingMs); + Assert.Equal(3_097_278u, disk2.WritesCompleted); + Assert.Equal(0u, disk2.WritesMerged); + Assert.Equal(32_037_680u, disk2.SectorsWritten); + Assert.Equal(1_410_540u, disk2.TimeWritingMs); + Assert.Equal(0u, disk2.IoInProgress); + Assert.Equal(5_488_608u, disk2.TimeIoMs); + Assert.Equal(1_434_496u, disk2.WeightedTimeIoMs); + + var disk3 = dictionary["sda"]; + Assert.Equal(0u, disk3.ReadsCompleted); + Assert.Equal(0u, disk3.ReadsMerged); + Assert.Equal(0u, disk3.SectorsRead); + Assert.Equal(0u, disk3.TimeReadingMs); + Assert.Equal(0u, disk3.WritesCompleted); + Assert.Equal(0u, disk3.WritesMerged); + Assert.Equal(0u, disk3.SectorsWritten); + Assert.Equal(0u, disk3.TimeWritingMs); + Assert.Equal(0u, disk3.IoInProgress); + Assert.Equal(0u, disk3.TimeIoMs); + Assert.Equal(0u, disk3.WeightedTimeIoMs); + } + + [Fact] + public void Test_ReadAll_With_Invalid_Lines() + { + string diskStatsFileContent = + " 259 1 nvme1n1 4180498 5551 247430002 746099 96474435 12677267 2160066791 23514624 0 68786140 29777259 0 0 0 0 22111407 5516535\n" + + " 259 2 nvme1n1p1 4180387 5551 247422458\n" + + " 259 2 nvme1n1p1 4180387 5551 247422458 746080 96474435 12677267 2160066791 23514624 0 68786108 24260705 0 0 0 0 0 0\n" + + " 259 0 nvme0n1 6090587 689465 1120208521 1810566 19069165 8947684 406356430 3897150 0 38134844 6246643 69106 0 271818368 23139 1659742 515787\n" + + " 259 nvme0n1p1 378 0 26406 96 0 0 0 0 0 760 96 0 0 0 0 0 0\n"; + + var fileSystem = new HardcodedValueFileSystem(new Dictionary + { + { new FileInfo("/proc/diskstats"), diskStatsFileContent } + }); + + var reader = new DiskStatsReader(fileSystem); + var dictionary = reader.ReadAll().ToDictionary(x => x.DeviceName); + Assert.Equal(3, dictionary.Count); + + var disk1 = dictionary["nvme1n1"]; + Assert.Equal(4_180_498u, disk1.ReadsCompleted); + Assert.Equal(5_551u, disk1.ReadsMerged); + Assert.Equal(247_430_002u, disk1.SectorsRead); + Assert.Equal(746_099u, disk1.TimeReadingMs); + Assert.Equal(96_474_435u, disk1.WritesCompleted); + Assert.Equal(12_677_267u, disk1.WritesMerged); + Assert.Equal(2_160_066_791u, disk1.SectorsWritten); + Assert.Equal(23_514_624u, disk1.TimeWritingMs); + Assert.Equal(0u, disk1.IoInProgress); + Assert.Equal(68_786_140u, disk1.TimeIoMs); + Assert.Equal(29_777_259u, disk1.WeightedTimeIoMs); + Assert.Equal(0u, disk1.DiscardsCompleted); + Assert.Equal(0u, disk1.DiscardsMerged); + Assert.Equal(0u, disk1.SectorsDiscarded); + Assert.Equal(0u, disk1.TimeDiscardingMs); + Assert.Equal(22_111_407u, disk1.FlushRequestsCompleted); + Assert.Equal(5_516_535u, disk1.TimeFlushingMs); + + var disk2 = dictionary["nvme1n1p1"]; + Assert.NotNull(disk2); + + var disk3 = dictionary["nvme0n1"]; + Assert.NotNull(disk3); + } +} diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/FakeDiskStatsReader.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/FakeDiskStatsReader.cs new file mode 100644 index 00000000000..1c69be74709 --- /dev/null +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/FakeDiskStatsReader.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk.Test; + +internal class FakeDiskStatsReader(Dictionary> stats) : IDiskStatsReader +{ + private int _index; + + public List ReadAll() + { + if (_index >= stats.Values.First().Count) + { + throw new InvalidOperationException("No more values available."); + } + + List list = stats.Values.Select(x => x[_index]).ToList(); + _index++; + return list; + } +} diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/LinuxSystemDiskMetricsTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/LinuxSystemDiskMetricsTests.cs new file mode 100644 index 00000000000..c6aba8e0b53 --- /dev/null +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Disk/LinuxSystemDiskMetricsTests.cs @@ -0,0 +1,216 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using Microsoft.Extensions.Diagnostics.Metrics.Testing; +using Microsoft.Extensions.Diagnostics.ResourceMonitoring.Test.Helpers; +using Microsoft.Extensions.Logging.Testing; +using Microsoft.Extensions.Time.Testing; +using Microsoft.Shared.Instruments; +using Microsoft.TestUtilities; +using Moq; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.ResourceMonitoring.Linux.Disk.Test; + +[OSSkipCondition(OperatingSystems.Windows | OperatingSystems.MacOSX, SkipReason = "Linux specific tests")] +public class LinuxSystemDiskMetricsTests +{ + private readonly FakeLogger _fakeLogger = new(); + + [Fact] + public void Creates_Meter_With_Correct_Name() + { + using var meterFactory = new TestMeterFactory(); + var diskStatsReaderMock = new Mock(); + var options = new ResourceMonitoringOptions { EnableSystemDiskIoMetrics = true }; + + _ = new LinuxSystemDiskMetrics( + _fakeLogger, + meterFactory, + Options.Options.Create(options), + TimeProvider.System, + diskStatsReaderMock.Object); + + Meter meter = meterFactory.Meters.Single(); + Assert.Equal(ResourceUtilizationInstruments.MeterName, meter.Name); + } + + [Fact] + public void Test_MetricValues() + { + using var meterFactory = new TestMeterFactory(); + var fakeTimeProvider = new FakeTimeProvider(); + var options = new ResourceMonitoringOptions { EnableSystemDiskIoMetrics = true }; + + // Set up + var diskStatsReader = new FakeDiskStatsReader(new Dictionary> + { + { + "sda", [ + new DiskStats + { + DeviceName = "sda", + SectorsRead = 0, + SectorsWritten = 0, + ReadsCompleted = 0, + WritesCompleted = 0, + TimeIoMs = 0 + }, + new DiskStats + { + DeviceName = "sda", + SectorsRead = 500, + SectorsWritten = 1000, + ReadsCompleted = 600, + WritesCompleted = 1200, + TimeIoMs = 1234 + }, + new DiskStats + { + DeviceName = "sda", + SectorsRead = 700, + SectorsWritten = 1100, + ReadsCompleted = 800, + WritesCompleted = 1300, + TimeIoMs = 2234 + }, + new DiskStats + { + DeviceName = "sda", + SectorsRead = 1000, + SectorsWritten = 1600, + ReadsCompleted = 1300, + WritesCompleted = 1350, + TimeIoMs = 4444 + } + ] + }, + { + "sdb", [ + new DiskStats + { + DeviceName = "sdb", + SectorsRead = 200, + SectorsWritten = 300, + ReadsCompleted = 400, + WritesCompleted = 500, + TimeIoMs = 6000 + }, + new DiskStats + { + DeviceName = "sdb", + SectorsRead = 350, + SectorsWritten = 450, + ReadsCompleted = 550, + WritesCompleted = 650, + TimeIoMs = 7500 + }, + new DiskStats + { + DeviceName = "sdb", + SectorsRead = 400, + SectorsWritten = 500, + ReadsCompleted = 600, + WritesCompleted = 700, + TimeIoMs = 7500 + }, + new DiskStats + { + DeviceName = "sdb", + SectorsRead = 550, + SectorsWritten = 650, + ReadsCompleted = 750, + WritesCompleted = 850, + TimeIoMs = 9500 + } + ] + }, + }); + + _ = new LinuxSystemDiskMetrics( + _fakeLogger, + meterFactory, + Options.Options.Create(options), + fakeTimeProvider, + diskStatsReader); + Meter meter = meterFactory.Meters.Single(); + + var readTag = new KeyValuePair("disk.io.direction", "read"); + var writeTag = new KeyValuePair("disk.io.direction", "write"); + var deviceTagSda = new KeyValuePair("system.device", "sda"); + var deviceTagSdb = new KeyValuePair("system.device", "sdb"); + + using var diskIoCollector = new MetricCollector(meter, ResourceUtilizationInstruments.SystemDiskIo); + using var operationCollector = new MetricCollector(meter, ResourceUtilizationInstruments.SystemDiskOperations); + using var ioTimeCollector = new MetricCollector(meter, ResourceUtilizationInstruments.SystemDiskIoTime); + + // 1st measurement + diskIoCollector.RecordObservableInstruments(); + operationCollector.RecordObservableInstruments(); + ioTimeCollector.RecordObservableInstruments(); + + // Assert the 1st measurement + var diskIoMeasurement = diskIoCollector.GetMeasurementSnapshot(); + Assert.Equal(4, diskIoMeasurement.Count); + Assert.Equal(256_000, diskIoMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSda)).Value); // (500 - 0) * 512 = 256000 + Assert.Equal(76_800, diskIoMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSdb)).Value); // (350 - 200) * 512 = 76800 + Assert.Equal(512_000, diskIoMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSda)).Value); // (1000 - 0) * 512 = 512000 + Assert.Equal(76_800, diskIoMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSdb)).Value); // (450 - 300) * 512 = 76800 + var operationMeasurement = operationCollector.GetMeasurementSnapshot(); + Assert.Equal(4, operationMeasurement.Count); + Assert.Equal(600, operationMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSda)).Value); // 600 - 0 = 600 + Assert.Equal(150, operationMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSdb)).Value); // 550 - 400 = 150 + Assert.Equal(1200, operationMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSda)).Value); // 1200 - 0 = 1200 + Assert.Equal(150, operationMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSdb)).Value); // 650 - 500 = 150 + var ioTimeMeasurement = ioTimeCollector.GetMeasurementSnapshot(); + Assert.Equal(2, ioTimeMeasurement.Count); + Assert.Equal(1.234, ioTimeMeasurement.Last(x => x.MatchesTags(deviceTagSda)).Value, 0.01); // (1234 - 0) / 1000 = 1.234 + Assert.Equal(1.5, ioTimeMeasurement.Last(x => x.MatchesTags(deviceTagSdb)).Value, 0.01); // (7500 - 6000) / 1000 = 6.0 + + // 2nd measurement + fakeTimeProvider.Advance(TimeSpan.FromMinutes(1)); + diskIoCollector.RecordObservableInstruments(); + operationCollector.RecordObservableInstruments(); + ioTimeCollector.RecordObservableInstruments(); + + // Assert the 2nd measurement + diskIoMeasurement = diskIoCollector.GetMeasurementSnapshot(); + Assert.Equal(358_400, diskIoMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSda)).Value); // (700 - 0) * 512 = 358400 + Assert.Equal(102_400, diskIoMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSdb)).Value); // (400 - 200) * 512 = 102400 + Assert.Equal(563_200, diskIoMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSda)).Value); // (1100 - 0) * 512 = 563200 + Assert.Equal(102_400, diskIoMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSdb)).Value); // (500 - 300) * 512 = 102400 + operationMeasurement = operationCollector.GetMeasurementSnapshot(); + Assert.Equal(800, operationMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSda)).Value); // 800 - 0 = 800 + Assert.Equal(200, operationMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSdb)).Value); // 600 - 400 = 200 + Assert.Equal(1300, operationMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSda)).Value); // 1300 - 0 = 1300 + Assert.Equal(200, operationMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSdb)).Value); // 700 - 500 = 200 + ioTimeMeasurement = ioTimeCollector.GetMeasurementSnapshot(); + Assert.Equal(2.234, ioTimeMeasurement.Last(x => x.MatchesTags(deviceTagSda)).Value, 0.01); // (2234 - 0) / 1000 = 2.234 + Assert.Equal(1.5, ioTimeMeasurement.Last(x => x.MatchesTags(deviceTagSdb)).Value, 0.01); // (7500 - 6000) / 1000 = 1.5 + + // 3rd measurement + fakeTimeProvider.Advance(TimeSpan.FromMinutes(1)); + diskIoCollector.RecordObservableInstruments(); + operationCollector.RecordObservableInstruments(); + ioTimeCollector.RecordObservableInstruments(); + + // Assert the 3rd measurement + diskIoMeasurement = diskIoCollector.GetMeasurementSnapshot(); + Assert.Equal(512_000, diskIoMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSda)).Value); // (1000 - 0) * 512 = 512000 + Assert.Equal(179_200, diskIoMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSdb)).Value); // (550 - 200) * 512 = 179200 + Assert.Equal(819_200, diskIoMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSda)).Value); // (1600 - 0) * 512 = 819200 + Assert.Equal(179_200, diskIoMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSdb)).Value); // (650 - 300) * 512 = 179200 + operationMeasurement = operationCollector.GetMeasurementSnapshot(); + Assert.Equal(1300, operationMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSda)).Value); // 1300 - 0 = 1300 + Assert.Equal(350, operationMeasurement.Last(x => x.MatchesTags(readTag, deviceTagSdb)).Value); // 750 - 400 = 350 + Assert.Equal(1350, operationMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSda)).Value); // 1350 - 0 = 1350 + Assert.Equal(350, operationMeasurement.Last(x => x.MatchesTags(writeTag, deviceTagSdb)).Value); // 850 - 500 = 350 + ioTimeMeasurement = ioTimeCollector.GetMeasurementSnapshot(); + Assert.Equal(4.444, ioTimeMeasurement.Last(x => x.MatchesTags(deviceTagSda)).Value, 0.01); // (4444 - 0) / 1000 = 4.444 + Assert.Equal(3.5, ioTimeMeasurement.Last(x => x.MatchesTags(deviceTagSdb)).Value, 0.01); // (9500 - 6000) / 1000 = 3.5 + } +} diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests.csproj b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests.csproj index b7fcf503d96..ff2cd26412f 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests.csproj +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests.csproj @@ -6,8 +6,7 @@ - - + diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Disk/WindowsDiskMetricsTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Disk/WindowsDiskMetricsTests.cs index 25587a6ad59..a592aacce19 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Disk/WindowsDiskMetricsTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Windows/Disk/WindowsDiskMetricsTests.cs @@ -30,7 +30,7 @@ public void Creates_Meter_With_Correct_Name() { using var meterFactory = new TestMeterFactory(); var performanceCounterFactoryMock = new Mock(); - var options = new ResourceMonitoringOptions { EnableDiskIoMetrics = true }; + var options = new ResourceMonitoringOptions { EnableSystemDiskIoMetrics = true }; _ = new WindowsDiskMetrics( _fakeLogger, @@ -49,7 +49,7 @@ public void DiskOperationMetricsTest() using var meterFactory = new TestMeterFactory(); var performanceCounterFactory = new Mock(); var fakeTimeProvider = new FakeTimeProvider(); - var options = new ResourceMonitoringOptions { EnableDiskIoMetrics = true }; + var options = new ResourceMonitoringOptions { EnableSystemDiskIoMetrics = true }; // Set up const string ReadCounterName = WindowsDiskPerfCounterNames.DiskReadsCounter; @@ -123,7 +123,7 @@ public void DiskIoBytesMetricsTest() using var meterFactory = new TestMeterFactory(); var performanceCounterFactory = new Mock(); var fakeTimeProvider = new FakeTimeProvider(); - var options = new ResourceMonitoringOptions { EnableDiskIoMetrics = true }; + var options = new ResourceMonitoringOptions { EnableSystemDiskIoMetrics = true }; // Set up const string ReadCounterName = WindowsDiskPerfCounterNames.DiskReadBytesCounter;