From: kkeirstead <85592574+kkeirstead@users.noreply.github.com> Date: Tue, 2 May 2023 16:55:11 +0000 (-0700) Subject: Enable UpDownCounter For Dotnet-Counters and Dotnet-Monitor (#3849) X-Git-Tag: accepted/tizen/unified/riscv/20231226.055542~39^2^2~70 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c22d6c5612daab1320a22c6c58572bc8e1474ce0;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Enable UpDownCounter For Dotnet-Counters and Dotnet-Monitor (#3849) * Use value from UDC event payload. * Adding support for UpDownCounter reporting a value instead of a rate * Check for payload version, don't attempt to parse if version 0 * Don't get rate for updowncounter payload --- diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs index 18eb5264e..99e303008 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs @@ -86,6 +86,17 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe } } + internal class UpDownCounterPayload : CounterPayload + { + public UpDownCounterPayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) : + base(providerName, name, metadata, value, timestamp, "Metric", EventType.UpDownCounter) + { + // In case these properties are not provided, set them to appropriate values. + string counterName = string.IsNullOrEmpty(displayName) ? name : displayName; + DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName; + } + } + internal class CounterEndedPayload : CounterPayload { public CounterEndedPayload(string providerName, string name, DateTime timestamp) @@ -144,6 +155,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe Rate, Gauge, Histogram, + UpDownCounter, Error, CounterEnded } diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs index eedc106d1..347a4e11f 100644 --- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs +++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs @@ -90,6 +90,10 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe { HandleCounterRate(traceEvent, filter, sessionId, out payload); } + else if (traceEvent.EventName == "UpDownCounterRateValuePublished") + { + HandleUpDownCounterValue(traceEvent, filter, sessionId, out payload); + } else if (traceEvent.EventName == "TimeSeriesLimitReached") { HandleTimeSeriesLimitReached(traceEvent, sessionId, out payload); @@ -183,7 +187,47 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe else { // for observable instruments we assume the lack of data is meaningful and remove it from the UI - // this happens when the ObservableCounter callback function throws an exception. + // this happens when the ObservableCounter callback function throws an exception + // or when the ObservableCounter doesn't include a measurement for a particular set of tag values. + payload = new CounterEndedPayload(meterName, instrumentName, traceEvent.TimeStamp); + } + } + + private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterFilter filter, string sessionId, out ICounterPayload payload) + { + payload = null; + + string payloadSessionId = (string)traceEvent.PayloadValue(0); + + if (payloadSessionId != sessionId || traceEvent.Version < 1) // Version 1 added the value field. + { + return; + } + + string meterName = (string)traceEvent.PayloadValue(1); + //string meterVersion = (string)obj.PayloadValue(2); + string instrumentName = (string)traceEvent.PayloadValue(3); + string unit = (string)traceEvent.PayloadValue(4); + string tags = (string)traceEvent.PayloadValue(5); + //string rateText = (string)traceEvent.PayloadValue(6); // Not currently using rate for UpDownCounters. + string valueText = (string)traceEvent.PayloadValue(7); + + if (!filter.IsIncluded(meterName, instrumentName)) + { + return; + } + + if (double.TryParse(valueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value)) + { + // UpDownCounter reports the value, not the rate - this is different than how Counter behaves. + payload = new UpDownCounterPayload(meterName, instrumentName, null, unit, tags, value, traceEvent.TimeStamp); + + } + else + { + // for observable instruments we assume the lack of data is meaningful and remove it from the UI + // this happens when the ObservableUpDownCounter callback function throws an exception + // or when the ObservableUpDownCounter doesn't include a measurement for a particular set of tag values. payload = new CounterEndedPayload(meterName, instrumentName, traceEvent.TimeStamp); } } diff --git a/src/Tools/dotnet-counters/CounterMonitor.cs b/src/Tools/dotnet-counters/CounterMonitor.cs index 928664d33..a01707438 100644 --- a/src/Tools/dotnet-counters/CounterMonitor.cs +++ b/src/Tools/dotnet-counters/CounterMonitor.cs @@ -88,6 +88,10 @@ namespace Microsoft.Diagnostics.Tools.Counters { HandleCounterRate(obj); } + else if (obj.EventName == "UpDownCounterRateValuePublished") + { + HandleUpDownCounterValue(obj); + } else if (obj.EventName == "TimeSeriesLimitReached") { HandleTimeSeriesLimitReached(obj); @@ -198,6 +202,42 @@ namespace Microsoft.Diagnostics.Tools.Counters } } + private void HandleUpDownCounterValue(TraceEvent obj) + { + if (obj.Version < 1) // Version 1 added the value field. + { + return; + } + + string sessionId = (string)obj.PayloadValue(0); + string meterName = (string)obj.PayloadValue(1); + //string meterVersion = (string)obj.PayloadValue(2); + string instrumentName = (string)obj.PayloadValue(3); + string unit = (string)obj.PayloadValue(4); + string tags = (string)obj.PayloadValue(5); + //string rateText = (string)obj.PayloadValue(6); // Not currently using rate for UpDownCounters. + string valueText = (string)obj.PayloadValue(7); + if (sessionId != _metricsEventSourceSessionId) + { + return; + } + MeterInstrumentEventObserved(meterName, obj.TimeStamp); + + // the value might be an empty string indicating no measurement was provided this collection interval + if (double.TryParse(valueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value)) + { + // UpDownCounter reports the value, not the rate - this is different than how Counter behaves, and is thus treated as a gauge. + CounterPayload payload = new GaugePayload(meterName, instrumentName, null, unit, tags, value, obj.TimeStamp); + _renderer.CounterPayloadReceived(payload, _pauseCmdSet); + } + else + { + // for observable instruments we assume the lack of data is meaningful and remove it from the UI + CounterPayload payload = new RatePayload(meterName, instrumentName, null, unit, tags, 0, _interval, obj.TimeStamp); + _renderer.CounterStopped(payload); + } + } + private void HandleHistogram(TraceEvent obj) { string sessionId = (string)obj.PayloadValue(0);