internal class CounterMonitor : ICountersLogger
{
private const int BufferDelaySecs = 1;
+ private const string EventCountersProviderPrefix = "EventCounters\\";
private int _processId;
- private CounterSet _counterList;
+ private List<EventPipeCounterGroup> _counterList;
private IConsole _console;
private ICounterRenderer _renderer;
private string _output;
private void HandleDiagnosticCounter(ICounterPayload payload)
{
+ // if EventCounters were explicitly requested on the command-line then show them always
+ if (_counterList.Where(group => group.ProviderName == payload.CounterMetadata.ProviderName && group.Type == CounterGroupType.EventCounter).Any())
+ {
+ CounterPayloadReceived((CounterPayload)payload);
+ return;
+ }
+
// init providerEventState if this is the first time we've seen an event from this provider
if (!_providerEventStates.TryGetValue(payload.CounterMetadata.ProviderName, out ProviderEventState providerState))
{
_settings.MaxTimeSeries = maxTimeSeries;
_settings.CounterIntervalSeconds = refreshInterval;
_settings.ResumeRuntime = resumeRuntime;
- _settings.CounterGroups = GetEventPipeProviders();
+ _settings.CounterGroups = _counterList.ToArray();
_settings.UseCounterRateAndValuePayloads = true;
bool useSharedSession = false;
_settings.MaxTimeSeries = maxTimeSeries;
_settings.CounterIntervalSeconds = refreshInterval;
_settings.ResumeRuntime = resumeRuntime;
- _settings.CounterGroups = GetEventPipeProviders();
+ _settings.CounterGroups = _counterList.ToArray();
_output = output;
_diagnosticsClient = holder.Client;
if (_output.Length == 0)
}
}
- internal CounterSet ConfigureCounters(string commaSeparatedProviderListText, List<string> providerList)
+ internal List<EventPipeCounterGroup> ConfigureCounters(string commaSeparatedProviderListText, List<string> providerList)
{
- CounterSet counters = new();
+ List<EventPipeCounterGroup> counters = new();
try
{
if (commaSeparatedProviderListText != null)
}
}
- if (counters.IsEmpty)
+ if (counters.Count == 0)
{
_console.Out.WriteLine($"--counters is unspecified. Monitoring System.Runtime counters by default.");
- counters.AddAllProviderCounters("System.Runtime");
+ ParseCounterProvider("System.Runtime", counters);
}
return counters;
}
// parses a comma separated list of providers
- internal static CounterSet ParseProviderList(string providerListText)
+ internal static List<EventPipeCounterGroup> ParseProviderList(string providerListText)
{
- CounterSet set = new();
+ List<EventPipeCounterGroup> set = new();
ParseProviderList(providerListText, set);
return set;
}
// parses a comma separated list of providers
- internal static void ParseProviderList(string providerListText, CounterSet counters)
+ internal static void ParseProviderList(string providerListText, List<EventPipeCounterGroup> counters)
{
bool inParen = false;
int startIdx = -1;
// System.Runtime
// System.Runtime[exception-count]
// System.Runtime[exception-count,cpu-usage]
- private static void ParseCounterProvider(string providerText, CounterSet counters)
+ private static void ParseCounterProvider(string providerText, List<EventPipeCounterGroup> counters)
{
string[] tokens = providerText.Split('[');
if (tokens.Length == 0)
{
throw new FormatException("Expected at most one '[' in counter_provider");
}
+
string providerName = tokens[0];
- if (tokens.Length == 1)
+ CounterGroupType groupType = CounterGroupType.All;
+ // EventCounters\ is a special prefix for a provider that marks it as an EventCounter provider.
+ if (providerName.StartsWith(EventCountersProviderPrefix))
{
- counters.AddAllProviderCounters(providerName); // Only a provider name was specified
+ providerName = providerName.Substring(EventCountersProviderPrefix.Length);
+ groupType = CounterGroupType.EventCounter;
}
- else
+
+ // An empty set of counters means all counters are enabled.
+ string[] enabledCounters = Array.Empty<string>();
+ EventPipeCounterGroup preExistingGroup = counters.FirstOrDefault(group => group.ProviderName == providerName);
+ if (preExistingGroup != null && preExistingGroup.Type != groupType)
+ {
+ throw new FormatException("Using the same provider name with and without the EventCounters\\ prefix in the counter list is not supported.");
+ }
+ if (tokens.Length == 2)
{
string counterNames = tokens[1];
if (!counterNames.EndsWith(']'))
throw new FormatException("Unexpected characters after closing ']' in counter_provider");
}
}
- string[] enabledCounters = counterNames.Substring(0, counterNames.Length - 1).Split(',', StringSplitOptions.RemoveEmptyEntries);
- counters.AddProviderCounters(providerName, enabledCounters);
+ enabledCounters = counterNames.Substring(0, counterNames.Length - 1).Split(',', StringSplitOptions.RemoveEmptyEntries);
}
- }
- private EventPipeCounterGroup[] GetEventPipeProviders() =>
- _counterList.Providers.Select(provider => new EventPipeCounterGroup
+ // haven't seen this provider yet so add it
+ if (preExistingGroup == null)
{
- ProviderName = provider,
- CounterNames = _counterList.GetCounters(provider).ToArray()
- }).ToArray();
+ counters.Add(new EventPipeCounterGroup
+ {
+ ProviderName = providerName,
+ CounterNames = enabledCounters,
+ Type = groupType
+ });
+ }
+ // we've already seen the provider so merge the configurations
+ else
+ {
+ // If the previous config had some specific counters and the new one also has specific counters then merge them.
+ // Otherwise one of the two requested all counters so the union is also all counters.
+ if (preExistingGroup.CounterNames.Length == 0 || enabledCounters.Length == 0)
+ {
+ preExistingGroup.CounterNames = Array.Empty<string>();
+ }
+ else
+ {
+ preExistingGroup.CounterNames = preExistingGroup.CounterNames.Union(enabledCounters).ToArray();
+ }
+ }
+ }
private async Task<ReturnCode> Start(MetricsPipeline pipeline, CancellationToken token)
{
+++ /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;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
- public class CounterSet
- {
- // a mapping from provider to a list of counters that should be enabled
- // an empty List means all counters are enabled
- private readonly Dictionary<string, List<string>> _providerCounters;
-
- public CounterSet()
- {
- _providerCounters = new Dictionary<string, List<string>>();
- }
-
- public bool IsEmpty => _providerCounters.Count == 0;
-
- public IEnumerable<string> Providers => _providerCounters.Keys;
-
- public bool IncludesAllCounters(string providerName)
- {
- return _providerCounters.TryGetValue(providerName, out List<string> enabledCounters) && enabledCounters.Count == 0;
- }
-
- public IEnumerable<string> GetCounters(string providerName)
- {
- if (!_providerCounters.TryGetValue(providerName, out List<string> enabledCounters))
- {
- return Array.Empty<string>();
- }
- return enabledCounters;
- }
-
- // Called when we want to enable all counters under a provider name.
- public void AddAllProviderCounters(string providerName)
- {
- _providerCounters[providerName] = new List<string>();
- }
-
- public void AddProviderCounters(string providerName, string[] counters)
- {
- if (!_providerCounters.TryGetValue(providerName, out List<string> enabledCounters))
- {
- enabledCounters = new List<string>(counters.Distinct());
- _providerCounters.Add(providerName, enabledCounters);
- }
- else if (enabledCounters.Count != 0) // empty list means all counters are enabled already
- {
- foreach (string counter in counters)
- {
- if (!enabledCounters.Contains(counter))
- {
- enabledCounters.Add(counter);
- }
- }
- }
- }
-
- public bool Contains(string providerName, string counterName)
- {
- return _providerCounters.TryGetValue(providerName, out List<string> counters) &&
- (counters.Count == 0 || counters.Contains(counterName));
- }
- }
-}
public ObservedProvider(string name)
{
Name = name;
- KnownData.TryGetProvider(name, out KnownProvider);
}
public string Name { get; } // Name of the category.
public Dictionary<string, ObservedCounter> Counters { get; } = new Dictionary<string, ObservedCounter>(); // Counters in this category.
- public readonly CounterProvider KnownProvider;
}
/// <summary>Information about an observed counter.</summary>
if (RenderRow(ref row) && // Blank line.
RenderTableRow(ref row, "Name", "Current Value", "Last Delta")) // Table header
{
- foreach (ObservedProvider provider in _providers.Values.OrderBy(p => p.KnownProvider == null).ThenBy(p => p.Name)) // Known providers first.
+ foreach (ObservedProvider provider in _providers.Values.OrderBy(p => p.Name))
{
if (!RenderTableRow(ref row, $"[{provider.Name}]"))
{
+++ /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;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
- internal static class KnownData
- {
- private const string maxVersion = "8.0";
-
- internal static readonly string[] s_AllVersions = new[] { net30, net31, net50, net60, net70, net80 };
- private static readonly string[] s_StartingNet5 = new[] { net50, net60, net70, net80 };
- private static readonly string[] s_StartingNet6 = new[] { net60, net70, net80 };
- private static readonly string[] s_StartingNet7 = new[] { net70, net80 };
-
- private static readonly IReadOnlyDictionary<string, CounterProvider> _knownProviders =
- CreateKnownProviders(maxVersion).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
-
- private const string net80 = "8.0";
- private const string net70 = "7.0";
- private const string net60 = "6.0";
- private const string net50 = "5.0";
- private const string net31 = "3.1";
- private const string net30 = "3.0";
-
- private static IEnumerable<CounterProvider> CreateKnownProviders(string runtimeVersion)
- {
- yield return new CounterProvider(
- "System.Runtime", // Name
- "A default set of performance counters provided by the .NET runtime.", // Description
- "0xffffffff", // Keywords
- "5", // Level
- new[] { // Counters
- new CounterProfile{ Name="cpu-usage", Description="The percent of process' CPU usage relative to all of the system CPU resources [0-100]", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="working-set", Description="Amount of working set used by the process (MB)", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gc-heap-size", Description="Total heap size reported by the GC (MB)", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gen-0-gc-count", Description="Number of Gen 0 GCs between update intervals", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gen-1-gc-count", Description="Number of Gen 1 GCs between update intervals", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gen-2-gc-count", Description="Number of Gen 2 GCs between update intervals", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="time-in-gc", Description="% time in GC since the last GC", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gen-0-size", Description="Gen 0 Heap Size", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gen-1-size", Description="Gen 1 Heap Size", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gen-2-size", Description="Gen 2 Heap Size", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="loh-size", Description="LOH Size", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="poh-size", Description="POH (Pinned Object Heap) Size", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="alloc-rate", Description="Number of bytes allocated in the managed heap between update intervals", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="gc-fragmentation", Description="GC Heap Fragmentation", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="assembly-count", Description="Number of Assemblies Loaded", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="exception-count", Description="Number of Exceptions / sec", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="threadpool-thread-count", Description="Number of ThreadPool Threads", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="monitor-lock-contention-count", Description="Number of times there were contention when trying to take the monitor lock between update intervals", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="threadpool-queue-length", Description="ThreadPool Work Items Queue Length", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="threadpool-completed-items-count", Description="ThreadPool Completed Work Items Count", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="active-timer-count", Description="Number of timers that are currently active", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="il-bytes-jitted", Description="Total IL bytes jitted", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="methods-jitted-count", Description="Number of methods jitted", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="gc-committed", Description="Size of committed memory by the GC (MB)", SupportedVersions=s_StartingNet6 }
- },
- runtimeVersion // RuntimeVersion
- );
- yield return new CounterProvider(
- "Microsoft.AspNetCore.Hosting", // Name
- "A set of performance counters provided by ASP.NET Core.", // Description
- "0x0", // Keywords
- "4", // Level
- new[] { // Counters
- new CounterProfile{ Name="requests-per-second", Description="Number of requests between update intervals", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="total-requests", Description="Total number of requests", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="current-requests", Description="Current number of requests", SupportedVersions=s_AllVersions },
- new CounterProfile{ Name="failed-requests", Description="Failed number of requests", SupportedVersions=s_AllVersions },
- },
- runtimeVersion
- );
- yield return new CounterProvider(
- "Microsoft-AspNetCore-Server-Kestrel", // Name
- "A set of performance counters provided by Kestrel.", // Description
- "0x0", // Keywords
- "4", // Level
- new[] {
- new CounterProfile{ Name="connections-per-second", Description="Number of connections between update intervals", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="total-connections", Description="Total Connections", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls-handshakes-per-second", Description="Number of TLS Handshakes made between update intervals", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="total-tls-handshakes", Description="Total number of TLS handshakes made", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="current-tls-handshakes", Description="Number of currently active TLS handshakes", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="failed-tls-handshakes", Description="Total number of failed TLS handshakes", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="current-connections", Description="Number of current connections", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="connection-queue-length", Description="Length of Kestrel Connection Queue", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="request-queue-length", Description="Length total HTTP request queue", SupportedVersions=s_StartingNet5 },
- },
- runtimeVersion
- );
- yield return new CounterProvider(
- "System.Net.Http",
- "A set of performance counters for System.Net.Http",
- "0x0", // Keywords
- "1", // Level
- new[] {
- new CounterProfile{ Name="requests-started", Description="Total Requests Started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="requests-started-rate", Description="Number of Requests Started between update intervals", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="requests-aborted", Description="Total Requests Aborted", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="requests-aborted-rate", Description="Number of Requests Aborted between update intervals", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="current-requests", Description="Current Requests", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="http11-connections-current-total", Description="Current number of HTTP 1.1 connections", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="http20-connections-current-total", Description="Current number of HTTP 2.0 connections", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="http30-connections-current-total", Description="Current number of HTTP 3.0 connections", SupportedVersions=s_StartingNet7 },
- new CounterProfile{ Name="http11-requests-queue-duration", Description="Average duration of the time HTTP 1.1 requests spent in the request queue", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="http20-requests-queue-duration", Description="Average duration of the time HTTP 2.0 requests spent in the request queue", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="http30-requests-queue-duration", Description="Average duration of the time HTTP 3.0 requests spent in the request queue", SupportedVersions=s_StartingNet7 },
- },
- runtimeVersion
- );
- yield return new CounterProvider(
- "System.Net.NameResolution",
- "A set of performance counters for DNS lookups",
- "0x0",
- "1",
- new[] {
- new CounterProfile{ Name="dns-lookups-requested", Description="The number of DNS lookups requested since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="dns-lookups-duration", Description="Average DNS Lookup Duration", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="current-dns-lookups", Description="The current number of DNS lookups that have started but not yet completed", SupportedVersions=s_StartingNet6 },
- },
- runtimeVersion
- );
- yield return new CounterProvider(
- "System.Net.Security",
- "A set of performance counters for TLS",
- "0x0",
- "1",
- new[] {
- new CounterProfile{ Name="tls-handshake-rate", Description="The number of TLS handshakes completed per update interval", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="total-tls-handshakes", Description="The total number of TLS handshakes completed since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="current-tls-handshakes", Description="The current number of TLS handshakes that have started but not yet completed", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="failed-tls-handshakes", Description="The total number of TLS handshakes failed since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="all-tls-sessions-open", Description="The number of active all TLS sessions", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls10-sessions-open", Description="The number of active TLS 1.0 sessions", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls11-sessions-open", Description="The number of active TLS 1.1 sessions", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls12-sessions-open", Description="The number of active TLS 1.2 sessions", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls13-sessions-open", Description="The number of active TLS 1.3 sessions", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="all-tls-handshake-duration", Description="The average duration of all TLS handshakes", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls10-handshake-duration", Description="The average duration of TLS 1.0 handshakes", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls11-handshake-duration", Description="The average duration of TLS 1.1 handshakes", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls12-handshake-duration", Description="The average duration of TLS 1.2 handshakes", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="tls13-handshake-duration", Description="The average duration of TLS 1.3 handshakes", SupportedVersions=s_StartingNet5 },
- },
- runtimeVersion
- );
- yield return new CounterProvider(
- "System.Net.Sockets",
- "A set of performance counters for System.Net.Sockets",
- "0x0",
- "1",
- new[] {
- new CounterProfile{ Name="outgoing-connections-established", Description="The total number of outgoing connections established since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="incoming-connections-established", Description="The total number of incoming connections established since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="current-outgoing-connect-attempts", Description="The current number of outgoing connect attempts that have started but not yet completed", SupportedVersions=s_StartingNet7 },
- new CounterProfile{ Name="bytes-received", Description="The total number of bytes received since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="bytes-sent", Description="The total number of bytes sent since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="datagrams-received", Description="The total number of datagrams received since the process started", SupportedVersions=s_StartingNet5 },
- new CounterProfile{ Name="datagrams-sent", Description="The total number of datagrams sent since the process started", SupportedVersions=s_StartingNet5 },
- },
- runtimeVersion
- );
- }
-
- public static IReadOnlyList<CounterProvider> GetAllProviders(string version)
- {
- return CreateKnownProviders(version).Where(p => p.Counters.Count > 0).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase).Values.ToList();
- }
-
- public static bool TryGetProvider(string providerName, out CounterProvider provider) => _knownProviders.TryGetValue(providerName, out provider);
- }
-}
private static Option CounterOption() =>
new(
alias: "--counters",
- description: "A comma-separated list of counter providers. Counter providers can be specified as <provider_name> or <provider_name>[comma_separated_counter_names]. If the provider_name is used without qualifying counter_names then all counters will be shown. For example \"System.Runtime[cpu-usage,working-set],Microsoft.AspNetCore.Hosting\" includes the cpu-usage and working-set counters from the System.Runtime provider and all the counters from the Microsoft.AspNetCore.Hosting provider. To discover provider and counter names, use the list command.")
+ description: "A comma-separated list of counter providers. Counter providers can be specified as <provider_name> or <provider_name>[comma_separated_counter_names]. If the provider_name" +
+ " is used without qualifying counter_names then all counters will be shown. For example \"System.Runtime[dotnet.assembly.count,dotnet.gc.pause.time],Microsoft.AspNetCore.Hosting\"" +
+ " includes the dotnet.assembly.count and dotnet.gc.pause.time counters from the System.Runtime provider and all the counters from the Microsoft.AspNetCore.Hosting provider. Provider" +
+ " names can either refer to the name of a Meter for the System.Diagnostics.Metrics API or the name of an EventSource for the EventCounters API. If the monitored application has both" +
+ " a Meter and an EventSource with the same name, the Meter is automatically preferred. Use the prefix \'EventCounters\\\' in front of a provider name to only show the EventCounters." +
+ " To discover well-known provider and counter names, please visit https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics.")
{
Argument = new Argument<string>(name: "counters")
};
" This is useful to monitor the rate of change for a metric.")
{ };
- private static readonly string[] s_SupportedRuntimeVersions = KnownData.s_AllVersions;
-
public static int List(IConsole console, string runtimeVersion)
{
- if (!s_SupportedRuntimeVersions.Contains(runtimeVersion))
- {
- Console.WriteLine($"{runtimeVersion} is not a supported version string or a supported runtime version.");
- Console.WriteLine("Supported version strings: 3.0, 3.1, 5.0, 6.0, 7.0, 8.0");
- return 0;
- }
- IReadOnlyList<CounterProvider> profiles = KnownData.GetAllProviders(runtimeVersion);
- int maxNameLength = profiles.Max(p => p.Name.Length);
- Console.WriteLine($"Showing well-known counters for .NET (Core) version {runtimeVersion} only. Specific processes may support additional counters.");
- foreach (CounterProvider profile in profiles)
- {
- IReadOnlyList<CounterProfile> counters = profile.GetAllCounters();
- int maxCounterNameLength = counters.Max(c => c.Name.Length);
- Console.WriteLine($"{profile.Name.PadRight(maxNameLength)}");
- foreach (CounterProfile counter in profile.Counters.Values)
- {
- Console.WriteLine($" {counter.Name.PadRight(maxCounterNameLength)} \t\t {counter.Description}");
- }
- Console.WriteLine("");
- }
+ Console.WriteLine("Counter information has been moved to the online .NET documentation.");
+ Console.WriteLine("Please visit https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics.");
return 1;
}
" Status: Running",
"",
"Name Current Value",
- "[System.Runtime]",
- " Allocation Rate (B / 1 sec) 1,731",
"[Provider2]",
- " CounterXyz (Doodads) 0.076");
+ " CounterXyz (Doodads) 0.076",
+ "[System.Runtime]",
+ " Allocation Rate (B / 1 sec) 1,731");
+
}
[Fact]
" Status: Running",
"",
"Name Current Value Last Delta",
- "[System.Runtime]",
- " Allocation Rate (B / 1 sec) 1,731",
"[Provider1]",
" Counter1 ({widget} / 1 sec)",
" color",
" Counter2 ({widget} / 1 sec)",
" size temp",
" 1 14",
- " hot 160");
+ " hot 160",
+ "[System.Runtime]",
+ " Allocation Rate (B / 1 sec) 1,731");
}
[Fact]
" Status: Running",
"",
"Name Current Value Last Delta",
- "[System.Runtime]",
- " Allocation Rate (B / 1 sec) 1,731",
"[Provider1]",
" Counter1 ({widget} / 1 sec)",
" color",
" Counter2 ({widget} / 1 sec)",
" size temp",
" 1 14",
- " hot 160");
+ " hot 160",
+ "[System.Runtime]",
+ " Allocation Rate (B / 1 sec) 1,731");
exporter.CounterPayloadReceived(CreateIncrementingEventCounter("System.Runtime", "Allocation Rate", "B", 1732), false);
exporter.CounterPayloadReceived(CreateMeterCounterPreNet8("Provider1", "Counter1", "{widget}", "color=red", 0.2), false);
" Status: Running",
"",
"Name Current Value Last Delta",
- "[System.Runtime]",
- " Allocation Rate (B / 1 sec) 1,732 1",
"[Provider1]",
" Counter1 ({widget} / 1 sec)",
" color",
" Counter2 ({widget} / 1 sec)",
" size temp",
" 1 10 -4",
- " hot 160");
+ " hot 160",
+ "[System.Runtime]",
+ " Allocation Rate (B / 1 sec) 1,732 1");
}
// Starting in .NET 8 MetricsEventSource, Meter counter instruments report both rate of change and
private ITestOutputHelper _outputHelper;
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(2);
- private static readonly string SystemRuntimeName = "System.Runtime";
+ private static readonly string EventCounterSystemRuntimeName = "EventCounters\\System.Runtime";
private static readonly string TagStart = "[";
private static HashSet<CounterTypes> ExpectedCounterTypes = new() { CounterTypes.Metric, CounterTypes.Rate };
ValidateCustomMetrics(metricComponents, CountersExportFormat.csv);
}
- [SkippableTheory(Skip = "https://github.com/dotnet/diagnostics/issues/4806"), MemberData(nameof(Configurations))]
- public async Task TestCounterMonitorSystemRuntimeMetricsJSON(TestConfiguration configuration)
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task TestCounterMonitorEventCounterSystemRuntimeMetricsJSON(TestConfiguration configuration)
{
CheckRuntimeOS();
- List<MetricComponents> metricComponents = await GetCounterTraceJSON(configuration, new List<string> { SystemRuntimeName });
+ List<MetricComponents> metricComponents = await GetCounterTraceJSON(configuration, new List<string> { EventCounterSystemRuntimeName });
- ValidateSystemRuntimeMetrics(metricComponents);
+ ValidateEventCounterSystemRuntimeMetrics(metricComponents);
}
- [SkippableTheory(Skip = "https://github.com/dotnet/diagnostics/issues/4806"), MemberData(nameof(Configurations))]
- public async Task TestCounterMonitorSystemRuntimeMetricsCSV(TestConfiguration configuration)
+ [SkippableTheory, MemberData(nameof(Configurations))]
+ public async Task TestCounterMonitorEventCounterSystemRuntimeMetricsCSV(TestConfiguration configuration)
{
CheckRuntimeOS();
- List<MetricComponents> metricComponents = await GetCounterTraceCSV(configuration, new List<string> { SystemRuntimeName });
+ List<MetricComponents> metricComponents = await GetCounterTraceCSV(configuration, new List<string> { EventCounterSystemRuntimeName });
- ValidateSystemRuntimeMetrics(metricComponents);
+ ValidateEventCounterSystemRuntimeMetrics(metricComponents);
}
- private void ValidateSystemRuntimeMetrics(List<MetricComponents> metricComponents)
+ private void ValidateEventCounterSystemRuntimeMetrics(List<MetricComponents> metricComponents)
{
string[] ExpectedProviders = { "System.Runtime" };
Assert.Equal(ExpectedProviders, metricComponents.Select(c => c.ProviderName).ToHashSet());
using System.Collections.Generic;
using System.Linq;
+using Microsoft.Diagnostics.Monitoring.EventPipe;
using Microsoft.Diagnostics.Tools;
using Microsoft.Diagnostics.Tools.Counters;
using Xunit;
public void GenerateCounterListTestSingleProvider()
{
CounterMonitor monitor = new();
- CounterSet counters = CounterMonitor.ParseProviderList("MySource");
- Assert.Single(counters.Providers);
- Assert.Equal("MySource", counters.Providers.First());
- Assert.True(counters.IncludesAllCounters("MySource"));
+ List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource");
+ Assert.Single(counters);
+ EventPipeCounterGroup mySourceGroup = counters.First();
+ Assert.Equal("MySource", mySourceGroup.ProviderName);
+ Assert.False(mySourceGroup.CounterNames.Any());
}
[Fact]
public void GenerateCounterListTestSingleProviderWithFilter()
{
CounterMonitor monitor = new();
- CounterSet counters = CounterMonitor.ParseProviderList("MySource[counter1,counter2,counter3]");
- Assert.Single(counters.Providers);
- Assert.Equal("MySource", counters.Providers.First());
- Assert.False(counters.IncludesAllCounters("MySource"));
- Assert.True(Enumerable.SequenceEqual(counters.GetCounters("MySource"), new string[] { "counter1", "counter2", "counter3" }));
+ List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource[counter1,counter2,counter3]");
+ Assert.Single(counters);
+ EventPipeCounterGroup mySourceGroup = counters.First();
+ Assert.Equal("MySource", mySourceGroup.ProviderName);
+ Assert.True(Enumerable.SequenceEqual(mySourceGroup.CounterNames, new string[] { "counter1", "counter2", "counter3" }));
}
[Fact]
public void GenerateCounterListTestManyProviders()
{
CounterMonitor monitor = new();
- CounterSet counters = CounterMonitor.ParseProviderList("MySource1,MySource2,System.Runtime");
- Assert.Equal(3, counters.Providers.Count());
- Assert.Equal("MySource1", counters.Providers.ElementAt(0));
- Assert.Equal("MySource2", counters.Providers.ElementAt(1));
- Assert.Equal("System.Runtime", counters.Providers.ElementAt(2));
+ List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource1,MySource2,System.Runtime");
+ Assert.Equal(3, counters.Count());
+ Assert.Equal("MySource1", counters.ElementAt(0).ProviderName);
+ Assert.Equal("MySource2", counters.ElementAt(1).ProviderName);
+ Assert.Equal("System.Runtime", counters.ElementAt(2).ProviderName);
+ }
+
+ [Fact]
+ public void GenerateCounterListTestEventCountersPrefix()
+ {
+ CounterMonitor monitor = new();
+ List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource1,EventCounters\\MySource2");
+ Assert.Equal(2, counters.Count());
+ Assert.Equal("MySource1", counters.ElementAt(0).ProviderName);
+ Assert.Equal(CounterGroupType.All, counters.ElementAt(0).Type);
+ Assert.Equal("MySource2", counters.ElementAt(1).ProviderName);
+ Assert.Equal(CounterGroupType.EventCounter, counters.ElementAt(1).Type);
}
[Fact]
public void GenerateCounterListTestManyProvidersWithFilter()
{
CounterMonitor monitor = new();
- CounterSet counters = CounterMonitor.ParseProviderList("MySource1[mycounter1,mycounter2], MySource2[mycounter1], System.Runtime[cpu-usage,working-set]");
- Assert.Equal(3, counters.Providers.Count());
+ List<EventPipeCounterGroup> counters = CounterMonitor.ParseProviderList("MySource1[mycounter1,mycounter2], MySource2[mycounter1], System.Runtime[cpu-usage,working-set]");
+ Assert.Equal(3, counters.Count());
- Assert.Equal("MySource1", counters.Providers.ElementAt(0));
- Assert.False(counters.IncludesAllCounters("MySource1"));
- Assert.True(Enumerable.SequenceEqual(counters.GetCounters("MySource1"), new string[] { "mycounter1", "mycounter2" }));
+ EventPipeCounterGroup mySource1Group = counters.ElementAt(0);
+ Assert.Equal("MySource1", mySource1Group.ProviderName);
+ Assert.True(Enumerable.SequenceEqual(mySource1Group.CounterNames, new string[] { "mycounter1", "mycounter2" }));
- Assert.Equal("MySource2", counters.Providers.ElementAt(1));
- Assert.False(counters.IncludesAllCounters("MySource2"));
- Assert.True(Enumerable.SequenceEqual(counters.GetCounters("MySource2"), new string[] { "mycounter1" }));
+ EventPipeCounterGroup mySource2Group = counters.ElementAt(1);
+ Assert.Equal("MySource2", mySource2Group.ProviderName);
+ Assert.True(Enumerable.SequenceEqual(mySource2Group.CounterNames, new string[] { "mycounter1" }));
- Assert.Equal("System.Runtime", counters.Providers.ElementAt(2));
- Assert.False(counters.IncludesAllCounters("System.Runtime"));
- Assert.True(Enumerable.SequenceEqual(counters.GetCounters("System.Runtime"), new string[] { "cpu-usage", "working-set" }));
+ EventPipeCounterGroup runtimeGroup = counters.ElementAt(2);
+ Assert.Equal("System.Runtime", runtimeGroup.ProviderName);
+ Assert.True(Enumerable.SequenceEqual(runtimeGroup.CounterNames, new string[] { "cpu-usage", "working-set" }));
}
[Fact]
CounterMonitor monitor = new();
List<string> commandLineProviderArgs = new() { "System.Runtime", "MyEventSource" };
string countersOptionText = "MyEventSource1,MyEventSource2";
- CounterSet counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
- Assert.Contains("MyEventSource", counters.Providers);
- Assert.Contains("MyEventSource1", counters.Providers);
- Assert.Contains("MyEventSource2", counters.Providers);
- Assert.Contains("System.Runtime", counters.Providers);
+ List<EventPipeCounterGroup> counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
+ Assert.Contains("MyEventSource", counters.Select(g => g.ProviderName));
+ Assert.Contains("MyEventSource1", counters.Select(g => g.ProviderName));
+ Assert.Contains("MyEventSource2", counters.Select(g => g.ProviderName));
+ Assert.Contains("System.Runtime", counters.Select(g => g.ProviderName));
}
[Fact]
CounterMonitor monitor = new();
List<string> commandLineProviderArgs = new() { "System.Runtime", "MyEventSource" };
string countersOptionText = "System.Runtime,MyEventSource";
- CounterSet counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
- Assert.Equal(2, counters.Providers.Count());
- Assert.Contains("MyEventSource", counters.Providers);
- Assert.Contains("System.Runtime", counters.Providers);
+ List<EventPipeCounterGroup> counters = monitor.ConfigureCounters(countersOptionText, commandLineProviderArgs);
+ Assert.Equal(2, counters.Count());
+ Assert.Contains("MyEventSource", counters.Select(g => g.ProviderName));
+ Assert.Contains("System.Runtime", counters.Select(g => g.ProviderName));
}
[Fact]
CommandLineErrorException e = Assert.Throws<CommandLineErrorException>(() => monitor.ConfigureCounters(countersOptionText, null));
Assert.Equal("Error parsing --counters argument: Expected at most one '[' in counter_provider", e.Message);
}
+
+ [Fact]
+ public void ParseErrorMultiplePrefixesOnSameProvider()
+ {
+ CounterMonitor monitor = new();
+ string countersOptionText = "System.Runtime,MyEventSource,EventCounters\\System.Runtime";
+ CommandLineErrorException e = Assert.Throws<CommandLineErrorException>(() => monitor.ConfigureCounters(countersOptionText, null));
+ Assert.Equal("Error parsing --counters argument: Using the same provider name with and without the EventCounters\\ prefix in the counter list is not supported.", e.Message);
+ }
}
}
+++ /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 Xunit;
-
-namespace Microsoft.Diagnostics.Tools.Counters
-{
- /// <summary>
- /// These test the some of the known providers that we provide as a default configuration for customers to use.
- /// </summary>
- public class KnownProviderTests
- {
- [Fact]
- public void TestRuntimeProvider()
- {
- KnownData.TryGetProvider("System.Runtime", out CounterProvider runtimeProvider);
-
- Assert.Equal("System.Runtime", runtimeProvider.Name);
- Assert.Equal("0xffffffff", runtimeProvider.Keywords);
- Assert.Equal("5", runtimeProvider.Level);
- Assert.Equal("System.Runtime:0xffffffff:5:EventCounterIntervalSec=1", runtimeProvider.ToProviderString(1));
- }
-
- [Fact]
- public void TestASPNETProvider()
- {
- KnownData.TryGetProvider("Microsoft.AspNetCore.Hosting", out CounterProvider aspnetProvider);
-
- Assert.Equal("Microsoft.AspNetCore.Hosting", aspnetProvider.Name);
- Assert.Equal("0x0", aspnetProvider.Keywords);
- Assert.Equal("4", aspnetProvider.Level);
- Assert.Equal("Microsoft.AspNetCore.Hosting:0x0:4:EventCounterIntervalSec=5", aspnetProvider.ToProviderString(5));
- }
-
- [Fact]
- public void UnknownProvider()
- {
- KnownData.TryGetProvider("SomeRandomProvider", out CounterProvider randomProvider);
-
- Assert.Null(randomProvider);
- }
-
- // TODO: Add more as we add more providers as known providers to the tool...
- }
-}