2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System ;
5
+ using System . Collections . Frozen ;
5
6
using System . Collections . Generic ;
6
7
using System . Diagnostics ;
7
8
using System . Diagnostics . Metrics ;
8
- using System . Linq ;
9
+ using System . IO ;
9
10
using Microsoft . Extensions . Logging ;
10
11
using Microsoft . Extensions . Logging . Abstractions ;
11
12
using Microsoft . Extensions . Options ;
@@ -22,14 +23,22 @@ internal sealed class LinuxSystemDiskMetrics
22
23
private const string DeviceKey = "system.device" ;
23
24
private const string DirectionKey = "disk.io.direction" ;
24
25
26
+ // Exclude devices with these prefixes because they represent virtual, loopback, or device-mapper disks
27
+ // that do not correspond to real physical storage. Including them would distort system disk I/O metrics.
28
+ private static readonly string [ ] _skipDevicePrefixes = new [ ] { "ram" , "loop" , "dm-" } ;
25
29
private static readonly KeyValuePair < string , object ? > _directionReadTag = new ( DirectionKey , "read" ) ;
26
30
private static readonly KeyValuePair < string , object ? > _directionWriteTag = new ( DirectionKey , "write" ) ;
27
31
private readonly ILogger < LinuxSystemDiskMetrics > _logger ;
28
32
private readonly TimeProvider _timeProvider ;
29
33
private readonly IDiskStatsReader _diskStatsReader ;
30
34
private readonly object _lock = new ( ) ;
31
- private readonly Dictionary < string , DiskStats > _baselineDiskStatsDict = [ ] ;
32
- private List < DiskStats > _diskStatsSnapshot = [ ] ;
35
+ private readonly FrozenDictionary < string , DiskStats > _baselineDiskStatsDict = FrozenDictionary < string , DiskStats > . Empty ;
36
+ private readonly TimeSpan _retryInterval = TimeSpan . FromMinutes ( 5 ) ;
37
+
38
+ private DateTimeOffset _lastDiskStatsFailure = DateTimeOffset . MinValue ;
39
+ private bool _diskStatsUnavailable ;
40
+
41
+ private DiskStats [ ] _diskStatsSnapshot = [ ] ;
33
42
private DateTimeOffset _lastRefreshTime = DateTimeOffset . MinValue ;
34
43
35
44
public LinuxSystemDiskMetrics (
@@ -48,7 +57,7 @@ public LinuxSystemDiskMetrics(
48
57
}
49
58
50
59
// We need to read the disk stats once to get the baseline values
51
- _baselineDiskStatsDict = GetAllDiskStats ( ) . ToDictionary ( d => d . DeviceName ) ;
60
+ _baselineDiskStatsDict = GetAllDiskStats ( ) . ToFrozenDictionary ( d => d . DeviceName ) ;
52
61
53
62
#pragma warning disable CA2000 // Dispose objects before losing scope
54
63
// We don't dispose the meter because IMeterFactory handles that
@@ -85,7 +94,7 @@ public LinuxSystemDiskMetrics(
85
94
private IEnumerable < Measurement < long > > GetDiskIoMeasurements ( )
86
95
{
87
96
List < Measurement < long > > measurements = [ ] ;
88
- List < DiskStats > diskStatsSnapshot = GetDiskStatsSnapshot ( ) ;
97
+ DiskStats [ ] diskStatsSnapshot = GetDiskStatsSnapshot ( ) ;
89
98
90
99
foreach ( DiskStats diskStats in diskStatsSnapshot )
91
100
{
@@ -102,7 +111,7 @@ private IEnumerable<Measurement<long>> GetDiskIoMeasurements()
102
111
private IEnumerable < Measurement < long > > GetDiskOperationMeasurements ( )
103
112
{
104
113
List < Measurement < long > > measurements = [ ] ;
105
- List < DiskStats > diskStatsSnapshot = GetDiskStatsSnapshot ( ) ;
114
+ DiskStats [ ] diskStatsSnapshot = GetDiskStatsSnapshot ( ) ;
106
115
107
116
foreach ( DiskStats diskStats in diskStatsSnapshot )
108
117
{
@@ -119,7 +128,7 @@ private IEnumerable<Measurement<long>> GetDiskOperationMeasurements()
119
128
private IEnumerable < Measurement < double > > GetDiskIoTimeMeasurements ( )
120
129
{
121
130
List < Measurement < double > > measurements = [ ] ;
122
- List < DiskStats > diskStatsSnapshot = GetDiskStatsSnapshot ( ) ;
131
+ DiskStats [ ] diskStatsSnapshot = GetDiskStatsSnapshot ( ) ;
123
132
124
133
foreach ( DiskStats diskStats in diskStatsSnapshot )
125
134
{
@@ -131,12 +140,12 @@ private IEnumerable<Measurement<double>> GetDiskIoTimeMeasurements()
131
140
return measurements ;
132
141
}
133
142
134
- private List < DiskStats > GetDiskStatsSnapshot ( )
143
+ private DiskStats [ ] GetDiskStatsSnapshot ( )
135
144
{
136
145
lock ( _lock )
137
146
{
138
147
DateTimeOffset now = _timeProvider . GetUtcNow ( ) ;
139
- if ( _diskStatsSnapshot . Count == 0 || ( now - _lastRefreshTime ) . TotalSeconds > MinimumDiskStatsRefreshIntervalInSeconds )
148
+ if ( _diskStatsSnapshot . Length == 0 || ( now - _lastRefreshTime ) . TotalSeconds > MinimumDiskStatsRefreshIntervalInSeconds )
140
149
{
141
150
_diskStatsSnapshot = GetAllDiskStats ( ) ;
142
151
_lastRefreshTime = now ;
@@ -146,27 +155,37 @@ private List<DiskStats> GetDiskStatsSnapshot()
146
155
return _diskStatsSnapshot ;
147
156
}
148
157
149
- private List < DiskStats > GetAllDiskStats ( )
158
+ private DiskStats [ ] GetAllDiskStats ( )
150
159
{
160
+ if ( _diskStatsUnavailable &&
161
+ _timeProvider . GetUtcNow ( ) - _lastDiskStatsFailure < _retryInterval )
162
+ {
163
+ return Array . Empty < DiskStats > ( ) ;
164
+ }
165
+
151
166
try
152
167
{
153
- List < DiskStats > diskStatsList = _diskStatsReader . ReadAll ( ) ;
154
-
155
- // We should not include ram, loop, or dm(device-mapper) devices in the disk stats, should we?
156
- diskStatsList = diskStatsList
157
- . Where ( d => ! d . DeviceName . StartsWith ( "ram" , StringComparison . OrdinalIgnoreCase )
158
- && ! d . DeviceName . StartsWith ( "loop" , StringComparison . OrdinalIgnoreCase )
159
- && ! d . DeviceName . StartsWith ( "dm-" , StringComparison . OrdinalIgnoreCase ) )
160
- . ToList ( ) ;
168
+ DiskStats [ ] diskStatsList = _diskStatsReader . ReadAll ( _skipDevicePrefixes ) ;
169
+ _diskStatsUnavailable = false ;
170
+
161
171
return diskStatsList ;
162
172
}
173
+ catch ( Exception ex ) when (
174
+ ex is FileNotFoundException ||
175
+ ex is DirectoryNotFoundException ||
176
+ ex is UnauthorizedAccessException )
177
+ {
178
+ _logger . HandleDiskStatsException ( ex . Message ) ;
179
+ _lastDiskStatsFailure = _timeProvider . GetUtcNow ( ) ;
180
+ _diskStatsUnavailable = true ;
181
+ }
163
182
#pragma warning disable CA1031
164
183
catch ( Exception ex )
165
184
#pragma warning restore CA1031
166
185
{
167
- Log . HandleDiskStatsException ( _logger , ex . Message ) ;
186
+ _logger . HandleDiskStatsException ( ex . Message ) ;
168
187
}
169
188
170
- return [ ] ;
189
+ return Array . Empty < DiskStats > ( ) ;
171
190
}
172
191
}
0 commit comments