Publish `UpDownCounter` and `ObservableUpDownCounter` Events for System.Diagnostics...
authorkkeirstead <85592574+kkeirstead@users.noreply.github.com>
Sun, 29 Jan 2023 00:56:15 +0000 (16:56 -0800)
committerGitHub <noreply@github.com>
Sun, 29 Jan 2023 00:56:15 +0000 (16:56 -0800)
Add support for publishing UpDownCounter (and Observable...) - also added in the missing testing for this.

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/AggregationManager.cs
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/MetricsEventSource.cs
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/RateAggregator.cs
src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs
src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs

index 8af2391..770af1d 100644 (file)
@@ -275,7 +275,7 @@ namespace System.Diagnostics.Metrics
                 {
                     lock (this)
                     {
-                        return CheckTimeSeriesAllowed() ? new RateSumAggregator() : null;
+                        return CheckTimeSeriesAllowed() ? new RateSumAggregator(isMonotonic: true) : null;
                     }
                 };
             }
@@ -285,7 +285,7 @@ namespace System.Diagnostics.Metrics
                 {
                     lock (this)
                     {
-                        return CheckTimeSeriesAllowed() ? new RateAggregator() : null;
+                        return CheckTimeSeriesAllowed() ? new RateAggregator(isMonotonic: true) : null;
                     }
                 };
             }
@@ -312,6 +312,26 @@ namespace System.Diagnostics.Metrics
                     }
                 };
             }
+            else if (genericDefType == typeof(UpDownCounter<>))
+            {
+                return () =>
+                {
+                    lock (this)
+                    {
+                        return CheckTimeSeriesAllowed() ? new RateSumAggregator(isMonotonic: false) : null;
+                    }
+                };
+            }
+            else if (genericDefType == typeof(ObservableUpDownCounter<>))
+            {
+                return () =>
+                {
+                    lock (this)
+                    {
+                        return CheckTimeSeriesAllowed() ? new RateAggregator(isMonotonic: false) : null;
+                    }
+                };
+            }
             else
             {
                 return null;
index 8b2fd37..79a5b78 100644 (file)
@@ -191,6 +191,14 @@ namespace System.Diagnostics.Metrics
             WriteEvent(15, runningSessionId);
         }
 
+        [Event(16, Keywords = Keywords.TimeSeriesValues)]
+        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+                            Justification = "This calls WriteEvent with all primitive arguments which is safe. Primitives are always serialized properly.")]
+        public void UpDownCounterRateValuePublished(string sessionId, string meterName, string? meterVersion, string instrumentName, string? unit, string tags, string rate)
+        {
+            WriteEvent(16, sessionId, meterName, meterVersion ?? "", instrumentName, unit ?? "", tags, rate);
+        }
+
         /// <summary>
         /// Called when the EventSource gets a command from a EventListener or ETW.
         /// </summary>
@@ -407,8 +415,16 @@ namespace System.Diagnostics.Metrics
             {
                 if (stats.AggregationStatistics is RateStatistics rateStats)
                 {
-                    Log.CounterRateValuePublished(sessionId, instrument.Meter.Name, instrument.Meter.Version, instrument.Name, instrument.Unit, FormatTags(stats.Labels),
-                        rateStats.Delta.HasValue ? rateStats.Delta.Value.ToString(CultureInfo.InvariantCulture) : "");
+                    if (rateStats.IsMonotonic)
+                    {
+                        Log.CounterRateValuePublished(sessionId, instrument.Meter.Name, instrument.Meter.Version, instrument.Name, instrument.Unit, FormatTags(stats.Labels),
+                            rateStats.Delta.HasValue ? rateStats.Delta.Value.ToString(CultureInfo.InvariantCulture) : "");
+                    }
+                    else
+                    {
+                        Log.UpDownCounterRateValuePublished(sessionId, instrument.Meter.Name, instrument.Meter.Version, instrument.Name, instrument.Unit, FormatTags(stats.Labels),
+                            rateStats.Delta.HasValue ? rateStats.Delta.Value.ToString(CultureInfo.InvariantCulture) : "");
+                    }
                 }
                 else if (stats.AggregationStatistics is LastValueStatistics lastValueStats)
                 {
index dc27288..d1109f5 100644 (file)
@@ -6,6 +6,12 @@ namespace System.Diagnostics.Metrics
     internal sealed class RateSumAggregator : Aggregator
     {
         private double _sum;
+        private bool _isMonotonic;
+
+        public RateSumAggregator(bool isMonotonic)
+        {
+            _isMonotonic = isMonotonic;
+        }
 
         public override void Update(double value)
         {
@@ -19,7 +25,7 @@ namespace System.Diagnostics.Metrics
         {
             lock (this)
             {
-                RateStatistics? stats = new RateStatistics(_sum);
+                RateStatistics? stats = new RateStatistics(_sum, _isMonotonic);
                 _sum = 0;
                 return stats;
             }
@@ -30,6 +36,12 @@ namespace System.Diagnostics.Metrics
     {
         private double? _prevValue;
         private double _value;
+        private bool _isMonotonic;
+
+        public RateAggregator(bool isMonotonic)
+        {
+            _isMonotonic = isMonotonic;
+        }
 
         public override void Update(double value)
         {
@@ -48,7 +60,7 @@ namespace System.Diagnostics.Metrics
                 {
                     delta = _value - _prevValue.Value;
                 }
-                RateStatistics stats = new RateStatistics(delta);
+                RateStatistics stats = new RateStatistics(delta, _isMonotonic);
                 _prevValue = _value;
                 return stats;
             }
@@ -57,11 +69,14 @@ namespace System.Diagnostics.Metrics
 
     internal sealed class RateStatistics : IAggregationStatistics
     {
-        public RateStatistics(double? delta)
+        public RateStatistics(double? delta, bool isMonotonic)
         {
             Delta = delta;
+            IsMonotonic = isMonotonic;
         }
 
         public double? Delta { get; }
+
+        public bool IsMonotonic { get; }
     }
 }
index b468bbd..e85ebbe 100644 (file)
@@ -35,6 +35,9 @@ namespace System.Diagnostics.Metrics.Tests
             int gaugeState = 0;
             ObservableGauge<int> og = meter.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; });
             Histogram<int> h = meter.CreateHistogram<int>("histogram1");
+            UpDownCounter<int> udc = meter.CreateUpDownCounter<int>("upDownCounter1");
+            int upDownCounterState = 0;
+            ObservableUpDownCounter<int> oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState -= 11; return upDownCounterState; });
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter1"))
@@ -42,19 +45,23 @@ namespace System.Diagnostics.Metrics.Tests
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 1);
                 c.Add(5);
                 h.Record(19);
+                udc.Add(-33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(-40);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12");
             AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7");
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", "", "-33", "-40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", "", "", "-11");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
         }
 
@@ -69,6 +76,9 @@ namespace System.Diagnostics.Metrics.Tests
             int gaugeState = 0;
             ObservableGauge<int> og = meter.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!");
             Histogram<int> h = meter.CreateHistogram<int>("histogram1", "a unit", "the description");
+            UpDownCounter<int> udc = meter.CreateUpDownCounter<int>("upDownCounter1", "udc unit", "udc description");
+            int upDownCounterState = 0;
+            ObservableUpDownCounter<int> oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState += 11; return upDownCounterState; }, "oudc unit", "oudc description");
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter2"))
@@ -76,19 +86,23 @@ namespace System.Diagnostics.Metrics.Tests
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 1);
                 c.Add(5);
                 h.Record(19);
+                udc.Add(33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(40);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12");
             AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7", "7");
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18", "27");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", udc.Unit, "33", "40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", oudc.Unit, "", "11");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
         }
 
@@ -105,6 +119,8 @@ namespace System.Diagnostics.Metrics.Tests
                 ObservableCounter<int> oc;
                 ObservableGauge<int> og;
                 Histogram<int> h;
+                UpDownCounter<int> udc;
+                ObservableUpDownCounter<int> oudc;
 
                 EventWrittenEventArgs[] events;
                 using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter3"))
@@ -119,22 +135,29 @@ namespace System.Diagnostics.Metrics.Tests
                     int gaugeState = 0;
                     og = meter.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; });
                     h = meter.CreateHistogram<int>("histogram1");
+                    udc = meter.CreateUpDownCounter<int>("upDownCounter1");
+                    int upDownCounterState = 0;
+                    oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState -= 11; return upDownCounterState; });
 
                     c.Add(5);
                     h.Record(19);
+                    udc.Add(33);
                     listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                     c.Add(12);
                     h.Record(26);
+                    udc.Add(40);
                     listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                     events = listener.Events.ToArray();
                 }
 
-                AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+                AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
                 AssertInitialEnumerationCompleteEventPresent(events);
                 AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12");
                 AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7");
                 AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18");
                 AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
+                AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", "", "33", "40");
+                AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", "", "", "-11");
                 AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
             }
             finally
@@ -153,6 +176,8 @@ namespace System.Diagnostics.Metrics.Tests
             ObservableCounter<int> oc;
             ObservableGauge<int> og;
             Histogram<int> h;
+            UpDownCounter<int> udc;
+            ObservableUpDownCounter<int> oudc;
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter4"))
@@ -166,22 +191,29 @@ namespace System.Diagnostics.Metrics.Tests
                 int gaugeState = 0;
                 og = meter.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; });
                 h = meter.CreateHistogram<int>("histogram1");
+                udc = meter.CreateUpDownCounter<int>("upDownCounter1");
+                int upDownCounterState = 0;
+                oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState += 11; return upDownCounterState; });
 
                 c.Add(5);
                 h.Record(19);
+                udc.Add(-33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(-40);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12");
             AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7");
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", "", "-33", "-40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", "", "", "11");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
         }
 
@@ -212,6 +244,17 @@ namespace System.Diagnostics.Metrics.Tests
                 };
             });
             Histogram<int> h = meter.CreateHistogram<int>("histogram1");
+            UpDownCounter<int> udc = meter.CreateUpDownCounter<int>("upDownCounter1");
+            int upDownCounterState = 0;
+            ObservableUpDownCounter<int> oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () =>
+            {
+                upDownCounterState -= 11;
+                return new Measurement<int>[]
+                {
+                    new Measurement<int>(upDownCounterState,   new KeyValuePair<string,object?>("Color", "red"),  new KeyValuePair<string,object?>("Size", 19) ),
+                    new Measurement<int>(2*upDownCounterState, new KeyValuePair<string,object?>("Color", "blue"), new KeyValuePair<string,object?>("Size", 4 ) )
+                };
+            });
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter5"))
@@ -222,17 +265,21 @@ namespace System.Diagnostics.Metrics.Tests
                 c.Add(6, new KeyValuePair<string, object?>("Color", "blue"));
                 h.Record(19, new KeyValuePair<string, object?>("Size", 123));
                 h.Record(20, new KeyValuePair<string, object?>("Size", 124));
+                udc.Add(-33, new KeyValuePair<string, object?>("Color", "red"));
+                udc.Add(-34, new KeyValuePair<string, object?>("Color", "blue"));
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
 
                 c.Add(12, new KeyValuePair<string, object?>("Color", "red"));
                 c.Add(13, new KeyValuePair<string, object?>("Color", "blue"));
                 h.Record(26, new KeyValuePair<string, object?>("Size", 123));
                 h.Record(27, new KeyValuePair<string, object?>("Size", 124));
+                udc.Add(40, new KeyValuePair<string, object?>("Color", "red"));
+                udc.Add(41, new KeyValuePair<string, object?>("Color", "blue"));
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "Color=red", "", "5", "12");
             AssertCounterEventsPresent(events, meter.Name, c.Name, "Color=blue", "", "6", "13");
@@ -242,6 +289,10 @@ namespace System.Diagnostics.Metrics.Tests
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "Color=blue,Size=4", "", "18", "36");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=123", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "Size=124", "", "0.5=20;0.95=20;0.99=20", "0.5=27;0.95=27;0.99=27");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "Color=red", "", "-33", "40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "Color=blue", "", "-34", "41");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "Color=red,Size=19", "", "", "-11");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "Color=blue,Size=4", "", "", "-22");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
         }
 
@@ -348,6 +399,23 @@ namespace System.Diagnostics.Metrics.Tests
 
             Histogram<int> h = meter.CreateHistogram<int>("histogram1");
 
+            UpDownCounter<int> udc = meter.CreateUpDownCounter<int>("upDownCounter1");
+            int upDownCounterState = 0;
+            int upDownCounterCollectInterval = 0;
+            ObservableUpDownCounter<int> oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () =>
+            {
+                upDownCounterState += 11;
+                upDownCounterCollectInterval++;
+                if ((upDownCounterCollectInterval % 2) == 0)
+                {
+                    return new Measurement<int>[] { new Measurement<int>(upDownCounterState) };
+                }
+                else
+                {
+                    return new Measurement<int>[0];
+                }
+            });
+
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter6"))
             {
@@ -355,23 +423,27 @@ namespace System.Diagnostics.Metrics.Tests
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 1);
                 c.Add(5);
                 h.Record(19);
+                udc.Add(33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 // no measurements in interval 3
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(40);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 4);
                 // no measurements in interval 5
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 5);
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "0", "12");
             AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "0", "14", "0");
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "18", "", "36", "");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "", "0.5=26;0.95=26;0.99=26", "");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", "", "33", "0", "40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", "", "", "0", "22", "0");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 5);
         }
 
@@ -386,6 +458,9 @@ namespace System.Diagnostics.Metrics.Tests
             int gaugeState = 0;
             ObservableGauge<int> og = meter.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!");
             Histogram<int> h = meter.CreateHistogram<int>("histogram1", "a unit", "the description");
+            UpDownCounter<int> udc = meter.CreateUpDownCounter<int>("upDownCounter1", "udc unit", "udc description");
+            int upDownCounterState = 0;
+            ObservableUpDownCounter<int> oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState += 11; return upDownCounterState; }, "oudc unit", "oudc description");
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter7"))
@@ -393,9 +468,11 @@ namespace System.Diagnostics.Metrics.Tests
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 1);
                 c.Add(5);
                 h.Record(19);
+                udc.Add(33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(40);
 
                 // some alternate listener attempts to listen in the middle
                 using MetricsEventListener listener2 = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "ADifferentMeter");
@@ -406,11 +483,13 @@ namespace System.Diagnostics.Metrics.Tests
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "", c.Unit, "5", "12");
             AssertCounterEventsPresent(events, meter.Name, oc.Name, "", oc.Unit, "", "7", "7");
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18", "27");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", udc.Unit, "33", "40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", oudc.Unit, "", "11");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
         }
 
@@ -426,6 +505,9 @@ namespace System.Diagnostics.Metrics.Tests
             int gaugeState = 0;
             ObservableGauge<int> og = meterA.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!");
             Histogram<int> h = meterB.CreateHistogram<int>("histogram1", "a unit", "the description");
+            UpDownCounter<int> udc = meterA.CreateUpDownCounter<int>("upDownCounter1", "udc unit", "udc description");
+            int upDownCounterState = 0;
+            ObservableUpDownCounter<int> oudc = meterA.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState += 11; return upDownCounterState; }, "oudc unit", "oudc description");
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter8;TestMeter9"))
@@ -433,9 +515,11 @@ namespace System.Diagnostics.Metrics.Tests
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 1);
                 c.Add(5);
                 h.Record(19);
+                udc.Add(33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(40);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
 
                 meterA.Dispose();
@@ -446,14 +530,16 @@ namespace System.Diagnostics.Metrics.Tests
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meterA.Name, c.Name, "", c.Unit, "5", "12");
             AssertCounterEventsPresent(events, meterA.Name, oc.Name, "", oc.Unit, "", "7", "7");
             AssertGaugeEventsPresent(events, meterA.Name, og.Name, "", og.Unit, "9", "18", "27");
             AssertHistogramEventsPresent(events, meterB.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26", "0.5=21;0.95=21;0.99=21");
+            AssertUpDownCounterEventsPresent(events, meterA.Name, udc.Name, "", udc.Unit, "33", "40");
+            AssertUpDownCounterEventsPresent(events, meterA.Name, oudc.Name, "", oudc.Unit, "", "11", "11");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 4);
-            AssertEndInstrumentReportingEventsPresent(events, c, oc, og);
+            AssertEndInstrumentReportingEventsPresent(events, c, oc, og, udc, oudc);
         }
 
 
@@ -469,6 +555,9 @@ namespace System.Diagnostics.Metrics.Tests
             int gaugeState = 0;
             ObservableGauge<int> og = meterA.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; }, "12394923 asd [],;/", "junk!");
             Histogram<int> h = meterB.CreateHistogram<int>("histogram1", "a unit", "the description");
+            UpDownCounter<int> udc = meterA.CreateUpDownCounter<int>("upDownCounter1", "udc unit", "udc description");
+            int upDownCounterState = 0;
+            ObservableUpDownCounter<int> oudc = meterA.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState += 11; return upDownCounterState; }, "oudc unit", "oudc description");
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.InstrumentPublishing, null, ""))
@@ -477,7 +566,7 @@ namespace System.Diagnostics.Metrics.Tests
                 events = listener.Events.ToArray();
             }
 
-            AssertInstrumentPublishingEventsPresent(events, c, oc, og, h);
+            AssertInstrumentPublishingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
         }
 
@@ -553,7 +642,6 @@ namespace System.Diagnostics.Metrics.Tests
         {
             using Meter meter = new Meter("TestMeter13");
             Counter<int> c = meter.CreateCounter<int>("counter1");
-            
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, 2, 50, "TestMeter13"))
@@ -659,6 +747,9 @@ namespace System.Diagnostics.Metrics.Tests
             int gaugeState = 0;
             ObservableGauge<int> og = meter.CreateObservableGauge<int>("observableGauge1", () => { gaugeState += 9; return gaugeState; });
             Histogram<int> h = meter.CreateHistogram<int>("histogram1");
+            UpDownCounter<int> udc = meter.CreateUpDownCounter<int>("upDownCounter1");
+            int upDownCounterState = 0;
+            ObservableUpDownCounter<int> oudc = meter.CreateObservableUpDownCounter<int>("observableUpDownCounter1", () => { upDownCounterState += 11; return upDownCounterState; });
 
             EventWrittenEventArgs[] events;
             using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter16"))
@@ -666,19 +757,23 @@ namespace System.Diagnostics.Metrics.Tests
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 1);
                 c.Add(5);
                 h.Record(19);
+                udc.Add(33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(40);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12");
             AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7");
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", "", "33", "40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", "", "", "11");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
 
             // Now create a new listener and do everything a 2nd time. Because the listener above has been disposed the source should be
@@ -689,19 +784,23 @@ namespace System.Diagnostics.Metrics.Tests
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 1);
                 c.Add(5);
                 h.Record(19);
+                udc.Add(33);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 2);
                 c.Add(12);
                 h.Record(26);
+                udc.Add(40);
                 listener.WaitForCollectionStop(s_waitForEventTimeout, 3);
                 events = listener.Events.ToArray();
             }
 
-            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h);
+            AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h, udc, oudc);
             AssertInitialEnumerationCompleteEventPresent(events);
             AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12");
             AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7");
             AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "36", "45");
             AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26");
+            AssertUpDownCounterEventsPresent(events, meter.Name, udc.Name, "", "", "33", "40");
+            AssertUpDownCounterEventsPresent(events, meter.Name, oudc.Name, "", "", "", "11");
             AssertCollectStartStopEventsPresent(events, IntervalSecs, 3);
         }
 
@@ -847,7 +946,19 @@ namespace System.Diagnostics.Metrics.Tests
         private void AssertCounterEventsPresent(EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags,
             string expectedUnit, params string[] expectedRates)
         {
-            var counterEvents = events.Where(e => e.EventName == "CounterRateValuePublished").Select(e =>
+            AssertGenericCounterEventsPresent("CounterRateValuePublished", events, meterName, instrumentName, tags, expectedUnit, expectedRates);
+        }
+
+        private void AssertUpDownCounterEventsPresent(EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags,
+            string expectedUnit, params string[] expectedRates)
+        {
+            AssertGenericCounterEventsPresent("UpDownCounterRateValuePublished", events, meterName, instrumentName, tags, expectedUnit, expectedRates);
+        }
+
+        private void AssertGenericCounterEventsPresent(string eventName, EventWrittenEventArgs[] events, string meterName, string instrumentName, string tags,
+            string expectedUnit, params string[] expectedRates)
+        {
+            var counterEvents = events.Where(e => e.EventName == eventName).Select(e =>
                 new
                 {
                     MeterName = e.Payload[1].ToString(),
index 8e5e14b..6f9438e 100644 (file)
@@ -849,6 +849,9 @@ namespace System.Diagnostics.Metrics.Tests
                 counter.Add(1);
                 Assert.Equal(6, count);
 
+                upDownCounter.Add(-1);
+                Assert.Equal(6, count);
+
                 histogram.Record(1);
                 Assert.Equal(6, count);