From 02c8a103aa93980a56ff43f6329129e058f3fd0a Mon Sep 17 00:00:00 2001
From: Niko Ehrenfeuchter <nikolaus.ehrenfeuchter@unibas.ch>
Date: Tue, 24 Jul 2018 17:59:31 +0200
Subject: [PATCH] Use an abstract class "MonitorBase" for load monitors

---
 ATxCommon/ATxCommon.csproj           |   1 +
 ATxCommon/Monitoring/Cpu.cs          | 229 ++------------------------
 ATxCommon/Monitoring/MonitorBase.cs  | 232 +++++++++++++++++++++++++++
 ATxCommon/Monitoring/PhysicalDisk.cs | 229 ++------------------------
 4 files changed, 255 insertions(+), 436 deletions(-)
 create mode 100644 ATxCommon/Monitoring/MonitorBase.cs

diff --git a/ATxCommon/ATxCommon.csproj b/ATxCommon/ATxCommon.csproj
index c006aed..7e3dffd 100644
--- a/ATxCommon/ATxCommon.csproj
+++ b/ATxCommon/ATxCommon.csproj
@@ -48,6 +48,7 @@
     <Compile Include="Conv.cs" />
     <Compile Include="FsUtils.cs" />
     <Compile Include="Monitoring\Cpu.cs" />
+    <Compile Include="Monitoring\MonitorBase.cs" />
     <Compile Include="Monitoring\PhysicalDisk.cs" />
     <Compile Include="NLog\RateLimitWrapper.cs" />
     <Compile Include="Serializables\DriveToCheck.cs" />
diff --git a/ATxCommon/Monitoring/Cpu.cs b/ATxCommon/Monitoring/Cpu.cs
index 5500d22..fec80c3 100644
--- a/ATxCommon/Monitoring/Cpu.cs
+++ b/ATxCommon/Monitoring/Cpu.cs
@@ -1,226 +1,23 @@
-using System;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.Timers;
-using NLog;
-using Timer = System.Timers.Timer;
-
-namespace ATxCommon.Monitoring
+namespace ATxCommon.Monitoring
 {
+    /// <inheritdoc />
     /// <summary>
-    /// CPU load monitoring class, constantly checking the current load at the given <see
-    /// cref="Interval"/> in a separate (timer-based) thread.
-    /// 
-    /// The load is determined using a <see cref="PerformanceCounter"/>, and is compared against
-    /// a configurable <see cref="Limit"/>. If the load changes from below the limit to above, a
-    /// <see cref="LoadAboveLimit"/> event will be raised. If the load has been above the limit
-    /// and is then dropping below, an <see cref="OnLoadBelowLimit"/> event will be raised as soon
-    /// as a given number of consecutive measurements (defined via <see cref="Probation"/>) were
-    /// found to be below the limit.
+    /// Monitoring class for CPU, using the idle/non-idle time fraction for measuring the load.
     /// </summary>
-    public class Cpu
+    public class Cpu : MonitorBase
     {
-        private static readonly Logger Log = LogManager.GetCurrentClassLogger();
-
-        /// <summary>
-        /// The generic event handler delegate for CPU events.
-        /// </summary>
-        public delegate void EventHandler(object sender, EventArgs e);
-        
-        /// <summary>
-        /// Event raised when the CPU load exceeds the configured limit for any measurement.
-        /// </summary>
-        public event EventHandler LoadAboveLimit;
-
-        /// <summary>
-        /// Event raised when the CPU load is below the configured limit for at least four
-        /// consecutive measurements after having exceeded this limit before.
-        /// </summary>
-        public event EventHandler LoadBelowLimit;
-
-        private readonly Timer _monitoringTimer;
-        private readonly PerformanceCounter _cpuCounter;
-        private readonly float[] _loadReadings = {0F, 0F, 0F, 0F};
-
-        private int _interval;
-        private int _limit;
-        private int _behaving;
-        private int _probation;
-
-        /// <summary>
-        /// Performance counter category name, <see cref="PerformanceCounter"/> for details.
-        /// </summary>
-        private const string Category = "Processor";
-        
-        /// <summary>
-        /// Description string to be used in log messages.
-        /// </summary>
-        private readonly string _description;
-
-        #region properties
-
-        /// <summary>
-        /// Current CPU load (usage percentage over all cores), averaged of the last four readings.
-        /// </summary>
-        /// <returns>The average CPU load from the last four readings.</returns>
-        public float Load { get; private set; }
-
-        /// <summary>
-        /// Flag representing whether the load is considered to be high or low.
-        /// </summary>
-        public bool HighLoad { get; private set; }
-
-        /// <summary>
-        /// How often (in ms) to check the CPU load.
-        /// </summary>
-        public int Interval {
-            get => _interval;
-            set {
-                _interval = value;
-                _monitoringTimer.Interval = value;
-                Log.Debug("{0} monitoring interval: {1}ms", _description, _interval);
-            }
-        }
-
-        /// <summary>
-        /// Upper limit of CPU load (usage in % over all cores) before it is classified as "high".
-        /// </summary>
-        public int Limit {
-            get => _limit;
-            set {
-                _limit = value;
-                Log.Debug("{0} monitoring limit: {1}", _description, _limit);
-            }
-        }
-
-        /// <summary>
-        /// Number of cycles where the CPU load value has to be below the limit before it is
-        /// classified as "low" again.
-        /// </summary>
-        public int Probation {
-            get => _probation;
-            set {
-                _probation = value;
-                Log.Debug("{0} monitoring probation cycles when violating limit: {1}",
-                    _description, _probation);
-            }
-        }
-        
-        /// <summary>
-        /// Indicating whether the CPU load monitoring is active.
-        /// </summary>
-        public bool Enabled {
-            get => _monitoringTimer.Enabled;
-            set {
-                Log.Debug("{0} - {1} monitoring.", _description, value ? "enabling" : "disabling");
-                _monitoringTimer.Enabled = value;
-            }
-        }
-
-        /// <summary>
-        /// Log level to use for reporting current performance readings.
-        /// </summary>
-        public LogLevel LogPerformanceReadings { get; set; } = LogLevel.Trace;
-
-        #endregion
-
-
+        /// <inheritdoc />
+        protected sealed override string Category { get; set; } = "Processor";
 
+        /// <inheritdoc />
         /// <summary>
         /// Create performance counter and initialize it.
         /// </summary>
-        public Cpu(string counterName = "% Processor Time") {
-            _interval = 250;
-            _limit = 25;
-            _probation = 40;
-            Log.Info("Initializing {0} performance monitoring for [{1}].", Category, counterName);
-            // assemble the description string to be used in messages:
-            _description = $"{Category} {counterName}";
-            try {
-                Log.Debug("{0} monitoring initializing PerformanceCounter (takes one second)...", Category);
-                _cpuCounter = new PerformanceCounter(Category, counterName, "_Total");
-                var curLoad = _cpuCounter.NextValue();
-                Log.Debug("{0} initial value: {1:0.0}", _description, curLoad);
-                Thread.Sleep(1000);
-                curLoad = _cpuCounter.NextValue();
-                Log.Debug("{0} current value: {1:0.0}", _description, curLoad);
-                // now initialize the load state:
-                HighLoad = curLoad > _limit;
-                _monitoringTimer = new Timer(_interval);
-                _monitoringTimer.Elapsed += UpdateCpuLoad;
-            }
-            catch (Exception) {
-                Log.Error("{0} monitoring initialization failed!", Category);
-                throw;
-            }
-
-            Log.Debug("{0} monitoring initialization completed.", _description);
-        }
-
-        /// <summary>
-        /// Check current CPU load, update the history of readings and trigger the corresponding
-        /// events if the required criteria are met.
-        /// </summary>
-        private void UpdateCpuLoad(object sender, ElapsedEventArgs e) {
-            _monitoringTimer.Enabled = false;
-            try {
-                // ConstrainedCopy seems to be the most efficient approach to shift the array:
-                Array.ConstrainedCopy(_loadReadings, 1, _loadReadings, 0, 3);
-                _loadReadings[3] = _cpuCounter.NextValue();
-                Load = _loadReadings.Average();
-                if (_loadReadings[3] > _limit) {
-                    if (_behaving > _probation) {
-                        // this means the load was considered as "low" before, so raise an event:
-                        OnLoadAboveLimit();
-                        Log.Trace("{0} ({1:0.0}) violating limit ({2})!",
-                            _description, _loadReadings[3], _limit);
-                    } else if (_behaving > 0) {
-                        // this means we were still in probation, so no need to trigger again...
-                        Log.Trace("{0}: resetting behaving counter (was {1}).",
-                            _description, _behaving);
-                    }
-                  _behaving = 0;
-                } else {
-                    _behaving++;
-                    if (_behaving == _probation) {
-                        Log.Trace("{0} below limit for {1} cycles, passing probation!",
-                            _description, _probation);
-                        OnLoadBelowLimit();
-                    } else if (_behaving > _probation) {
-                        Log.Trace("{0} behaving well since {1} cycles.",
-                            _description, _behaving);
-                    } else if (_behaving < 0) {
-                        Log.Info("{0}: integer wrap around happened, resetting probation " +
-                                 "counter (no reason to worry).", _description);
-                        _behaving = _probation + 1;
-                    }
-                }
-            }
-            catch (Exception ex) {
-                Log.Error("Updating {0} counters failed: {1}", _description, ex.Message);
-            }
-            finally {
-                _monitoringTimer.Enabled = true;
-            }
-            Log.Log(LogPerformanceReadings, "{0}: {1:0.0} {2}", _description,
-                _loadReadings[3], _loadReadings[3] < Limit ? " [" + _behaving + "]" : "");
-        }
-
-        /// <summary>
-        /// Raise the "LoadAboveLimit" event.
-        /// </summary>
-        protected virtual void OnLoadAboveLimit() {
-            HighLoad = true;
-            LoadAboveLimit?.Invoke(this, EventArgs.Empty);
-        }
-
-        /// <summary>
-        /// Raise the "LoadBelowLimit" event.
-        /// </summary>
-        protected virtual void OnLoadBelowLimit() {
-            HighLoad = false;
-            LoadBelowLimit?.Invoke(this, EventArgs.Empty);
+        /// <param name="counterName">The counter to use for the monitoring, default is the
+        /// overall "% Processor Time".
+        /// </param>
+        public Cpu(string counterName = "% Processor Time")
+            : base(counterName) {
         }
     }
-}
+}
\ No newline at end of file
diff --git a/ATxCommon/Monitoring/MonitorBase.cs b/ATxCommon/Monitoring/MonitorBase.cs
new file mode 100644
index 0000000..cbdccca
--- /dev/null
+++ b/ATxCommon/Monitoring/MonitorBase.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Timers;
+using NLog;
+using Timer = System.Timers.Timer;
+
+namespace ATxCommon.Monitoring
+{
+    /// <summary>
+    /// Abstract load monitoring class, constantly checking the load at the given <see
+    /// cref="Interval"/> in a separate (timer-based) thread.
+    /// 
+    /// The load (depending on the implementation in the derived class) is determined using a <see
+    /// cref="PerformanceCounter"/>, and is compared against a configurable <see cref="Limit"/>. If
+    /// the load changes from below the limit to above, a <see cref="LoadAboveLimit"/> event will
+    /// be raised. If the load has been above the limit and is then dropping below, an <see
+    /// cref="OnLoadBelowLimit"/> event will be raised as soon as a given number of consecutive
+    /// load measurements (defined via <see cref="Probation"/>) were found to be below the limit.
+    /// </summary>
+    public abstract class MonitorBase
+    {
+        protected static readonly Logger Log = LogManager.GetCurrentClassLogger();
+
+        /// <summary>
+        /// The generic event handler delegate for load monitoring events.
+        /// </summary>
+        public delegate void EventHandler(object sender, EventArgs e);
+        
+        /// <summary>
+        /// Event raised when the load exceeds the limit for any (i.e. single!) measurement.
+        /// </summary>
+        public event EventHandler LoadAboveLimit;
+
+        /// <summary>
+        /// Event raised when the load is below the configured limit for at least the number of
+        /// consecutive measurements configured in <see cref="Probation"/> after having exceeded
+        /// this limit before.
+        /// </summary>
+        public event EventHandler LoadBelowLimit;
+
+        protected readonly Timer MonitoringTimer;
+        protected readonly PerformanceCounter PerfCounter;
+        protected readonly float[] LoadReadings = {0F, 0F, 0F, 0F};
+
+        private int _interval;
+        private int _behaving;
+        private int _probation;
+
+        private float _limit;
+
+        /// <summary>
+        /// Description string to be used in log messages.
+        /// </summary>
+        private readonly string _description;
+
+
+        #region properties
+
+        /// <summary>
+        /// Name of the performance counter category, see also <see cref="PerformanceCounter"/>.
+        /// </summary>
+        protected abstract string Category { get; set; }
+
+        /// <summary>
+        /// Current load, averaged of the last four readings.
+        /// </summary>
+        /// <returns>The average load from the last four readings.</returns>
+        public float Load { get; private set; }
+
+        /// <summary>
+        /// Flag representing whether the load is considered to be high or low.
+        /// </summary>
+        public bool HighLoad { get; private set; }
+
+        /// <summary>
+        /// Time interval (in ms) after which to update the current load measurement.
+        /// </summary>
+        public int Interval {
+            get => _interval;
+            set {
+                _interval = value;
+                MonitoringTimer.Interval = value;
+                Log.Debug("{0} monitoring interval: {1}ms", _description, _interval);
+            }
+        }
+
+        /// <summary>
+        /// Upper limit of the load before it is classified as "high".
+        /// </summary>
+        public float Limit {
+            get => _limit;
+            set {
+                _limit = value;
+                Log.Debug("{0} monitoring limit: {1:0.000}", _description, _limit);
+            }
+        }
+
+        /// <summary>
+        /// Number of cycles where the load value has to be below the limit before it is
+        /// classified as "low" again.
+        /// </summary>
+        public int Probation {
+            get => _probation;
+            set {
+                _probation = value;
+                Log.Debug("{0} monitoring probation cycles when violating limit: {1}",
+                    _description, _probation);
+            }
+        }
+        
+        /// <summary>
+        /// Indicating whether this load monitor instance is active.
+        /// </summary>
+        public bool Enabled {
+            get => MonitoringTimer.Enabled;
+            set {
+                Log.Debug("{0} - {1} monitoring.", _description, value ? "enabling" : "disabling");
+                MonitoringTimer.Enabled = value;
+            }
+        }
+
+        /// <summary>
+        /// Log level to use for reporting current performance readings, default = Trace.
+        /// </summary>
+        public LogLevel LogPerformanceReadings { get; set; } = LogLevel.Trace;
+
+        #endregion
+
+
+        /// <summary>
+        /// Create performance counter and initialize it.
+        /// </summary>
+        /// <param name="counterName">The counter to use for the monitoring, depends on the
+        /// category used in the derived class.
+        /// </param>
+        protected MonitorBase(string counterName) {
+            _interval = 250;
+            _limit = 0.5F;
+            _probation = 40;
+            Log.Info("Initializing {0} PerformanceCounter for [{1}].", Category, counterName);
+            // assemble the description string to be used in messages:
+            _description = $"{Category} {counterName}";
+            try {
+                PerfCounter = new PerformanceCounter(Category, counterName, "_Total");
+                var curLoad = PerfCounter.NextValue();
+                Log.Debug("{0} initial value: {1:0.000}", _description, curLoad);
+                /* this initialization might be necessary for "Processor" counters, so we just
+                 * temporarily disable those calls:
+                Thread.Sleep(1000);
+                curLoad = _perfCounter.NextValue();
+                Log.Debug("{0} current value: {1:0.000}", _description, curLoad);
+                 */
+                // initialize the load state as high, so we have to pass probation at least once:
+                HighLoad = true;
+                MonitoringTimer = new Timer(_interval);
+                MonitoringTimer.Elapsed += UpdateLoadReadings;
+            }
+            catch (Exception) {
+                Log.Error("{0} monitoring initialization failed!", Category);
+                throw;
+            }
+
+            Log.Debug("{0} monitoring initialization completed.", _description);
+        }
+
+        /// <summary>
+        /// Check current load value, update the history of readings and trigger the corresponding
+        /// events if the required criteria are met.
+        /// </summary>
+        private void UpdateLoadReadings(object sender, ElapsedEventArgs e) {
+            MonitoringTimer.Enabled = false;
+            try {
+                // ConstrainedCopy seems to be the most efficient approach to shift the array:
+                Array.ConstrainedCopy(LoadReadings, 1, LoadReadings, 0, 3);
+                LoadReadings[3] = PerfCounter.NextValue();
+                Load = LoadReadings.Average();
+                if (LoadReadings[3] > _limit) {
+                    if (_behaving > _probation) {
+                        // this means the load was considered as "low" before, so raise an event:
+                        OnLoadAboveLimit();
+                        Log.Trace("{0} ({1:0.00}) violating limit ({2})!",
+                            _description, LoadReadings[3], _limit);
+                    } else if (_behaving > 0) {
+                        // this means we were still in probation, so no need to trigger again...
+                        Log.Trace("{0}: resetting behaving counter (was {1}).",
+                            _description, _behaving);
+                    }
+                    _behaving = 0;
+                } else {
+                    _behaving++;
+                    if (_behaving == _probation) {
+                        Log.Trace("{0} below limit for {1} cycles, passing probation!",
+                            _description, _probation);
+                        OnLoadBelowLimit();
+                    } else if (_behaving > _probation) {
+                        Log.Trace("{0} behaving well since {1} cycles.",
+                            _description, _behaving);
+                    } else if (_behaving < 0) {
+                        Log.Info("{0}: integer wrap around happened, resetting probation " +
+                                 "counter (no reason to worry).", _description);
+                        _behaving = _probation + 1;
+                    }
+                }
+            }
+            catch (Exception ex) {
+                Log.Error("Updating {0} counters failed: {1}", _description, ex.Message);
+            }
+            finally {
+                MonitoringTimer.Enabled = true;
+            }
+            Log.Log(LogPerformanceReadings, "{0}: {1:0.000} {2}", _description,
+                LoadReadings[3], LoadReadings[3] < Limit ? " [" + _behaving + "]" : "");
+        }
+
+        /// <summary>
+        /// Raise the "LoadAboveLimit" event.
+        /// </summary>
+        protected virtual void OnLoadAboveLimit() {
+            HighLoad = true;
+            LoadAboveLimit?.Invoke(this, EventArgs.Empty);
+        }
+
+        /// <summary>
+        /// Raise the "LoadBelowLimit" event.
+        /// </summary>
+        protected virtual void OnLoadBelowLimit() {
+            HighLoad = false;
+            LoadBelowLimit?.Invoke(this, EventArgs.Empty);
+        }
+    }
+}
diff --git a/ATxCommon/Monitoring/PhysicalDisk.cs b/ATxCommon/Monitoring/PhysicalDisk.cs
index 115766f..db32759 100644
--- a/ATxCommon/Monitoring/PhysicalDisk.cs
+++ b/ATxCommon/Monitoring/PhysicalDisk.cs
@@ -1,135 +1,15 @@
-using System;
-using System.Diagnostics;
-using System.Linq;
-// using System.Threading;
-using System.Timers;
-using NLog;
-using Timer = System.Timers.Timer;
-
-namespace ATxCommon.Monitoring
+namespace ATxCommon.Monitoring
 {
+    /// <inheritdoc />
     /// <summary>
-    /// Load monitoring class for physical disks, constantly checking the queue length at the
-    /// given <see cref="Interval"/> in a separate (timer-based) thread.
-    /// 
-    /// The load (=queue length) is determined using a <see cref="PerformanceCounter"/>, and is
-    /// compared against a configurable <see cref="Limit"/>. If the load changes from below the
-    /// limit to above, a <see cref="LoadAboveLimit"/> event will be raised. If the load has been
-    /// above the limit and is then dropping below, an <see cref="OnLoadBelowLimit"/> event will
-    /// be raised as soon as a given number of consecutive load measurements (defined via <see
-    /// cref="Probation"/>) were found to be below the limit.
+    /// Monitoring class for disk I/O, using the disk queue length for measuring the load.
     /// </summary>
-    public class PhysicalDisk
+    public class PhysicalDisk : MonitorBase
     {
-        private static readonly Logger Log = LogManager.GetCurrentClassLogger();
-
-        /// <summary>
-        /// The generic event handler delegate for PhysicalDisk events.
-        /// </summary>
-        public delegate void EventHandler(object sender, EventArgs e);
-        
-        /// <summary>
-        /// Event raised when the PhysicalDisk load exceeds the limit for any measurement.
-        /// </summary>
-        public event EventHandler LoadAboveLimit;
-
-        /// <summary>
-        /// Event raised when the PhysicalDisk load is below the configured limit for at least
-        /// the number of consecutive measurements configured in <see cref="Probation"/> after
-        /// having exceeded this limit before.
-        /// </summary>
-        public event EventHandler LoadBelowLimit;
-
-        private readonly Timer _monitoringTimer;
-        private readonly PerformanceCounter _diskQueueLength;
-        private readonly float[] _loadReadings = {0F, 0F, 0F, 0F};
-
-        private int _interval;
-        private int _behaving;
-        private int _probation;
-
-        private float _limit;
-
-        /// <summary>
-        /// Name of the performance counter category, <see cref="PerformanceCounter"/>.
-        /// </summary>
-        private const string Category = "PhysicalDisk";
-
-        /// <summary>
-        /// Description string to be used in log messages.
-        /// </summary>
-        private readonly string _description;
-
-
-        #region properties
-
-        /// <summary>
-        /// Current PhysicalDisk Queue Length, averaged of the last four readings.
-        /// </summary>
-        /// <returns>The average PhysicalDisk Queue Length from the last four readings.</returns>
-        public float Load { get; private set; }
-
-        /// <summary>
-        /// Flag representing whether the load is considered to be high or low.
-        /// </summary>
-        public bool HighLoad { get; private set; }
-
-        /// <summary>
-        /// How often (in ms) to check the PhysicalDisk Queue Length.
-        /// </summary>
-        public int Interval {
-            get => _interval;
-            set {
-                _interval = value;
-                _monitoringTimer.Interval = value;
-                Log.Debug("{0} monitoring interval: {1}ms", _description, _interval);
-            }
-        }
-
-        /// <summary>
-        /// Upper limit of PhysicalDisk load before it is classified as "high".
-        /// </summary>
-        public float Limit {
-            get => _limit;
-            set {
-                _limit = value;
-                Log.Debug("{0} monitoring limit: {1:0.000}", _description, _limit);
-            }
-        }
-
-        /// <summary>
-        /// Number of cycles where the PhysicalDisk load value has to be below the limit before it is
-        /// classified as "low" again.
-        /// </summary>
-        public int Probation {
-            get => _probation;
-            set {
-                _probation = value;
-                Log.Debug("{0} monitoring probation cycles when violating limit: {1}",
-                    _description, _probation);
-            }
-        }
-        
-        /// <summary>
-        /// Indicating whether the PhysicalDisk load monitoring is active.
-        /// </summary>
-        public bool Enabled {
-            get => _monitoringTimer.Enabled;
-            set {
-                Log.Debug("{0} - {1} monitoring.", _description, value ? "enabling" : "disabling");
-                _monitoringTimer.Enabled = value;
-            }
-        }
-
-        /// <summary>
-        /// Log level to use for reporting current performance readings, default = Trace.
-        /// </summary>
-        public LogLevel LogPerformanceReadings { get; set; } = LogLevel.Trace;
-
-        #endregion
-
-
+        /// <inheritdoc />
+        protected sealed override string Category { get; set; } = "PhysicalDisk";
 
+        /// <inheritdoc />
         /// <summary>
         /// Create performance counter and initialize it.
         /// </summary>
@@ -137,99 +17,8 @@ namespace ATxCommon.Monitoring
         /// overall "Avg. Disk Queue Length", other reasonable options are the corresponding read
         /// or write queues ("Avg. Disk Read Queue Length" and "Avg. Disk Write Queue Length").
         /// </param>
-        public PhysicalDisk(string counterName = "Avg. Disk Queue Length") {
-            _interval = 250;
-            _limit = 0.5F;
-            _probation = 40;
-            Log.Info("Initializing {0} PerformanceCounter for [{1}].", Category, counterName);
-            // assemble the description string to be used in messages:
-            _description = $"{Category} {counterName}";
-            try {
-                _diskQueueLength = new PerformanceCounter(Category, counterName, "_Total");
-                var curLoad = _diskQueueLength.NextValue();
-                Log.Debug("{0} initial value: {1:0.000}", _description, curLoad);
-                /* this initialization doesn't seem to be necessary for PhysicalDisk, so we just
-                 * disable those calls for now:
-                Thread.Sleep(1000);
-                curLoad = _diskQueueLength.NextValue();
-                Log.Debug("{0} current value: {1:0.000}", _description, curLoad);
-                 */
-                // now initialize the load state:
-                HighLoad = curLoad > _limit;
-                _monitoringTimer = new Timer(_interval);
-                _monitoringTimer.Elapsed += UpdatePhysicalDiskLoad;
-            }
-            catch (Exception) {
-                Log.Error("{0} monitoring initialization failed!", Category);
-                throw;
-            }
-
-            Log.Debug("{0} monitoring initialization completed.", _description);
-        }
-
-        /// <summary>
-        /// Check current PhysicalDisk queue length, update the history of readings and trigger
-        /// the corresponding events if the required criteria are met.
-        /// </summary>
-        private void UpdatePhysicalDiskLoad(object sender, ElapsedEventArgs e) {
-            _monitoringTimer.Enabled = false;
-            try {
-                // ConstrainedCopy seems to be the most efficient approach to shift the array:
-                Array.ConstrainedCopy(_loadReadings, 1, _loadReadings, 0, 3);
-                _loadReadings[3] = _diskQueueLength.NextValue();
-                Load = _loadReadings.Average();
-                if (_loadReadings[3] > _limit) {
-                    if (_behaving > _probation) {
-                        // this means the load was considered as "low" before, so raise an event:
-                        OnLoadAboveLimit();
-                        Log.Trace("{0} ({1:0.00}) violating limit ({2})!",
-                            _description, _loadReadings[3], _limit);
-                    } else if (_behaving > 0) {
-                        // this means we were still in probation, so no need to trigger again...
-                        Log.Trace("{0}: resetting behaving counter (was {1}).",
-                            _description, _behaving);
-                    }
-                    _behaving = 0;
-                } else {
-                    _behaving++;
-                    if (_behaving == _probation) {
-                        Log.Trace("{0} below limit for {1} cycles, passing probation!",
-                            _description, _probation);
-                        OnLoadBelowLimit();
-                    } else if (_behaving > _probation) {
-                        Log.Trace("{0} behaving well since {1} cycles.",
-                            _description, _behaving);
-                    } else if (_behaving < 0) {
-                        Log.Info("{0}: integer wrap around happened, resetting probation " +
-                                 "counter (no reason to worry).", _description);
-                        _behaving = _probation + 1;
-                    }
-                }
-            }
-            catch (Exception ex) {
-                Log.Error("Updating {0} counters failed: {1}", _description, ex.Message);
-            }
-            finally {
-                _monitoringTimer.Enabled = true;
-            }
-            Log.Log(LogPerformanceReadings, "{0}: {1:0.000} {2}", _description,
-                _loadReadings[3], _loadReadings[3] < Limit ? " [" + _behaving + "]" : "");
-        }
-
-        /// <summary>
-        /// Raise the "LoadAboveLimit" event.
-        /// </summary>
-        protected virtual void OnLoadAboveLimit() {
-            HighLoad = true;
-            LoadAboveLimit?.Invoke(this, EventArgs.Empty);
-        }
-
-        /// <summary>
-        /// Raise the "LoadBelowLimit" event.
-        /// </summary>
-        protected virtual void OnLoadBelowLimit() {
-            HighLoad = false;
-            LoadBelowLimit?.Invoke(this, EventArgs.Empty);
+        public PhysicalDisk(string counterName = "Avg. Disk Queue Length")
+            : base(counterName) {
         }
     }
 }
\ No newline at end of file
-- 
GitLab