From fa95cb7ac68cbe9a889b1dff72c8e535a5b13dd8 Mon Sep 17 00:00:00 2001
From: kkeirstead <85592574+kkeirstead@users.noreply.github.com>
Date: Wed, 11 Oct 2023 15:11:49 -0700
Subject: [PATCH] Dotnet Counters + Dotnet Monitor Unification (#4254)
---
.../Counters/CounterPayload.cs | 181 +++--
.../Counters/CounterPayloadExtensions.cs | 21 -
.../Counters/ICounterPayload.cs | 6 +-
.../Counters/MetricsPipeline.cs | 11 +-
.../Counters/TraceEventExtensions.cs | 151 ++--
.../DiagnosticsEventPipeProcessor.cs | 4 +-
.../EventPipeStreamProvider.cs | 13 +-
.../EventSourcePipeline.cs | 2 +-
.../EventSourcePipelineSettings.cs | 2 +
...ft.Diagnostics.Monitoring.EventPipe.csproj | 1 +
.../Trace/EventTracePipeline.cs | 2 +-
.../EventCounter/EventCounterTrigger.cs | 5 +-
.../SystemDiagnosticsMetricsTrigger.cs | 7 +-
.../SystemDiagnosticsMetricsTriggerImpl.cs | 10 +-
.../Microsoft.Diagnostics.Monitoring.csproj | 2 +
src/Tools/dotnet-counters/CounterMonitor.cs | 704 +++++-------------
src/Tools/dotnet-counters/CounterPayload.cs | 63 --
.../CounterPayloadExtensions.cs | 28 +
.../dotnet-counters/Exporters/CSVExporter.cs | 9 +-
.../Exporters/ConsoleWriter.cs | 13 +-
.../Exporters/ICounterRenderer.cs | 4 +-
.../dotnet-counters/Exporters/JSONExporter.cs | 9 +-
src/Tools/dotnet-counters/Program.cs | 4 +-
.../EventCounterPipelineUnitTests.cs | 6 +-
.../EventCounterTriggerTests.cs | 3 +-
src/tests/dotnet-counters/CSVExporterTests.cs | 9 +-
.../dotnet-counters/JSONExporterTests.cs | 12 +-
27 files changed, 535 insertions(+), 747 deletions(-)
delete mode 100644 src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs
delete mode 100644 src/Tools/dotnet-counters/CounterPayload.cs
create mode 100644 src/Tools/dotnet-counters/CounterPayloadExtensions.cs
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs
index 99e303008..850949b08 100644
--- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayload.cs
@@ -7,12 +7,9 @@ using System.Linq;
namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
- ///
- /// TODO This is currently a duplication of the src\Tools\dotnet-counters\CounterPayload.cs stack. The two will be unified in a separate change.
- ///
- internal class CounterPayload : ICounterPayload
+ internal abstract class CounterPayload : ICounterPayload
{
- public CounterPayload(DateTime timestamp,
+ protected CounterPayload(DateTime timestamp,
string provider,
string name,
string displayName,
@@ -20,7 +17,9 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
double value,
CounterType counterType,
float interval,
- string metadata)
+ int series,
+ string metadata,
+ EventType eventType)
{
Timestamp = timestamp;
Name = name;
@@ -30,30 +29,11 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
CounterType = counterType;
Provider = provider;
Interval = interval;
+ Series = series;
Metadata = metadata;
- EventType = EventType.Gauge;
- }
-
- // Copied from dotnet-counters
- public CounterPayload(string providerName,
- string name,
- string metadata,
- double value,
- DateTime timestamp,
- string type,
- EventType eventType)
- {
- Provider = providerName;
- Name = name;
- Metadata = metadata;
- Value = value;
- Timestamp = timestamp;
- CounterType = (CounterType)Enum.Parse(typeof(CounterType), type);
EventType = eventType;
}
- public string Namespace { get; }
-
public string Name { get; }
public string DisplayName { get; protected set; }
@@ -73,12 +53,50 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
public string Metadata { get; }
public EventType EventType { get; set; }
+
+ public virtual bool IsMeter => false;
+
+ public int Series { get; }
+ }
+
+ internal sealed class EventCounterPayload : CounterPayload
+ {
+ public EventCounterPayload(DateTime timestamp,
+ string provider,
+ string name,
+ string displayName,
+ string unit,
+ double value,
+ CounterType counterType,
+ float interval,
+ int series,
+ string metadata) : base(timestamp, provider, name, displayName, unit, value, counterType, interval, series, metadata, EventType.Gauge)
+ {
+ }
+ }
+
+ internal abstract class MeterPayload : CounterPayload
+ {
+ protected MeterPayload(DateTime timestamp,
+ string provider,
+ string name,
+ string displayName,
+ string unit,
+ double value,
+ CounterType counterType,
+ string metadata,
+ EventType eventType)
+ : base(timestamp, provider, name, displayName, unit, value, counterType, 0.0f, 0, metadata, eventType)
+ {
+ }
+
+ public override bool IsMeter => true;
}
- internal class GaugePayload : CounterPayload
+ internal sealed class GaugePayload : MeterPayload
{
public GaugePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) :
- base(providerName, name, metadata, value, timestamp, "Metric", EventType.Gauge)
+ base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Metric, metadata, EventType.Gauge)
{
// In case these properties are not provided, set them to appropriate values.
string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
@@ -86,10 +104,10 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
}
}
- internal class UpDownCounterPayload : CounterPayload
+ internal class UpDownCounterPayload : MeterPayload
{
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)
+ base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Metric, metadata, EventType.UpDownCounter)
{
// In case these properties are not provided, set them to appropriate values.
string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
@@ -97,19 +115,26 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
}
}
- internal class CounterEndedPayload : CounterPayload
+ internal sealed class BeginInstrumentReportingPayload : MeterPayload
{
- public CounterEndedPayload(string providerName, string name, DateTime timestamp)
- : base(providerName, name, null, 0.0, timestamp, "Metric", EventType.CounterEnded)
+ public BeginInstrumentReportingPayload(string providerName, string name, DateTime timestamp)
+ : base(timestamp, providerName, name, string.Empty, string.Empty, 0.0, CounterType.Metric, null, EventType.BeginInstrumentReporting)
{
+ }
+ }
+ internal sealed class CounterEndedPayload : MeterPayload
+ {
+ public CounterEndedPayload(string providerName, string name, DateTime timestamp)
+ : base(timestamp, providerName, name, string.Empty, string.Empty, 0.0, CounterType.Metric, null, EventType.CounterEnded)
+ {
}
}
- internal class RatePayload : CounterPayload
+ internal sealed class RatePayload : MeterPayload
{
public RatePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, double intervalSecs, DateTime timestamp) :
- base(providerName, name, metadata, value, timestamp, "Rate", EventType.Rate)
+ base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Rate, metadata, EventType.Rate)
{
// In case these properties are not provided, set them to appropriate values.
string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
@@ -119,35 +144,46 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
}
}
- internal class PercentilePayload : CounterPayload
+ internal record struct Quantile(double Percentage, double Value);
+
+ internal sealed class PercentilePayload : MeterPayload
{
- public PercentilePayload(string providerName, string name, string displayName, string displayUnits, string metadata, IEnumerable quantiles, DateTime timestamp) :
- base(providerName, name, metadata, 0.0, timestamp, "Metric", EventType.Histogram)
+ public PercentilePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) :
+ base(timestamp, providerName, name, displayName, displayUnits, value, CounterType.Metric, metadata, EventType.Histogram)
{
// 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;
- Quantiles = quantiles.ToArray();
}
-
- public Quantile[] Quantiles { get; }
}
- internal record struct Quantile(double Percentage, double Value);
-
- internal class ErrorPayload : CounterPayload
+ // Dotnet-monitor and dotnet-counters previously had incompatible PercentilePayload implementations before being unified -
+ // Dotnet-monitor created a single payload that contained all of the quantiles to keep them together, whereas
+ // dotnet-counters created a separate payload for each quantile (multiple payloads per TraceEvent).
+ // AggregatePercentilePayload allows dotnet-monitor to construct a PercentilePayload for individual quantiles
+ // like dotnet-counters, while still keeping the quantiles together as a unit.
+ internal sealed class AggregatePercentilePayload : MeterPayload
{
- public ErrorPayload(string errorMessage) : this(errorMessage, DateTime.UtcNow)
+ public AggregatePercentilePayload(string providerName, string name, string displayName, string displayUnits, string metadata, IEnumerable quantiles, DateTime timestamp) :
+ base(timestamp, providerName, name, displayName, displayUnits, 0.0, CounterType.Metric, metadata, EventType.Histogram)
{
+ //string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
+ //DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName;
+ Quantiles = quantiles.ToArray();
}
- public ErrorPayload(string errorMessage, DateTime timestamp) :
- base(string.Empty, string.Empty, null, 0.0, timestamp, "Metric", EventType.Error)
+ public Quantile[] Quantiles { get; }
+ }
+
+ internal sealed class ErrorPayload : MeterPayload
+ {
+ public ErrorPayload(string errorMessage, DateTime timestamp, EventType eventType)
+ : base(timestamp, string.Empty, string.Empty, string.Empty, string.Empty, 0.0, CounterType.Metric, null, eventType)
{
ErrorMessage = errorMessage;
}
- public string ErrorMessage { get; private set; }
+ public string ErrorMessage { get; }
}
internal enum EventType : int
@@ -156,7 +192,52 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
Gauge,
Histogram,
UpDownCounter,
- Error,
- CounterEnded
+ BeginInstrumentReporting,
+ CounterEnded,
+ HistogramLimitError,
+ TimeSeriesLimitError,
+ ErrorTargetProcess,
+ MultipleSessionsNotSupportedError,
+ MultipleSessionsConfiguredIncorrectlyError,
+ ObservableInstrumentCallbackError
+ }
+
+ internal static class EventTypeExtensions
+ {
+ public static bool IsValuePublishedEvent(this EventType eventType)
+ {
+ return eventType is EventType.Gauge
+ || eventType is EventType.Rate
+ || eventType is EventType.Histogram
+ || eventType is EventType.UpDownCounter;
+ }
+
+ public static bool IsError(this EventType eventType)
+ {
+ return eventType is EventType.HistogramLimitError
+ || eventType is EventType.TimeSeriesLimitError
+ || eventType is EventType.ErrorTargetProcess
+ || eventType is EventType.MultipleSessionsNotSupportedError
+ || eventType is EventType.MultipleSessionsConfiguredIncorrectlyError
+ || eventType is EventType.ObservableInstrumentCallbackError;
+ }
+
+ public static bool IsNonFatalError(this EventType eventType)
+ {
+ return IsError(eventType)
+ && !IsTracingError(eventType)
+ && !IsSessionStartupError(eventType);
+ }
+
+ public static bool IsTracingError(this EventType eventType)
+ {
+ return eventType is EventType.ErrorTargetProcess;
+ }
+
+ public static bool IsSessionStartupError(this EventType eventType)
+ {
+ return eventType is EventType.MultipleSessionsNotSupportedError
+ || eventType is EventType.MultipleSessionsConfiguredIncorrectlyError;
+ }
}
}
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs
deleted file mode 100644
index 12d4b862e..000000000
--- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/CounterPayloadExtensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.Diagnostics.Monitoring.EventPipe
-{
- internal static class CounterPayloadExtensions
- {
- public static string GetDisplay(this ICounterPayload counterPayload)
- {
- if (counterPayload.CounterType == CounterType.Rate)
- {
- return $"{counterPayload.DisplayName} ({counterPayload.Unit} / {counterPayload.Interval} sec)";
- }
- if (!string.IsNullOrEmpty(counterPayload.Unit))
- {
- return $"{counterPayload.DisplayName} ({counterPayload.Unit})";
- }
- return $"{counterPayload.DisplayName}";
- }
- }
-}
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs
index 4e3ce06f7..4cc5fdb04 100644
--- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/ICounterPayload.cs
@@ -41,6 +41,10 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
///
string Metadata { get; }
- EventType EventType { get; set; }
+ EventType EventType { get; }
+
+ bool IsMeter { get; }
+
+ int Series { get; }
}
}
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs
index 1c3a29ab8..9f8f34c15 100644
--- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/MetricsPipeline.cs
@@ -17,6 +17,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
private readonly CounterFilter _filter;
private string _clientId;
private string _sessionId;
+ private CounterConfiguration _counterConfiguration;
public MetricsPipeline(DiagnosticsClient client,
MetricsPipelineSettings settings,
@@ -51,6 +52,14 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
_clientId = config.ClientId;
_sessionId = config.SessionId;
+ _counterConfiguration = new CounterConfiguration(_filter)
+ {
+ SessionId = _sessionId,
+ ClientId = _clientId,
+ MaxHistograms = Settings.MaxHistograms,
+ MaxTimeseries = Settings.MaxTimeSeries
+ };
+
return config;
}
@@ -61,7 +70,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
eventSource.Dynamic.All += traceEvent => {
try
{
- if (traceEvent.TryGetCounterPayload(_filter, _sessionId, _clientId, out ICounterPayload counterPayload))
+ if (traceEvent.TryGetCounterPayload(_counterConfiguration, out ICounterPayload counterPayload))
{
ExecuteCounterLoggerAction((metricLogger) => metricLogger.Log(counterPayload));
}
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs
index 942a7ebdd..06472cf3f 100644
--- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/Counters/TraceEventExtensions.cs
@@ -9,11 +9,29 @@ using Microsoft.Diagnostics.Tracing;
namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
+ internal class CounterConfiguration
+ {
+ public CounterConfiguration(CounterFilter filter)
+ {
+ CounterFilter = filter ?? throw new ArgumentNullException(nameof(filter));
+ }
+
+ public CounterFilter CounterFilter { get; }
+
+ public string SessionId { get; set; }
+
+ public string ClientId { get; set; }
+
+ public int MaxHistograms { get; set; }
+
+ public int MaxTimeseries { get; set; }
+ }
+
internal static class TraceEventExtensions
{
private static HashSet inactiveSharedSessions = new(StringComparer.OrdinalIgnoreCase);
- public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterFilter filter, string sessionId, string clientId, out ICounterPayload payload)
+ public static bool TryGetCounterPayload(this TraceEvent traceEvent, CounterConfiguration counterConfiguration, out ICounterPayload payload)
{
payload = null;
@@ -27,12 +45,12 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
string counterName = payloadFields["Name"].ToString();
string metadata = payloadFields["Metadata"].ToString();
-
+ int seriesValue = GetInterval(series);
//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)))
+ if (!counterConfiguration.CounterFilter.IsIncluded(traceEvent.ProviderName, counterName, seriesValue))
{
return false;
}
@@ -61,7 +79,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
// Note that dimensional data such as pod and namespace are automatically added in prometheus and azure monitor scenarios.
// We no longer added it here.
- payload = new CounterPayload(
+ payload = new EventCounterPayload(
traceEvent.TimeStamp,
traceEvent.ProviderName,
counterName, displayName,
@@ -69,57 +87,57 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
value,
counterType,
intervalSec,
+ seriesValue / 1000,
metadata);
return true;
}
- if (clientId != null && !inactiveSharedSessions.Contains(clientId) && MonitoringSourceConfiguration.SystemDiagnosticsMetricsProviderName.Equals(traceEvent.ProviderName))
+ if (counterConfiguration.ClientId != null && !inactiveSharedSessions.Contains(counterConfiguration.ClientId) && MonitoringSourceConfiguration.SystemDiagnosticsMetricsProviderName.Equals(traceEvent.ProviderName))
{
if (traceEvent.EventName == "BeginInstrumentReporting")
{
- // Do we want to log something for this?
- //HandleBeginInstrumentReporting(traceEvent);
+ HandleBeginInstrumentReporting(traceEvent, counterConfiguration, out payload);
}
if (traceEvent.EventName == "HistogramValuePublished")
{
- HandleHistogram(traceEvent, filter, sessionId, out payload);
+ HandleHistogram(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "GaugeValuePublished")
{
- HandleGauge(traceEvent, filter, sessionId, out payload);
+ HandleGauge(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "CounterRateValuePublished")
{
- HandleCounterRate(traceEvent, filter, sessionId, out payload);
+ HandleCounterRate(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "UpDownCounterRateValuePublished")
{
- HandleUpDownCounterValue(traceEvent, filter, sessionId, out payload);
+ HandleUpDownCounterValue(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "TimeSeriesLimitReached")
{
- HandleTimeSeriesLimitReached(traceEvent, sessionId, out payload);
+ HandleTimeSeriesLimitReached(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "HistogramLimitReached")
{
- HandleHistogramLimitReached(traceEvent, sessionId, out payload);
+ HandleHistogramLimitReached(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "Error")
{
- HandleError(traceEvent, sessionId, out payload);
+ HandleError(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "ObservableInstrumentCallbackError")
{
- HandleObservableInstrumentCallbackError(traceEvent, sessionId, out payload);
+ HandleObservableInstrumentCallbackError(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "MultipleSessionsNotSupportedError")
{
- HandleMultipleSessionsNotSupportedError(traceEvent, sessionId, out payload);
+ HandleMultipleSessionsNotSupportedError(traceEvent, counterConfiguration, out payload);
}
else if (traceEvent.EventName == "MultipleSessionsConfiguredIncorrectlyError")
{
- HandleMultipleSessionsConfiguredIncorrectlyError(traceEvent, clientId, out payload);
+ HandleMultipleSessionsConfiguredIncorrectlyError(traceEvent, counterConfiguration.ClientId, out payload);
}
return payload != null;
@@ -128,13 +146,13 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
return false;
}
- private static void HandleGauge(TraceEvent obj, CounterFilter filter, string sessionId, out ICounterPayload payload)
+ private static void HandleGauge(TraceEvent obj, CounterConfiguration counterConfiguration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)obj.PayloadValue(0);
- if (payloadSessionId != sessionId)
+ if (payloadSessionId != counterConfiguration.SessionId)
{
return;
}
@@ -146,7 +164,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
string tags = (string)obj.PayloadValue(5);
string lastValueText = (string)obj.PayloadValue(6);
- if (!filter.IsIncluded(meterName, instrumentName))
+ if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName))
{
return;
}
@@ -164,13 +182,36 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
}
}
- private static void HandleCounterRate(TraceEvent traceEvent, CounterFilter filter, string sessionId, out ICounterPayload payload)
+ private static void HandleBeginInstrumentReporting(TraceEvent traceEvent, CounterConfiguration counterConfiguration, out ICounterPayload payload)
+ {
+ payload = null;
+
+ string payloadSessionId = (string)traceEvent.PayloadValue(0);
+ if (payloadSessionId != counterConfiguration.SessionId)
+ {
+ return;
+ }
+
+ string meterName = (string)traceEvent.PayloadValue(1);
+ //string meterVersion = (string)obj.PayloadValue(2);
+ string instrumentName = (string)traceEvent.PayloadValue(3);
+
+ if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName))
+ {
+ return;
+ }
+
+
+ payload = new BeginInstrumentReportingPayload(meterName, instrumentName, traceEvent.TimeStamp);
+ }
+
+ private static void HandleCounterRate(TraceEvent traceEvent, CounterConfiguration counterConfiguration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)traceEvent.PayloadValue(0);
- if (payloadSessionId != sessionId)
+ if (payloadSessionId != counterConfiguration.SessionId)
{
return;
}
@@ -182,14 +223,14 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
string tags = (string)traceEvent.PayloadValue(5);
string rateText = (string)traceEvent.PayloadValue(6);
- if (!filter.IsIncluded(meterName, instrumentName))
+ if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName))
{
return;
}
- if (double.TryParse(rateText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double rate))
+ if (double.TryParse(rateText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
{
- payload = new RatePayload(meterName, instrumentName, null, unit, tags, rate, filter.DefaultIntervalSeconds, traceEvent.TimeStamp);
+ payload = new RatePayload(meterName, instrumentName, null, unit, tags, value, counterConfiguration.CounterFilter.DefaultIntervalSeconds, traceEvent.TimeStamp);
}
else
{
@@ -200,13 +241,13 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
}
}
- private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterFilter filter, string sessionId, out ICounterPayload payload)
+ private static void HandleUpDownCounterValue(TraceEvent traceEvent, CounterConfiguration configuration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)traceEvent.PayloadValue(0);
- if (payloadSessionId != sessionId || traceEvent.Version < 1) // Version 1 added the value field.
+ if (payloadSessionId != configuration.SessionId || traceEvent.Version < 1) // Version 1 added the value field.
{
return;
}
@@ -219,7 +260,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
//string rateText = (string)traceEvent.PayloadValue(6); // Not currently using rate for UpDownCounters.
string valueText = (string)traceEvent.PayloadValue(7);
- if (!filter.IsIncluded(meterName, instrumentName))
+ if (!configuration.CounterFilter.IsIncluded(meterName, instrumentName))
{
return;
}
@@ -239,12 +280,13 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
}
}
- private static void HandleHistogram(TraceEvent obj, CounterFilter filter, string sessionId, out ICounterPayload payload)
+ private static void HandleHistogram(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)obj.PayloadValue(0);
- if (payloadSessionId != sessionId)
+
+ if (payloadSessionId != configuration.SessionId)
{
return;
}
@@ -256,72 +298,71 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
string tags = (string)obj.PayloadValue(5);
string quantilesText = (string)obj.PayloadValue(6);
- if (!filter.IsIncluded(meterName, instrumentName))
+ if (!configuration.CounterFilter.IsIncluded(meterName, instrumentName))
{
return;
}
//Note quantiles can be empty.
IList quantiles = ParseQuantiles(quantilesText);
- payload = new PercentilePayload(meterName, instrumentName, null, unit, tags, quantiles, obj.TimeStamp);
- }
-
+ payload = new AggregatePercentilePayload(meterName, instrumentName, null, unit, tags, quantiles, obj.TimeStamp);
+ }
- private static void HandleHistogramLimitReached(TraceEvent obj, string sessionId, out ICounterPayload payload)
+ private static void HandleHistogramLimitReached(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)obj.PayloadValue(0);
- if (payloadSessionId != sessionId)
+ if (payloadSessionId != configuration.SessionId)
{
return;
}
- string errorMessage = $"Warning: Histogram tracking limit reached. Not all data is being shown. The limit can be changed with maxHistograms but will use more memory in the target process.";
+ string errorMessage = $"Warning: Histogram tracking limit ({configuration.MaxHistograms}) reached. Not all data is being shown. The limit can be changed but will use more memory in the target process.";
- payload = new ErrorPayload(errorMessage);
+ payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.HistogramLimitError);
}
- private static void HandleTimeSeriesLimitReached(TraceEvent obj, string sessionId, out ICounterPayload payload)
+ private static void HandleTimeSeriesLimitReached(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)obj.PayloadValue(0);
- if (payloadSessionId != sessionId)
+ if (payloadSessionId != configuration.SessionId)
{
return;
}
- string errorMessage = "Warning: Time series tracking limit reached. Not all data is being shown. The limit can be changed with maxTimeSeries but will use more memory in the target process.";
+ string errorMessage = $"Warning: Time series tracking limit ({configuration.MaxTimeseries}) reached. Not all data is being shown. The limit can be changed but will use more memory in the target process.";
- payload = new ErrorPayload(errorMessage, obj.TimeStamp);
+ payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.TimeSeriesLimitError);
}
- private static void HandleError(TraceEvent obj, string sessionId, out ICounterPayload payload)
+ private static void HandleError(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)obj.PayloadValue(0);
string error = (string)obj.PayloadValue(1);
- if (payloadSessionId != sessionId)
+ if (configuration.SessionId != payloadSessionId)
{
return;
}
string errorMessage = "Error reported from target process:" + Environment.NewLine + error;
- payload = new ErrorPayload(errorMessage, obj.TimeStamp);
+ payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.ErrorTargetProcess);
}
- private static void HandleMultipleSessionsNotSupportedError(TraceEvent obj, string sessionId, out ICounterPayload payload)
+ private static void HandleMultipleSessionsNotSupportedError(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)obj.PayloadValue(0);
- if (payloadSessionId == sessionId)
+ if (payloadSessionId == configuration.SessionId)
{
// If our session is the one that is running then the error is not for us,
// it is for some other session that came later
@@ -332,7 +373,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
string errorMessage = "Error: Another metrics collection session is already in progress for the target process." + Environment.NewLine +
"Concurrent sessions are not supported.";
- payload = new ErrorPayload(errorMessage, obj.TimeStamp);
+ payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.MultipleSessionsNotSupportedError);
}
}
@@ -383,20 +424,20 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
if (TryCreateSharedSessionConfiguredIncorrectlyMessage(obj, clientId, out string message))
{
- payload = new ErrorPayload(message.ToString(), obj.TimeStamp);
+ payload = new ErrorPayload(message.ToString(), obj.TimeStamp, EventType.MultipleSessionsConfiguredIncorrectlyError);
inactiveSharedSessions.Add(clientId);
}
}
- private static void HandleObservableInstrumentCallbackError(TraceEvent obj, string sessionId, out ICounterPayload payload)
+ private static void HandleObservableInstrumentCallbackError(TraceEvent obj, CounterConfiguration configuration, out ICounterPayload payload)
{
payload = null;
string payloadSessionId = (string)obj.PayloadValue(0);
string error = (string)obj.PayloadValue(1);
- if (payloadSessionId != sessionId)
+ if (payloadSessionId != configuration.SessionId)
{
return;
}
@@ -404,10 +445,10 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
string errorMessage = "Exception thrown from an observable instrument callback in the target process:" + Environment.NewLine +
error;
- payload = new ErrorPayload(errorMessage, obj.TimeStamp);
+ payload = new ErrorPayload(errorMessage, obj.TimeStamp, EventType.ObservableInstrumentCallbackError);
}
- private static IList ParseQuantiles(string quantileList)
+ private static List ParseQuantiles(string quantileList)
{
string[] quantileParts = quantileList.Split(';', StringSplitOptions.RemoveEmptyEntries);
List quantiles = new();
@@ -418,11 +459,11 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
continue;
}
- if (!double.TryParse(keyValParts[0], out double key))
+ if (!double.TryParse(keyValParts[0], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double key))
{
continue;
}
- if (!double.TryParse(keyValParts[1], out double val))
+ if (!double.TryParse(keyValParts[1], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double val))
{
continue;
}
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs
index 7ebc6409b..cff749c12 100644
--- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/DiagnosticsEventPipeProcessor.cs
@@ -39,7 +39,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
_sessionStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
}
- public async Task Process(DiagnosticsClient client, TimeSpan duration, CancellationToken token)
+ public async Task Process(DiagnosticsClient client, TimeSpan duration, bool resumeRuntime, CancellationToken token)
{
//No need to guard against reentrancy here, since the calling pipeline does this already.
IDisposable registration = token.Register(() => TryCancelCompletionSources(token));
@@ -53,7 +53,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
// Allows the event handling routines to stop processing before the duration expires.
Func stopFunc = () => Task.Run(() => { streamProvider.StopProcessing(); });
- Stream sessionStream = await streamProvider.ProcessEvents(client, duration, token).ConfigureAwait(false);
+ Stream sessionStream = await streamProvider.ProcessEvents(client, duration, resumeRuntime, token).ConfigureAwait(false);
if (!_sessionStarted.TrySetResult(true))
{
diff --git a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs
index 45cf97be4..23bb51b28 100644
--- a/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.EventPipe/EventPipeStreamProvider.cs
@@ -21,7 +21,7 @@ namespace Microsoft.Diagnostics.Monitoring.EventPipe
_stopProcessingSource = new TaskCompletionSource