Allow asp.net duration triggers to change counter interval (#2649)
authorWiktor Kopec <wiktork@microsoft.com>
Thu, 7 Oct 2021 18:00:10 +0000 (11:00 -0700)
committerGitHub <noreply@github.com>
Thu, 7 Oct 2021 18:00:10 +0000 (18:00 +0000)
* Allow asp.net duration triggers to change counter interval
- Also change interval to float

* PR feedback

* pr feedback

src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/AspNetTriggerSourceConfiguration.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/MetricSourceConfiguration.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterFilter.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/EventCounterPipeline.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/EventPipeCounterPipelineSettings.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/AspNet/AspNetRequestDurationTrigger.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/EventCounter/EventCounterTriggerImpl.cs
src/Microsoft.Diagnostics.Monitoring.EventPipe/Triggers/EventCounter/EventCounterTriggerSettings.cs
src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterPipelineUnitTests.cs
src/tests/Microsoft.Diagnostics.Monitoring.EventPipe/EventCounterTriggerTests.cs

index f27d97c2b43b957cf5472eb3e749d84e519a4947..415c29bf519c18489a201cf3a1b11a835fac9c3d 100644 (file)
@@ -13,13 +13,13 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
     {
         // In order to handle hung requests, we also capture metrics on a regular interval.
         // This acts as a wake up timer, since we cannot rely on Activity1Stop.
-        private readonly bool _supportHeartbeat;
+        // Note this value is parameterizable due to limitations in event counters: only 1 interval duration is
+        // respected. This gives the trigger infrastructure a way to use the same interval.
+        private readonly float? _heartbeatIntervalSeconds;
 
-        public const int DefaultHeartbeatInterval = 10;
-
-        public AspNetTriggerSourceConfiguration(bool supportHeartbeat = false)
+        public AspNetTriggerSourceConfiguration(float? heartbeatIntervalSeconds = null)
         {
-            _supportHeartbeat = supportHeartbeat;
+            _heartbeatIntervalSeconds = heartbeatIntervalSeconds;
         }
 
         /// <summary>
@@ -45,11 +45,11 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
 
         public override IList<EventPipeProvider> GetProviders()
         {
-            if (_supportHeartbeat)
+            if (_heartbeatIntervalSeconds.HasValue)
             {
                 return new AggregateSourceConfiguration(
-                    new AspNetTriggerSourceConfiguration(supportHeartbeat: false),
-                    new MetricSourceConfiguration(DefaultHeartbeatInterval, new[] { MicrosoftAspNetCoreHostingEventSourceName })).GetProviders();
+                    new AspNetTriggerSourceConfiguration(heartbeatIntervalSeconds: null),
+                    new MetricSourceConfiguration(_heartbeatIntervalSeconds.Value, new[] { MicrosoftAspNetCoreHostingEventSourceName })).GetProviders();
 
             }
             else
index 58561c8d1c811d33e61ab6af585e8cd9d68419b8..30d4f970f8095e65ae94cb49b876ae7a03fc9297 100644 (file)
@@ -17,7 +17,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
     {
         private readonly IList<EventPipeProvider> _eventPipeProviders;
 
-        public MetricSourceConfiguration(int metricIntervalSeconds, IEnumerable<string> customProviderNames)
+        public MetricSourceConfiguration(float metricIntervalSeconds, IEnumerable<string> customProviderNames)
         {
             if (customProviderNames == null)
             {
index df2756c3e665ce4a1a817c6ea849d4f2a0a473e0..dd83243ef763eea9bce44fb9151da1f759aa0204 100644 (file)
@@ -13,14 +13,16 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
         private Dictionary<string, List<string>> _enabledCounters;
         private int _intervalMilliseconds;
 
-        public static CounterFilter AllCounters(int counterIntervalSeconds)
+        public static CounterFilter AllCounters(float counterIntervalSeconds)
             => new CounterFilter(counterIntervalSeconds);
 
-        public CounterFilter(int intervalSeconds)
+        public CounterFilter(float intervalSeconds)
         {
             //Provider names are not case sensitive, but counter names are.
             _enabledCounters = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
-            _intervalMilliseconds = intervalSeconds * 1000;
+
+            //The Series payload of the counter which we use for filtering
+            _intervalMilliseconds = (int)(intervalSeconds * 1000);
         }
 
         // Called when we want to enable all counters under a provider name.
@@ -36,9 +38,9 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
 
         public IEnumerable<string> GetProviders() => _enabledCounters.Keys;
 
-        public bool IsIncluded(string providerName, string counterName, int interval)
+        public bool IsIncluded(string providerName, string counterName, int intervalMilliseconds)
         {
-            if (_intervalMilliseconds != interval)
+            if (_intervalMilliseconds != intervalMilliseconds)
             {
                 return false;
             }
index 12beff51b635026a6c76320e7f3fc089f2d767b1..14cdfe5b9f1fd511ba849ea07fbd5706c89b6d24 100644 (file)
@@ -24,7 +24,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
 
             if (settings.CounterGroups.Length > 0)
             {
-                _filter = new CounterFilter(CounterIntervalSeconds);
+                _filter = new CounterFilter(Settings.CounterIntervalSeconds);
                 foreach (var counterGroup in settings.CounterGroups)
                 {
                     _filter.AddFilter(counterGroup.ProviderName, counterGroup.CounterNames);
@@ -32,13 +32,13 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
             }
             else
             {
-                _filter = CounterFilter.AllCounters(CounterIntervalSeconds);
+                _filter = CounterFilter.AllCounters(Settings.CounterIntervalSeconds);
             }
         }
 
         protected override MonitoringSourceConfiguration CreateConfiguration()
         {
-            return new MetricSourceConfiguration(CounterIntervalSeconds, _filter.GetProviders());
+            return new MetricSourceConfiguration(Settings.CounterIntervalSeconds, _filter.GetProviders());
         }
 
         protected override async Task OnEventSourceAvailable(EventPipeEventSource eventSource, Func<Task> stopSessionAsync, CancellationToken token)
@@ -83,7 +83,5 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
                 }
             }
         }
-
-        private int CounterIntervalSeconds => (int)Settings.RefreshInterval.TotalSeconds;
     }
 }
index ccc09feb7a1bafbed9e620f23e6bca1de7a47a8b..ffcb93924a39184b64ebbad04c40e895ad92dcb1 100644 (file)
@@ -11,7 +11,10 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
     internal class EventPipeCounterPipelineSettings : EventSourcePipelineSettings
     {
         public EventPipeCounterGroup[] CounterGroups { get; set; }
-        public TimeSpan RefreshInterval { get; set; }
+
+        //Do not use TimeSpan here since we may need to synchronize this pipeline interval
+        //with a different session and want to make sure the values are identical.
+        public float CounterIntervalSeconds { get; set; }
     }
 
     internal class EventPipeCounterGroup
index edf73a89e68b83bcbd9de313f03271ca218d8d6f..9c8c0fdbcfdb0e232501e20dcb0a8638d46088d8 100644 (file)
@@ -22,6 +22,11 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
                 //Make sure we are part of the requested series. If multiple clients request metrics, all of them get the metrics.
                 string series = payloadFields["Series"].ToString();
                 string counterName = payloadFields["Name"].ToString();
+
+                //CONSIDER
+                //Concurrent counter sessions do not each get a separate interval. Instead the payload
+                //for _all_ the counters changes the Series to be the lowest specified interval, on a per provider basis.
+                //Currently the CounterFilter will remove any data whose Series doesn't match the requested interval.
                 if (!filter.IsIncluded(traceEvent.ProviderName, counterName, GetInterval(series)))
                 {
                     return false;
index 72e479ef740ab076b38db128a7315cba66ad4f1b..5100d392934ee1e648591eab2f7256315cab41c1 100644 (file)
@@ -13,8 +13,10 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.AspNet
     {
         private readonly long _durationTicks;
 
+        //Note that regardless of the metrics interval used, we will only update
+        //on a certain frequency to avoid unnecessary processing.
         //This is adjusted due to rounding errors on event counter timestamp math.
-        private readonly TimeSpan _heartBeatInterval = TimeSpan.FromSeconds(AspNetTriggerSourceConfiguration.DefaultHeartbeatInterval - 1);
+        private static readonly TimeSpan HeartbeatIntervalSeconds = TimeSpan.FromSeconds(9);
         private SlidingWindow _window;
         private Dictionary<string, DateTime> _requests = new();
         private DateTime _lastHeartbeatProcessed = DateTime.MinValue;
@@ -36,7 +38,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.AspNet
         {
             //May get additional heartbeats based on multiple counters or extra intervals. We only
             //process the data periodically.
-            if (timestamp - _lastHeartbeatProcessed > _heartBeatInterval)
+            if (timestamp - _lastHeartbeatProcessed > HeartbeatIntervalSeconds)
             {
                 _lastHeartbeatProcessed = timestamp;
                 List<string> requestsToRemove = new();
index 909e6452c0260cce56e0f6d5c105f48c3f97215f..719fb2ae3bee5cfd35ad4f1cd14a11950e2c3840 100644 (file)
@@ -44,7 +44,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.EventCounter
                 _valueFilter = value => value < maxValue;
             }
 
-            _intervalTicks = settings.CounterIntervalSeconds * TimeSpan.TicksPerSecond;
+            _intervalTicks = (long)(settings.CounterIntervalSeconds * TimeSpan.TicksPerSecond);
             _windowTicks = settings.SlidingWindowDuration.Ticks;
         }
 
index 88331ecd5f7a7baeb021238f154306fd7abc1a80..1fa46cad3085e36b67c6d5699e0bcda7780ac30c 100644 (file)
@@ -14,8 +14,8 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.EventCounter
     internal sealed class EventCounterTriggerSettings :
         IValidatableObject
     {
-        internal const int CounterIntervalSeconds_MaxValue = 24 * 60 * 60; // 1 day
-        internal const int CounterIntervalSeconds_MinValue = 1; // 1 second
+        internal const float CounterIntervalSeconds_MaxValue = 24 * 60 * 60; // 1 day
+        internal const float CounterIntervalSeconds_MinValue = 1; // 1 second
 
         internal const string EitherGreaterThanLessThanMessage = "Either the " + nameof(GreaterThan) + " field or the " + nameof(LessThan) + " field are required.";
 
@@ -59,7 +59,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.Triggers.EventCounter
         /// The sampling interval of the event counter.
         /// </summary>
         [Range(CounterIntervalSeconds_MinValue, CounterIntervalSeconds_MaxValue)]
-        public int CounterIntervalSeconds { get; set; }
+        public float CounterIntervalSeconds { get; set; }
 
         IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
         {
index 2aca29c7edc945f40a42b71d73cd1c90a71ae793..cf7cc8e97668aa0987ce8f86260c4096c1c0d783 100644 (file)
@@ -113,7 +113,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests
                             CounterNames = expectedCounters
                         }
                     },
-                    RefreshInterval = TimeSpan.FromSeconds(1)
+                    CounterIntervalSeconds = 1
                 }, new[] { logger });
 
                 await PipelineTestUtilities.ExecutePipelineWithDebugee(
index d212f6ef647ed530da8259d49f91e8db822e027d..257b6b078c12961dfd49bddbb47071be1ed6bf84 100644 (file)
@@ -440,12 +440,12 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests
         /// </summary>
         private sealed class CpuUsagePayloadFactory
         {
-            private readonly int _intervalSeconds;
+            private readonly float _intervalSeconds;
             private readonly Random _random;
 
             private DateTime? _lastTimestamp;
 
-            public CpuUsagePayloadFactory(int seed, int intervalSeconds)
+            public CpuUsagePayloadFactory(int seed, float intervalSeconds)
             {
                 _intervalSeconds = intervalSeconds;
                 _random = new Random(seed);