namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
- /// <summary>
- /// TODO This is currently a duplication of the src\Tools\dotnet-counters\CounterPayload.cs stack. The two will be unified in a separate change.
- /// </summary>
- internal class CounterPayload : ICounterPayload
+ internal abstract class CounterPayload : ICounterPayload
{
- public CounterPayload(DateTime timestamp,
+ protected CounterPayload(DateTime timestamp,
string provider,
string name,
string displayName,
double value,
CounterType counterType,
float interval,
- string metadata)
+ int series,
+ string metadata,
+ EventType eventType)
{
Timestamp = timestamp;
Name = name;
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; }
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;
}
}
- 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;
}
}
- 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;
}
}
- 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<Quantile> 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<Quantile> 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
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;
+ }
}
}
+++ /dev/null
-// 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}";
- }
- }
-}
/// </summary>
string Metadata { get; }
- EventType EventType { get; set; }
+ EventType EventType { get; }
+
+ bool IsMeter { get; }
+
+ int Series { get; }
}
}
private readonly CounterFilter _filter;
private string _clientId;
private string _sessionId;
+ private CounterConfiguration _counterConfiguration;
public MetricsPipeline(DiagnosticsClient client,
MetricsPipelineSettings settings,
_clientId = config.ClientId;
_sessionId = config.SessionId;
+ _counterConfiguration = new CounterConfiguration(_filter)
+ {
+ SessionId = _sessionId,
+ ClientId = _clientId,
+ MaxHistograms = Settings.MaxHistograms,
+ MaxTimeseries = Settings.MaxTimeSeries
+ };
+
return config;
}
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));
}
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<string> 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;
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;
}
// 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,
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;
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;
}
string tags = (string)obj.PayloadValue(5);
string lastValueText = (string)obj.PayloadValue(6);
- if (!filter.IsIncluded(meterName, instrumentName))
+ if (!counterConfiguration.CounterFilter.IsIncluded(meterName, instrumentName))
{
return;
}
}
}
- 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;
}
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
{
}
}
- 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;
}
//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;
}
}
}
- 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;
}
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<Quantile> 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
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);
}
}
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;
}
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<Quantile> ParseQuantiles(string quantileList)
+ private static List<Quantile> ParseQuantiles(string quantileList)
{
string[] quantileParts = quantileList.Split(';', StringSplitOptions.RemoveEmptyEntries);
List<Quantile> quantiles = new();
{
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;
}
_sessionStarted = new TaskCompletionSource<bool>(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));
// Allows the event handling routines to stop processing before the duration expires.
Func<Task> 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))
{
_stopProcessingSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
}
- public async Task<Stream> ProcessEvents(DiagnosticsClient client, TimeSpan duration, CancellationToken cancellationToken)
+ public async Task<Stream> ProcessEvents(DiagnosticsClient client, TimeSpan duration, bool resumeRuntime, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
session = await client.StartEventPipeSessionAsync(_sourceConfig.GetProviders(), _sourceConfig.RequestRundown, _sourceConfig.BufferSizeInMB, cancellationToken).ConfigureAwait(false);
+ if (resumeRuntime)
+ {
+ try
+ {
+ await client.ResumeRuntimeAsync(cancellationToken).ConfigureAwait(false);
+ }
+ catch (UnsupportedCommandException)
+ {
+ // Noop if the command is unknown since the target process is most likely a 3.1 app.
+ }
+ }
}
catch (EndOfStreamException e)
{
{
try
{
- return _processor.Value.Process(Client, Settings.Duration, token);
+ return _processor.Value.Process(Client, Settings.Duration, Settings.ResumeRuntime, token);
}
catch (InvalidOperationException e)
{
internal class EventSourcePipelineSettings
{
public TimeSpan Duration { get; set; }
+
+ public bool ResumeRuntime { get; set; }
}
}
<ItemGroup>
<InternalsVisibleTo Include="dotnet-monitor" />
<InternalsVisibleTo Include="dotnet-counters" />
+ <InternalsVisibleTo Include="DotnetCounters.UnitTests" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.WebApi" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.Tool.UnitTests" />
{
//It is important that the underlying stream be completely read, or disposed.
//If rundown is enabled, the underlying stream must be drained or disposed, or the app hangs.
- using Stream eventStream = await _provider.Value.ProcessEvents(Client, Settings.Duration, token).ConfigureAwait(false);
+ using Stream eventStream = await _provider.Value.ProcessEvents(Client, Settings.Duration, Settings.ResumeRuntime, token).ConfigureAwait(false);
await _onStreamAvailable(eventStream, token).ConfigureAwait(false);
}
private readonly CounterFilter _filter;
private readonly EventCounterTriggerImpl _impl;
private readonly string _providerName;
+ private CounterConfiguration _counterConfiguration;
public EventCounterTrigger(EventCounterTriggerSettings settings)
{
_filter = new CounterFilter(settings.CounterIntervalSeconds);
_filter.AddFilter(settings.ProviderName, new string[] { settings.CounterName });
+ _counterConfiguration = new CounterConfiguration(_filter);
+
_impl = new EventCounterTriggerImpl(settings);
_providerName = settings.ProviderName;
public bool HasSatisfiedCondition(TraceEvent traceEvent)
{
// Filter to the counter of interest before forwarding to the implementation
- if (traceEvent.TryGetCounterPayload(_filter, null, null, out ICounterPayload payload))
+ if (traceEvent.TryGetCounterPayload(_counterConfiguration, out ICounterPayload payload))
{
return _impl.HasSatisfiedCondition(payload);
}
private readonly string _meterName;
private readonly string _clientId;
private readonly string _sessionId;
+ private CounterConfiguration _counterConfiguration;
public SystemDiagnosticsMetricsTrigger(SystemDiagnosticsMetricsTriggerSettings settings)
{
_clientId = settings.ClientId;
_sessionId = settings.SessionId;
+
+ _clientId = settings.ClientId;
+
+ _counterConfiguration = new CounterConfiguration(_filter) { SessionId = _sessionId, ClientId = _clientId };
}
public IReadOnlyDictionary<string, IReadOnlyCollection<string>> GetProviderEventMap()
public bool HasSatisfiedCondition(TraceEvent traceEvent)
{
// Filter to the counter of interest before forwarding to the implementation
- if (traceEvent.TryGetCounterPayload(_filter, _sessionId, _clientId, out ICounterPayload payload))
+ if (traceEvent.TryGetCounterPayload(_counterConfiguration, out ICounterPayload payload))
{
return _impl.HasSatisfiedCondition(payload);
}
{
EventType eventType = payload.EventType;
- if (eventType == EventType.Error || eventType == EventType.CounterEnded)
+ if (eventType.IsError() || eventType == EventType.CounterEnded)
{
// not currently logging the error messages
}
else
{
- bool passesValueFilter = (payload is PercentilePayload percentilePayload) ?
- _valueFilterHistogram(CreatePayloadDictionary(percentilePayload)) :
+ bool passesValueFilter = (payload is AggregatePercentilePayload aggregatePercentilePayload) ?
+ _valueFilterHistogram(CreatePayloadDictionary(aggregatePercentilePayload)) :
_valueFilterDefault(payload.Value);
return SharedTriggerImplHelper.HasSatisfiedCondition(ref _latestTicks, ref _targetTicks, _windowTicks, _intervalTicks, payload, passesValueFilter);
}
}
- private static Dictionary<int, double> CreatePayloadDictionary(PercentilePayload percentilePayload)
+ private static Dictionary<int, double> CreatePayloadDictionary(AggregatePercentilePayload aggregatePercentilePayload)
{
- return percentilePayload.Quantiles.ToDictionary(keySelector: p => CounterUtilities.CreatePercentile(p.Percentage), elementSelector: p => p.Value);
+ return aggregatePercentilePayload.Quantiles.ToDictionary(keySelector: q => CounterUtilities.CreatePercentile(q.Percentage), elementSelector: q => q.Value);
}
}
}
<ItemGroup>
<InternalsVisibleTo Include="dotnet-monitor" />
+ <InternalsVisibleTo Include="dotnet-counters" />
+ <InternalsVisibleTo Include="DotnetCounters.UnitTests" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.EventPipe" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.EventPipe.UnitTests" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.Tool.UnitTests" />
using System.CommandLine;
using System.CommandLine.IO;
using System.CommandLine.Rendering;
+using System.ComponentModel;
using System.Diagnostics;
-using System.Diagnostics.Tracing;
-using System.Globalization;
-using System.IO;
using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Diagnostics.Monitoring;
using Microsoft.Diagnostics.Monitoring.EventPipe;
using Microsoft.Diagnostics.NETCore.Client;
using Microsoft.Diagnostics.Tools.Counters.Exporters;
-using Microsoft.Diagnostics.Tracing;
using Microsoft.Internal.Common.Utils;
namespace Microsoft.Diagnostics.Tools.Counters
{
- public class CounterMonitor
+ internal class CounterMonitor : ICountersLogger
{
private const int BufferDelaySecs = 1;
- private const string SharedSessionId = "SHARED"; // This should be identical to the one used by dotnet-monitor in MetricSourceConfiguration.cs
- private static HashSet<string> inactiveSharedSessions = new(StringComparer.OrdinalIgnoreCase);
-
- private string _sessionId;
private int _processId;
- private int _interval;
private CounterSet _counterList;
- private CancellationToken _ct;
private IConsole _console;
private ICounterRenderer _renderer;
private string _output;
private bool _pauseCmdSet;
- private readonly TaskCompletionSource<int> _shouldExit;
- private bool _resumeRuntime;
+ private readonly TaskCompletionSource<ReturnCode> _shouldExit;
private DiagnosticsClient _diagnosticsClient;
- private EventPipeSession _session;
- private readonly string _clientId;
- private int _maxTimeSeries;
- private int _maxHistograms;
- private TimeSpan _duration;
+ private MetricsPipelineSettings _settings;
private class ProviderEventState
{
public CounterMonitor()
{
_pauseCmdSet = false;
- _clientId = Guid.NewGuid().ToString();
-
- _shouldExit = new TaskCompletionSource<int>();
- }
-
- private void DynamicAllMonitor(TraceEvent obj)
- {
- if (_shouldExit.Task.IsCompleted)
- {
- return;
- }
-
- lock (this)
- {
- // If we are paused, ignore the event.
- // There's a potential race here between the two tasks but not a huge deal if we miss by one event.
- _renderer.ToggleStatus(_pauseCmdSet);
-
- // If a session received a MultipleSessionsConfiguredIncorrectlyError, ignore future shared events
- if (obj.ProviderName == "System.Diagnostics.Metrics" && !inactiveSharedSessions.Contains(_clientId))
- {
- if (obj.EventName == "BeginInstrumentReporting")
- {
- HandleBeginInstrumentReporting(obj);
- }
- if (obj.EventName == "HistogramValuePublished")
- {
- HandleHistogram(obj);
- }
- else if (obj.EventName == "GaugeValuePublished")
- {
- HandleGauge(obj);
- }
- else if (obj.EventName == "CounterRateValuePublished")
- {
- HandleCounterRate(obj);
- }
- else if (obj.EventName == "UpDownCounterRateValuePublished")
- {
- HandleUpDownCounterValue(obj);
- }
- else if (obj.EventName == "TimeSeriesLimitReached")
- {
- HandleTimeSeriesLimitReached(obj);
- }
- else if (obj.EventName == "HistogramLimitReached")
- {
- HandleHistogramLimitReached(obj);
- }
- else if (obj.EventName == "Error")
- {
- HandleError(obj);
- }
- else if (obj.EventName == "ObservableInstrumentCallbackError")
- {
- HandleObservableInstrumentCallbackError(obj);
- }
- else if (obj.EventName == "MultipleSessionsNotSupportedError")
- {
- HandleMultipleSessionsNotSupportedError(obj);
- }
- else if (obj.EventName == "MultipleSessionsConfiguredIncorrectlyError")
- {
- HandleMultipleSessionsConfiguredIncorrectlyError(obj);
- }
- }
- else if (obj.EventName == "EventCounters")
- {
- HandleDiagnosticCounter(obj);
- }
- }
+ _shouldExit = new TaskCompletionSource<ReturnCode>();
}
private void MeterInstrumentEventObserved(string meterName, DateTime timestamp)
}
}
- private void HandleBeginInstrumentReporting(TraceEvent obj)
- {
- string sessionId = (string)obj.PayloadValue(0);
- string meterName = (string)obj.PayloadValue(1);
- // string instrumentName = (string)obj.PayloadValue(3);
- if (sessionId != _sessionId)
- {
- return;
- }
- MeterInstrumentEventObserved(meterName, obj.TimeStamp);
- }
-
- private void HandleCounterRate(TraceEvent obj)
- {
- 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);
- if (sessionId != _sessionId || !Filter(meterName, instrumentName))
- {
- return;
- }
- MeterInstrumentEventObserved(meterName, obj.TimeStamp);
-
- // the value might be an empty string indicating no measurement was provided this collection interval
- if (double.TryParse(rateText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double rate))
- {
- CounterPayload payload = new RatePayload(meterName, instrumentName, null, unit, tags, rate, _interval, obj.TimeStamp);
- _renderer.CounterPayloadReceived(payload, _pauseCmdSet);
- }
-
- }
-
- private void HandleGauge(TraceEvent obj)
- {
- 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 lastValueText = (string)obj.PayloadValue(6);
- if (sessionId != _sessionId || !Filter(meterName, instrumentName))
- {
- return;
- }
- MeterInstrumentEventObserved(meterName, obj.TimeStamp);
-
- // the value might be an empty string indicating no measurement was provided this collection interval
- if (double.TryParse(lastValueText, NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double lastValue))
- {
- CounterPayload payload = new GaugePayload(meterName, instrumentName, null, unit, tags, lastValue, 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 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 != _sessionId || !Filter(meterName, instrumentName))
- {
- 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);
- 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 quantilesText = (string)obj.PayloadValue(6);
- if (sessionId != _sessionId || !Filter(meterName, instrumentName))
- {
- return;
- }
- MeterInstrumentEventObserved(meterName, obj.TimeStamp);
- KeyValuePair<double, double>[] quantiles = ParseQuantiles(quantilesText);
- foreach ((double key, double val) in quantiles)
- {
- CounterPayload payload = new PercentilePayload(meterName, instrumentName, null, unit, AppendQuantile(tags, $"Percentile={key * 100}"), val, obj.TimeStamp);
- _renderer.CounterPayloadReceived(payload, _pauseCmdSet);
- }
- }
-
- private void HandleHistogramLimitReached(TraceEvent obj)
- {
- string sessionId = (string)obj.PayloadValue(0);
- if (sessionId != _clientId)
- {
- return;
- }
- _renderer.SetErrorText(
- $"Warning: Histogram tracking limit ({_maxHistograms}) reached. Not all data is being shown." + Environment.NewLine +
- "The limit can be changed with --maxHistograms but will use more memory in the target process."
- );
- }
-
- private void HandleTimeSeriesLimitReached(TraceEvent obj)
- {
- string sessionId = (string)obj.PayloadValue(0);
- if (sessionId != _sessionId)
- {
- return;
- }
- _renderer.SetErrorText(
- $"Warning: Time series tracking limit ({_maxTimeSeries}) reached. Not all data is being shown." + Environment.NewLine +
- "The limit can be changed with --maxTimeSeries but will use more memory in the target process."
- );
- }
-
- private void HandleError(TraceEvent obj)
- {
- string sessionId = (string)obj.PayloadValue(0);
- string error = (string)obj.PayloadValue(1);
- if (sessionId != _sessionId)
- {
- return;
- }
- _renderer.SetErrorText(
- "Error reported from target process:" + Environment.NewLine +
- error
- );
- _shouldExit.TrySetResult((int)ReturnCode.TracingError);
- }
-
- private void HandleObservableInstrumentCallbackError(TraceEvent obj)
- {
- string sessionId = (string)obj.PayloadValue(0);
- string error = (string)obj.PayloadValue(1);
- if (sessionId != _sessionId)
- {
- return;
- }
- _renderer.SetErrorText(
- "Exception thrown from an observable instrument callback in the target process:" + Environment.NewLine +
- error
- );
- }
-
- private void HandleMultipleSessionsNotSupportedError(TraceEvent obj)
- {
- string runningSessionId = (string)obj.PayloadValue(0);
- if (runningSessionId == _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
- return;
- }
- _renderer.SetErrorText(
- "Error: Another metrics collection session is already in progress for the target process." + Environment.NewLine +
- "Concurrent sessions are not supported.");
- _shouldExit.TrySetResult((int)ReturnCode.SessionCreationError);
- }
-
- private void HandleMultipleSessionsConfiguredIncorrectlyError(TraceEvent obj)
- {
- if (TraceEventExtensions.TryCreateSharedSessionConfiguredIncorrectlyMessage(obj, _clientId, out string message))
- {
- _renderer.SetErrorText(message);
- inactiveSharedSessions.Add(_clientId);
- _shouldExit.TrySetResult((int)ReturnCode.SessionCreationError);
- }
- }
-
- private static KeyValuePair<double, double>[] ParseQuantiles(string quantileList)
- {
- string[] quantileParts = quantileList.Split(';', StringSplitOptions.RemoveEmptyEntries);
- List<KeyValuePair<double, double>> quantiles = new();
- foreach (string quantile in quantileParts)
- {
- string[] keyValParts = quantile.Split('=', StringSplitOptions.RemoveEmptyEntries);
- if (keyValParts.Length != 2)
- {
- continue;
- }
- if (!double.TryParse(keyValParts[0], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double key))
- {
- continue;
- }
- if (!double.TryParse(keyValParts[1], NumberStyles.Number | NumberStyles.Float, CultureInfo.InvariantCulture, out double val))
- {
- continue;
- }
- quantiles.Add(new KeyValuePair<double, double>(key, val));
- }
- return quantiles.ToArray();
- }
-
- private static string AppendQuantile(string tags, string quantile) => string.IsNullOrEmpty(tags) ? quantile : $"{tags},{quantile}";
-
- private void HandleDiagnosticCounter(TraceEvent obj)
+ private void HandleDiagnosticCounter(ICounterPayload payload)
{
- IDictionary<string, object> payloadVal = (IDictionary<string, object>)(obj.PayloadValue(0));
- IDictionary<string, object> payloadFields = (IDictionary<string, object>)(payloadVal["Payload"]);
-
- // If it's not a counter we asked for, ignore it.
- string name = payloadFields["Name"].ToString();
- if (!_counterList.Contains(obj.ProviderName, name))
- {
- return;
- }
-
// init providerEventState if this is the first time we've seen an event from this provider
- if (!_providerEventStates.TryGetValue(obj.ProviderName, out ProviderEventState providerState))
+ if (!_providerEventStates.TryGetValue(payload.Provider, out ProviderEventState providerState))
{
providerState = new ProviderEventState()
{
- FirstReceiveTimestamp = obj.TimeStamp
+ FirstReceiveTimestamp = payload.Timestamp
};
- _providerEventStates.Add(obj.ProviderName, providerState);
+ _providerEventStates.Add(payload.Provider, providerState);
}
// we give precedence to instrument events over diagnostic counter events. If we are seeing
return;
}
- CounterPayload payload;
- if (payloadFields["CounterType"].Equals("Sum"))
- {
- payload = new RatePayload(
- obj.ProviderName,
- name,
- payloadFields["DisplayName"].ToString(),
- payloadFields["DisplayUnits"].ToString(),
- null,
- (double)payloadFields["Increment"],
- _interval,
- obj.TimeStamp);
- }
- else
- {
- payload = new GaugePayload(
- obj.ProviderName,
- name,
- payloadFields["DisplayName"].ToString(),
- payloadFields["DisplayUnits"].ToString(),
- null,
- (double)payloadFields["Mean"],
- obj.TimeStamp);
- }
-
// If we saw the first event for this provider recently then a duplicate instrument event may still be
// coming. We'll buffer this event for a while and then render it if it remains unduplicated for
// a while.
// This is all best effort, if we do show the DiagnosticCounter event and then an instrument event shows up
- // later the renderer may obsserve some odd behavior like changes in the counter metadata, oddly timed reporting
+ // later the renderer may observe some odd behavior like changes in the counter metadata, oddly timed reporting
// intervals, or counters that stop reporting.
// I'm gambling this is good enough that the behavior will never be seen in practice, but if it is we could
// either adjust the time delay or try to improve how the renderers handle it.
- if (providerState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) >= obj.TimeStamp)
+ if (providerState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) >= payload.Timestamp)
+ {
+ _bufferedEvents.Enqueue((CounterPayload)payload);
+ }
+ else
{
- _bufferedEvents.Enqueue(payload);
+ CounterPayloadReceived((CounterPayload)payload);
+ }
+ }
+
+ private void CounterPayloadReceived(CounterPayload payload)
+ {
+ if (payload is AggregatePercentilePayload aggregatePayload)
+ {
+ foreach (Quantile quantile in aggregatePayload.Quantiles)
+ {
+ (double key, double val) = quantile;
+ PercentilePayload percentilePayload = new(payload.Provider, payload.Name, payload.DisplayName, payload.Unit, AppendQuantile(payload.Metadata, $"Percentile={key * 100}"), val, payload.Timestamp);
+ _renderer.CounterPayloadReceived(percentilePayload, _pauseCmdSet);
+ }
+
}
else
{
}
}
+ private static string AppendQuantile(string tags, string quantile) => string.IsNullOrEmpty(tags) ? quantile : $"{tags},{quantile}";
+
// when receiving DiagnosticCounter events we may have buffered them to wait for
// duplicate instrument events. If we've waited long enough then we should remove
// them from the buffer and render them.
while (_bufferedEvents.Count != 0)
{
CounterPayload payload = _bufferedEvents.Peek();
- ProviderEventState providerEventState = _providerEventStates[payload.ProviderName];
+ ProviderEventState providerEventState = _providerEventStates[payload.Provider];
if (providerEventState.InstrumentEventObserved)
{
_bufferedEvents.Dequeue();
else if (providerEventState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) < now)
{
_bufferedEvents.Dequeue();
- _renderer.CounterPayloadReceived(payload, _pauseCmdSet);
+ CounterPayloadReceived((CounterPayload)payload);
}
else
{
}
}
- private void StopMonitor()
- {
- try
- {
- _session?.Stop();
- }
- catch (EndOfStreamException ex)
- {
- // If the app we're monitoring exits abruptly, this may throw in which case we just swallow the exception and exit gracefully.
- Debug.WriteLine($"[ERROR] {ex}");
- }
- // We may time out if the process ended before we sent StopTracing command. We can just exit in that case.
- catch (TimeoutException)
- {
- }
- // On Unix platforms, we may actually get a PNSE since the pipe is gone with the process, and Runtime Client Library
- // does not know how to distinguish a situation where there is no pipe to begin with, or where the process has exited
- // before dotnet-counters and got rid of a pipe that once existed.
- // Since we are catching this in StopMonitor() we know that the pipe once existed (otherwise the exception would've
- // been thrown in StartMonitor directly)
- catch (PlatformNotSupportedException)
- {
- }
- // On non-abrupt exits, the socket may be already closed by the runtime and we won't be able to send a stop request through it.
- catch (ServerNotAvailableException)
- {
- }
- _renderer.Stop();
- }
-
- public async Task<int> Monitor(
+ public async Task<ReturnCode> Monitor(
CancellationToken ct,
List<string> counter_list,
string counters,
ValidateNonNegative(maxTimeSeries, nameof(maxTimeSeries));
if (!ProcessLauncher.Launcher.HasChildProc && !CommandUtils.ValidateArgumentsForAttach(processId, name, diagnosticPort, out _processId))
{
- return (int)ReturnCode.ArgumentError;
+ return ReturnCode.ArgumentError;
}
ct.Register(() => _shouldExit.TrySetResult((int)ReturnCode.Ok));
bool useAnsi = vTerm.IsEnabled;
if (holder == null)
{
- return (int)ReturnCode.Ok;
+ return ReturnCode.Ok;
}
try
{
// the launch command may misinterpret app arguments as the old space separated
// provider list so we need to ignore it in that case
_counterList = ConfigureCounters(counters, _processId != 0 ? counter_list : null);
- _ct = ct;
- _interval = refreshInterval;
- _maxHistograms = maxHistograms;
- _maxTimeSeries = maxTimeSeries;
_renderer = new ConsoleWriter(useAnsi);
_diagnosticsClient = holder.Client;
- _resumeRuntime = resumeRuntime;
- _duration = duration;
- int ret = await Start().ConfigureAwait(false);
+ _settings = new MetricsPipelineSettings();
+ _settings.Duration = duration == TimeSpan.Zero ? Timeout.InfiniteTimeSpan : duration;
+ _settings.MaxHistograms = maxHistograms;
+ _settings.MaxTimeSeries = maxTimeSeries;
+ _settings.CounterIntervalSeconds = refreshInterval;
+ _settings.ResumeRuntime = resumeRuntime;
+ _settings.CounterGroups = GetEventPipeProviders();
+
+ bool useSharedSession = false;
+ if (_diagnosticsClient.GetProcessInfo().TryGetProcessClrVersion(out Version version))
+ {
+ useSharedSession = version.Major >= 8 ? true : false;
+ }
+ _settings.UseSharedSession = useSharedSession;
+
+ ReturnCode ret;
+ MetricsPipeline eventCounterPipeline = new(holder.Client, _settings, new[] { this });
+ await using (eventCounterPipeline.ConfigureAwait(false))
+ {
+ ret = await Start(eventCounterPipeline, ct).ConfigureAwait(false);
+ }
ProcessLauncher.Launcher.Cleanup();
return ret;
}
catch (OperationCanceledException)
{
- try
- {
- _session.Stop();
- }
- catch (Exception) { } // Swallow all exceptions for now.
+ //Cancellation token should automatically stop the session
console.Out.WriteLine($"Complete");
- return (int)ReturnCode.Ok;
+ return ReturnCode.Ok;
}
}
}
catch (CommandLineErrorException e)
{
console.Error.WriteLine(e.Message);
- return (int)ReturnCode.ArgumentError;
+ return ReturnCode.ArgumentError;
}
}
- public async Task<int> Collect(
+ public async Task<ReturnCode> Collect(
CancellationToken ct,
List<string> counter_list,
string counters,
ValidateNonNegative(maxTimeSeries, nameof(maxTimeSeries));
if (!ProcessLauncher.Launcher.HasChildProc && !CommandUtils.ValidateArgumentsForAttach(processId, name, diagnosticPort, out _processId))
{
- return (int)ReturnCode.ArgumentError;
+ return ReturnCode.ArgumentError;
}
ct.Register(() => _shouldExit.TrySetResult((int)ReturnCode.Ok));
// the launch command may misinterpret app arguments as the old space separated
// provider list so we need to ignore it in that case
_counterList = ConfigureCounters(counters, _processId != 0 ? counter_list : null);
- _ct = ct;
- _interval = refreshInterval;
- _maxHistograms = maxHistograms;
- _maxTimeSeries = maxTimeSeries;
+ _settings = new MetricsPipelineSettings();
+ _settings.Duration = duration == TimeSpan.Zero ? Timeout.InfiniteTimeSpan : duration;
+ _settings.MaxHistograms = maxHistograms;
+ _settings.MaxTimeSeries = maxTimeSeries;
+ _settings.CounterIntervalSeconds = refreshInterval;
+ _settings.ResumeRuntime = resumeRuntime;
+ _settings.CounterGroups = GetEventPipeProviders();
_output = output;
_diagnosticsClient = holder.Client;
- _duration = duration;
if (_output.Length == 0)
{
_console.Error.WriteLine("Output cannot be an empty string");
- return (int)ReturnCode.ArgumentError;
+ return ReturnCode.ArgumentError;
}
if (format == CountersExportFormat.csv)
{
else
{
_console.Error.WriteLine($"The output format {format} is not a valid output format.");
- return (int)ReturnCode.ArgumentError;
+ return ReturnCode.ArgumentError;
}
- _resumeRuntime = resumeRuntime;
- int ret = await Start().ConfigureAwait(false);
+
+ ReturnCode ret;
+ MetricsPipeline eventCounterPipeline = new(holder.Client, _settings, new[] { this });
+ await using (eventCounterPipeline.ConfigureAwait(false))
+ {
+ ret = await Start(pipeline: eventCounterPipeline, ct).ConfigureAwait(false);
+ }
+
return ret;
}
catch (OperationCanceledException)
{
- try
- {
- _session.Stop();
- }
- catch (Exception) { } // session.Stop() can throw if target application already stopped before we send the stop command.
- return (int)ReturnCode.Ok;
+ //Cancellation token should automatically stop the session
+ return ReturnCode.Ok;
}
}
}
catch (CommandLineErrorException e)
{
console.Error.WriteLine(e.Message);
- return (int)ReturnCode.ArgumentError;
+ return ReturnCode.ArgumentError;
}
}
}
}
- private EventPipeProvider[] GetEventPipeProviders()
- {
- // EventSources support EventCounter based metrics directly
- IEnumerable<EventPipeProvider> eventCounterProviders = _counterList.Providers.Select(
- providerName => new EventPipeProvider(providerName, EventLevel.Error, 0, new Dictionary<string, string>()
- {{ "EventCounterIntervalSec", _interval.ToString() }}));
-
- //System.Diagnostics.Metrics EventSource supports the new Meter/Instrument APIs
- const long TimeSeriesValues = 0x2;
- StringBuilder metrics = new();
- foreach (string provider in _counterList.Providers)
+ private EventPipeCounterGroup[] GetEventPipeProviders() =>
+ _counterList.Providers.Select(provider => new EventPipeCounterGroup
{
- if (metrics.Length != 0)
- {
- metrics.Append(',');
- }
- if (_counterList.IncludesAllCounters(provider))
- {
- metrics.Append(provider);
- }
- else
- {
- string[] providerCounters = _counterList.GetCounters(provider).Select(counter => $"{provider}\\{counter}").ToArray();
- metrics.Append(string.Join(',', providerCounters));
- }
- }
+ ProviderName = provider,
+ CounterNames = _counterList.GetCounters(provider).ToArray()
+ }).ToArray();
- // Shared Session Id was added in 8.0 - older runtimes will not properly support it.
- _sessionId = Guid.NewGuid().ToString();
- if (_diagnosticsClient.GetProcessInfo().TryGetProcessClrVersion(out Version version))
- {
- _sessionId = version.Major >= 8 ? SharedSessionId : _sessionId;
- }
-
- EventPipeProvider metricsEventSourceProvider =
- new("System.Diagnostics.Metrics", EventLevel.Informational, TimeSeriesValues,
- new Dictionary<string, string>()
- {
- { "SessionId", _sessionId },
- { "Metrics", metrics.ToString() },
- { "RefreshInterval", _interval.ToString() },
- { "MaxTimeSeries", _maxTimeSeries.ToString() },
- { "MaxHistograms", _maxHistograms.ToString() },
- { "ClientId", _clientId }
- }
- );
-
- return eventCounterProviders.Append(metricsEventSourceProvider).ToArray();
- }
-
- private bool Filter(string meterName, string instrumentName)
- {
- return _counterList.GetCounters(meterName).Contains(instrumentName) || _counterList.IncludesAllCounters(meterName);
- }
-
- private Task<int> Start()
+ private async Task<ReturnCode> Start(MetricsPipeline pipeline, CancellationToken token)
{
- EventPipeProvider[] providers = GetEventPipeProviders();
_renderer.Initialize();
-
- Task monitorTask = new(() => {
+ Task monitorTask = new(async () => {
try
{
- _session = _diagnosticsClient.StartEventPipeSession(providers, false, 10);
- if (_resumeRuntime)
- {
- try
- {
- _diagnosticsClient.ResumeRuntime();
- }
- catch (UnsupportedCommandException)
- {
- // Noop if the command is unknown since the target process is most likely a 3.1 app.
- }
- }
- EventPipeEventSource source = new(_session.EventStream);
- source.Dynamic.All += DynamicAllMonitor;
- _renderer.EventPipeSourceConnected();
- source.Process();
+ Task runAsyncTask = await pipeline.StartAsync(token).ConfigureAwait(false);
+ await runAsyncTask.ConfigureAwait(false);
}
catch (DiagnosticsClientException ex)
{
});
monitorTask.Start();
- bool shouldStopAfterDuration = _duration != default(TimeSpan);
- Stopwatch durationStopwatch = null;
- if (shouldStopAfterDuration)
- {
- durationStopwatch = Stopwatch.StartNew();
- }
-
- while (!_shouldExit.Task.Wait(250))
+ while (!_shouldExit.Task.Wait(250, token))
{
HandleBufferedEvents();
if (!Console.IsInputRedirected && Console.KeyAvailable)
_pauseCmdSet = false;
}
}
+ }
+
+ try
+ {
+ await pipeline.StopAsync(token).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (PipelineException)
+ {
+ }
+
+ return await _shouldExit.Task.ConfigureAwait(false);
+ }
+
+ void ICountersLogger.Log(ICounterPayload payload)
+ {
+ if (_shouldExit.Task.IsCompleted)
+ {
+ return;
+ }
- if (shouldStopAfterDuration && durationStopwatch.Elapsed >= _duration)
+ lock (this)
+ {
+ // If we are paused, ignore the event.
+ // There's a potential race here between the two tasks but not a huge deal if we miss by one event.
+ _renderer.ToggleStatus(_pauseCmdSet);
+ if (payload is ErrorPayload errorPayload)
{
- durationStopwatch.Stop();
- break;
+ // Several of the error messages used by Dotnet are specific to the tool;
+ // the error messages found in errorPayload.ErrorMessage are not tool-specific.
+ // This replaces the generic error messages with specific ones as-needed.
+ string errorMessage = string.Empty;
+ switch (errorPayload.EventType)
+ {
+ case EventType.HistogramLimitError:
+ errorMessage = $"Warning: Histogram tracking limit ({_settings.MaxHistograms}) reached. Not all data is being shown." + Environment.NewLine +
+ "The limit can be changed with --maxHistograms but will use more memory in the target process.";
+ break;
+ case EventType.TimeSeriesLimitError:
+ errorMessage = $"Warning: Time series tracking limit ({_settings.MaxTimeSeries}) reached. Not all data is being shown." + Environment.NewLine +
+ "The limit can be changed with --maxTimeSeries but will use more memory in the target process.";
+ break;
+ case EventType.ErrorTargetProcess:
+ case EventType.MultipleSessionsNotSupportedError:
+ case EventType.MultipleSessionsConfiguredIncorrectlyError:
+ case EventType.ObservableInstrumentCallbackError:
+ default:
+ errorMessage = errorPayload.ErrorMessage;
+ break;
+ }
+
+ _renderer.SetErrorText(errorMessage);
+
+ if (errorPayload.EventType.IsSessionStartupError())
+ {
+ _shouldExit.TrySetResult(ReturnCode.SessionCreationError);
+ }
+ else if (errorPayload.EventType.IsTracingError())
+ {
+ _shouldExit.TrySetResult(ReturnCode.TracingError);
+ }
+ else if (errorPayload.EventType.IsNonFatalError())
+ {
+ // Don't need to exit for NonFatalError
+ }
+ else
+ {
+ _shouldExit.TrySetResult(ReturnCode.UnknownError);
+ }
+ }
+ else if (payload is CounterEndedPayload counterEnded)
+ {
+ _renderer.CounterStopped(counterEnded);
+ }
+ else if (payload.IsMeter)
+ {
+ MeterInstrumentEventObserved(payload.Provider, payload.Timestamp);
+ if (payload.EventType.IsValuePublishedEvent())
+ {
+ CounterPayloadReceived((CounterPayload)payload);
+ }
+ }
+ else
+ {
+ HandleDiagnosticCounter(payload);
}
}
+ }
+
+ public Task PipelineStarted(CancellationToken token)
+ {
+ _renderer.EventPipeSourceConnected();
+ return Task.CompletedTask;
+ }
- StopMonitor();
- return _shouldExit.Task;
+ public Task PipelineStopped(CancellationToken token)
+ {
+ _renderer.Stop();
+ return Task.CompletedTask;
}
}
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
- public class CounterPayload
- {
- public CounterPayload(string providerName, string name, string displayName, string displayUnits, string tags, double value, DateTime timestamp, string type)
- {
- ProviderName = providerName;
- Name = name;
- Tags = tags;
- Value = value;
- Timestamp = timestamp;
- CounterType = type;
- }
-
- public string ProviderName { get; private set; }
- public string Name { get; private set; }
- public double Value { get; private set; }
- public virtual string DisplayName { get; protected set; }
- public string CounterType { get; private set; }
- public DateTime Timestamp { get; private set; }
- public string Tags { get; private set; }
- }
-
- internal class GaugePayload : CounterPayload
- {
- public GaugePayload(string providerName, string name, string displayName, string displayUnits, string tags, double value, DateTime timestamp) :
- base(providerName, name, displayName, displayUnits, tags, value, timestamp, "Metric")
- {
- // 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 RatePayload : CounterPayload
- {
- public RatePayload(string providerName, string name, string displayName, string displayUnits, string tags, double value, double intervalSecs, DateTime timestamp) :
- base(providerName, name, displayName, displayUnits, tags, value, timestamp, "Rate")
- {
- // In case these properties are not provided, set them to appropriate values.
- string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
- string unitsName = string.IsNullOrEmpty(displayUnits) ? "Count" : displayUnits;
- string intervalName = intervalSecs.ToString() + " sec";
- DisplayName = $"{counterName} ({unitsName} / {intervalName})";
- }
- }
-
- internal class PercentilePayload : CounterPayload
- {
- public PercentilePayload(string providerName, string name, string displayName, string displayUnits, string tags, double val, DateTime timestamp) :
- base(providerName, name, displayName, displayUnits, tags, val, timestamp, "Metric")
- {
- // 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;
- }
- }
-}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Diagnostics.Monitoring.EventPipe;
+
+namespace Microsoft.Diagnostics.Tools.Counters
+{
+ internal static class CounterPayloadExtensions
+ {
+ public static string GetDisplay(this ICounterPayload counterPayload)
+ {
+ if (!counterPayload.IsMeter)
+ {
+ string unit = counterPayload.Unit == "count" ? "Count" : counterPayload.Unit;
+ if (counterPayload.CounterType == CounterType.Rate)
+ {
+ return $"{counterPayload.DisplayName} ({unit} / {counterPayload.Series} sec)";
+ }
+ if (!string.IsNullOrEmpty(counterPayload.Unit))
+ {
+ return $"{counterPayload.DisplayName} ({unit})";
+ }
+ }
+
+ return $"{counterPayload.DisplayName}";
+ }
+ }
+}
using System.Globalization;
using System.IO;
using System.Text;
+using Microsoft.Diagnostics.Monitoring.EventPipe;
namespace Microsoft.Diagnostics.Tools.Counters.Exporters
{
builder
.Append(payload.Timestamp.ToString()).Append(',')
- .Append(payload.ProviderName).Append(',')
- .Append(payload.DisplayName);
- if (!string.IsNullOrEmpty(payload.Tags))
+ .Append(payload.Provider).Append(',')
+ .Append(payload.GetDisplay());
+ if (!string.IsNullOrEmpty(payload.Metadata))
{
- builder.Append('[').Append(payload.Tags.Replace(',', ';')).Append(']');
+ builder.Append('[').Append(payload.Metadata.Replace(',', ';')).Append(']');
}
builder.Append(',')
.Append(payload.CounterType).Append(',')
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Microsoft.Diagnostics.Monitoring.EventPipe;
namespace Microsoft.Diagnostics.Tools.Counters.Exporters
{
/// ConsoleWriter is an implementation of ICounterRenderer for rendering the counter values in real-time
/// to the console. This is the renderer for the `dotnet-counters monitor` command.
/// </summary>
- public class ConsoleWriter : ICounterRenderer
+ internal class ConsoleWriter : ICounterRenderer
{
/// <summary>Information about an observed provider.</summary>
private class ObservedProvider
return;
}
- string providerName = payload.ProviderName;
+ string providerName = payload.Provider;
string name = payload.Name;
- string tags = payload.Tags;
+ string tags = payload.Metadata;
bool redraw = false;
if (!_providers.TryGetValue(providerName, out ObservedProvider provider))
if (!provider.Counters.TryGetValue(name, out ObservedCounter counter))
{
- string displayName = payload.DisplayName;
+ string displayName = payload.GetDisplay();
provider.Counters[name] = counter = new ObservedCounter(displayName);
_maxNameLength = Math.Max(_maxNameLength, displayName.Length);
if (tags != null)
{
lock (_lock)
{
- string providerName = payload.ProviderName;
+ string providerName = payload.Provider;
string counterName = payload.Name;
- string tags = payload.Tags;
+ string tags = payload.Metadata;
if (!_providers.TryGetValue(providerName, out ObservedProvider provider))
{
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Diagnostics.Monitoring.EventPipe;
+
namespace Microsoft.Diagnostics.Tools.Counters.Exporters
{
- public interface ICounterRenderer
+ internal interface ICounterRenderer
{
void Initialize();
void EventPipeSourceConnected();
using System.Globalization;
using System.IO;
using System.Text;
+using Microsoft.Diagnostics.Monitoring.EventPipe;
namespace Microsoft.Diagnostics.Tools.Counters.Exporters
{
}
builder
.Append("{ \"timestamp\": \"").Append(DateTime.Now.ToString("u")).Append("\", ")
- .Append(" \"provider\": \"").Append(JsonEscape(payload.ProviderName)).Append("\", ")
- .Append(" \"name\": \"").Append(JsonEscape(payload.DisplayName)).Append("\", ")
- .Append(" \"tags\": \"").Append(JsonEscape(payload.Tags)).Append("\", ")
- .Append(" \"counterType\": \"").Append(JsonEscape(payload.CounterType)).Append("\", ")
+ .Append(" \"provider\": \"").Append(JsonEscape(payload.Provider)).Append("\", ")
+ .Append(" \"name\": \"").Append(JsonEscape(payload.GetDisplay())).Append("\", ")
+ .Append(" \"tags\": \"").Append(JsonEscape(payload.Metadata)).Append("\", ")
+ .Append(" \"counterType\": \"").Append(JsonEscape(payload.CounterType.ToString())).Append("\", ")
.Append(" \"value\": ").Append(payload.Value.ToString(CultureInfo.InvariantCulture)).Append(" },");
}
}
internal static class Program
{
- private delegate Task<int> CollectDelegate(
+ private delegate Task<ReturnCode> CollectDelegate(
CancellationToken ct,
List<string> counter_list,
string counters,
int maxTimeSeries,
TimeSpan duration);
- private delegate Task<int> MonitorDelegate(
+ private delegate Task<ReturnCode> MonitorDelegate(
CancellationToken ct,
List<string> counter_list,
string counters,
public IEnumerable<ICounterPayload> Metrics => _metrics.Values;
- public void Log(ICounterPayload metric)
+ public void Log(ICounterPayload payload)
{
- string key = CreateKey(metric);
+ string key = CreateKey(payload);
- _metrics[key] = metric;
+ _metrics[key] = payload;
// Complete the task source if the last expected key was removed.
if (_expectedCounters.Remove(key) && _expectedCounters.Count == 0)
// Add some variance between -5 and 5 milliseconds to simulate "real" timestamp
_lastTimestamp = _lastTimestamp.Value.AddMilliseconds((10 * _random.NextDouble()) - 5);
- return new CounterPayload(
+ return new EventCounterPayload(
_lastTimestamp.Value,
EventCounterConstants.RuntimeProviderName,
EventCounterConstants.CpuUsageCounterName,
value,
CounterType.Metric,
actualInterval,
+ (int)_intervalSeconds,
null);
}
}
using System.Linq;
using Microsoft.Diagnostics.Tools.Counters;
using Microsoft.Diagnostics.Tools.Counters.Exporters;
+using Microsoft.Diagnostics.Monitoring.EventPipe;
using Xunit;
namespace DotnetCounters.UnitTests
DateTime start = DateTime.Now;
for (int i = 0; i < 100; i++)
{
- exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", "", "", i, 1, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", string.Empty, string.Empty, i, 1, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
DateTime start = DateTime.Now;
for (int i = 0; i < 10; i++)
{
- exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", "", "", i, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", string.Empty, null, i, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
DateTime start = DateTime.Now;
for (int i = 0; i < 100; i++)
{
- exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", "", "", i, 60, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", string.Empty, null, i, 60, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
DateTime start = DateTime.Now;
for (int i = 0; i < 100; i++)
{
- exporter.CounterPayloadReceived(new RatePayload("myProvider", "allocRateGen", "Allocation Rate Gen", "MB", "", i, 60, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new RatePayload("myProvider", "allocRateGen", "Allocation Rate Gen", "MB", string.Empty, i, 60, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
using Microsoft.Diagnostics.Tools.Counters;
using Microsoft.Diagnostics.Tools.Counters.Exporters;
using Newtonsoft.Json;
+using Microsoft.Diagnostics.Monitoring.EventPipe;
using Xunit;
+using System.Collections.Generic;
#pragma warning disable CA1507 // Use nameof to express symbol names
DateTime start = DateTime.Now;
for (int i = 0; i < 10; i++)
{
- exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", "", "", 1, 1, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new RatePayload("myProvider", "incrementingCounterOne", "Incrementing Counter One", string.Empty, string.Empty, 1, 1, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
DateTime start = DateTime.Now;
for (int i = 0; i < 10; i++)
{
- exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", "", "", 1, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new GaugePayload("myProvider", "counterOne", "Counter One", string.Empty, string.Empty, 1, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
DateTime start = DateTime.Now;
for (int i = 0; i < 20; i++)
{
- exporter.CounterPayloadReceived(new GaugePayload("myProvider", "heapSize", "Heap Size", "MB", "", i, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new GaugePayload("myProvider", "heapSize", "Heap Size", "MB", string.Empty, i, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
DateTime start = DateTime.Now;
for (int i = 0; i < 20; i++)
{
- exporter.CounterPayloadReceived(new RatePayload("myProvider", "heapSize", "Heap Size", "MB", "", 0, 60, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new RatePayload("myProvider", "heapSize", "Heap Size", "MB", string.Empty, 0, 60, start + TimeSpan.FromSeconds(i)), false);
}
exporter.Stop();
DateTime start = DateTime.Now;
for (int i = 0; i < 10; i++)
{
- exporter.CounterPayloadReceived(new PercentilePayload("myProvider", "counterOne", "Counter One", "", "f=abc,Percentile=50", 1, start + TimeSpan.FromSeconds(i)), false);
+ exporter.CounterPayloadReceived(new PercentilePayload("myProvider", "counterOne", "Counter One", "", "f=abc,Percentile=50", 1, start + TimeSpan.FromSeconds(1)), false);
}
exporter.Stop();