Skip to content

Commit c22d6c5

Browse files
authored
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
1 parent ddbcaa3 commit c22d6c5

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ public GaugePayload(string providerName, string name, string displayName, string
8686
}
8787
}
8888

89+
internal class UpDownCounterPayload : CounterPayload
90+
{
91+
public UpDownCounterPayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) :
92+
base(providerName, name, metadata, value, timestamp, "Metric", EventType.UpDownCounter)
93+
{
94+
// In case these properties are not provided, set them to appropriate values.
95+
string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
96+
DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName;
97+
}
98+
}
99+
89100
internal class CounterEndedPayload : CounterPayload
90101
{
91102
public CounterEndedPayload(string providerName, string name, DateTime timestamp)
@@ -144,6 +155,7 @@ internal enum EventType : int
144155
Rate,
145156
Gauge,
146157
Histogram,
158+
UpDownCounter,
147159
Error,
148160
CounterEnded
149161
}

src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilte
9090
{
9191
HandleCounterRate(traceEvent, filter, sessionId, out payload);
9292
}
93+
else if (traceEvent.EventName == "UpDownCounterRateValuePublished")
94+
{
95+
HandleUpDownCounterValue(traceEvent, filter, sessionId, out payload);
96+
}
9397
else if (traceEvent.EventName == "TimeSeriesLimitReached")
9498
{
9599
HandleTimeSeriesLimitReached(traceEvent, sessionId, out payload);
@@ -183,7 +187,47 @@ private static void HandleCounterRate(TraceEvent traceEvent, CounterFilter filte
183187
else
184188
{
185189
// for observable instruments we assume the lack of data is meaningful and remove it from the UI
186-
// this happens when the ObservableCounter callback function throws an exception.
190+
// this happens when the ObservableCounter callback function throws an exception
191+
// or when the ObservableCounter doesn't include a measurement for a particular set of tag values.
192+
payload = new CounterEndedPayload(meterName, instrumentName, traceEvent.TimeStamp);
193+
}
194+
}
195+
196+
private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterFilter filter, string sessionId, out ICounterPayload payload)
197+
{
198+
payload = null;
199+
200+
string payloadSessionId = (string)traceEvent.PayloadValue(0);
201+
202+
if (payloadSessionId != sessionId || traceEvent.Version < 1) // Version 1 added the value field.
203+
{
204+
return;
205+
}
206+
207+
string meterName = (string)traceEvent.PayloadValue(1);
208+
//string meterVersion = (string)obj.PayloadValue(2);
209+
string instrumentName = (string)traceEvent.PayloadValue(3);
210+
string unit = (string)traceEvent.PayloadValue(4);
211+
string tags = (string)traceEvent.PayloadValue(5);
212+
//string rateText = (string)traceEvent.PayloadValue(6); // Not currently using rate for UpDownCounters.
213+
string valueText = (string)traceEvent.PayloadValue(7);
214+
215+
if (!filter.IsIncluded(meterName, instrumentName))
216+
{
217+
return;
218+
}
219+
220+
if (double.TryParse(valueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
221+
{
222+
// UpDownCounter reports the value, not the rate - this is different than how Counter behaves.
223+
payload = new UpDownCounterPayload(meterName, instrumentName, null, unit, tags, value, traceEvent.TimeStamp);
224+
225+
}
226+
else
227+
{
228+
// for observable instruments we assume the lack of data is meaningful and remove it from the UI
229+
// this happens when the ObservableUpDownCounter callback function throws an exception
230+
// or when the ObservableUpDownCounter doesn't include a measurement for a particular set of tag values.
187231
payload = new CounterEndedPayload(meterName, instrumentName, traceEvent.TimeStamp);
188232
}
189233
}

src/Tools/dotnet-counters/CounterMonitor.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ private void DynamicAllMonitor(TraceEvent obj)
8888
{
8989
HandleCounterRate(obj);
9090
}
91+
else if (obj.EventName == "UpDownCounterRateValuePublished")
92+
{
93+
HandleUpDownCounterValue(obj);
94+
}
9195
else if (obj.EventName == "TimeSeriesLimitReached")
9296
{
9397
HandleTimeSeriesLimitReached(obj);
@@ -198,6 +202,42 @@ private void HandleGauge(TraceEvent obj)
198202
}
199203
}
200204

205+
private void HandleUpDownCounterValue(TraceEvent obj)
206+
{
207+
if (obj.Version < 1) // Version 1 added the value field.
208+
{
209+
return;
210+
}
211+
212+
string sessionId = (string)obj.PayloadValue(0);
213+
string meterName = (string)obj.PayloadValue(1);
214+
//string meterVersion = (string)obj.PayloadValue(2);
215+
string instrumentName = (string)obj.PayloadValue(3);
216+
string unit = (string)obj.PayloadValue(4);
217+
string tags = (string)obj.PayloadValue(5);
218+
//string rateText = (string)obj.PayloadValue(6); // Not currently using rate for UpDownCounters.
219+
string valueText = (string)obj.PayloadValue(7);
220+
if (sessionId != _metricsEventSourceSessionId)
221+
{
222+
return;
223+
}
224+
MeterInstrumentEventObserved(meterName, obj.TimeStamp);
225+
226+
// the value might be an empty string indicating no measurement was provided this collection interval
227+
if (double.TryParse(valueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
228+
{
229+
// UpDownCounter reports the value, not the rate - this is different than how Counter behaves, and is thus treated as a gauge.
230+
CounterPayload payload = new GaugePayload(meterName, instrumentName, null, unit, tags, value, obj.TimeStamp);
231+
_renderer.CounterPayloadReceived(payload, _pauseCmdSet);
232+
}
233+
else
234+
{
235+
// for observable instruments we assume the lack of data is meaningful and remove it from the UI
236+
CounterPayload payload = new RatePayload(meterName, instrumentName, null, unit, tags, 0, _interval, obj.TimeStamp);
237+
_renderer.CounterStopped(payload);
238+
}
239+
}
240+
201241
private void HandleHistogram(TraceEvent obj)
202242
{
203243
string sessionId = (string)obj.PayloadValue(0);

0 commit comments

Comments
 (0)