Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d55df48
[Dotnet Monitor] Ignore - Adding System.Diagnostics.Metrics Support (…
kkeirstead Nov 30, 2022
e6b5f21
[Dotnet Monitor] Revert To Logging a single payload (not a list) (#3538)
kkeirstead Dec 7, 2022
b52b4c4
Merge pull request #3545 from dotnet/main
wiktork Dec 8, 2022
ddf091e
[Feature branch changes, no review] Fixup counter apis (#3559)
wiktork Dec 16, 2022
959ebd6
[Dotnet-Monitor] [Feature Branch] Switched quantile to Percentile (#3…
kkeirstead Dec 21, 2022
f3fd8a6
Merge pull request #3575 from dotnet/main
wiktork Dec 29, 2022
c822924
Merge pull request #3579 from dotnet/main
wiktork Jan 4, 2023
5052752
Minor branch cleanup
wiktork Jan 4, 2023
1b70d14
Merge pull request #3580 from wiktork/dev/wiktork/cleanupFeature
wiktork Jan 4, 2023
308cffc
PR for feature branch
wiktork Jan 13, 2023
6f01912
Merge pull request #3599 from wiktork/dev/wiktork/cleanupFeature
wiktork Jan 17, 2023
149f3ad
PR feedback
wiktork Jan 19, 2023
22ff644
Pr feedback feedback
wiktork Jan 24, 2023
645c2d0
Merge pull request #3608 from wiktork/dev/wiktork/prFeedback
wiktork Jan 24, 2023
fe36113
Convert Histogram to single payload
wiktork Jan 25, 2023
79172f9
Merge pull request #3614 from wiktork/dev/wiktork/prFeedback
wiktork Jan 25, 2023
b47a957
Tweaks to account for new All flag
kkeirstead Jan 25, 2023
887c6d4
Fixed build/test failures from Wiktor's changes
kkeirstead Jan 25, 2023
ff4586f
Merge pull request #3615 from kkeirstead/kkeirstead/SDM_MetricsTypeCo…
kkeirstead Jan 25, 2023
4970332
Merge pull request #3617 from dotnet/main
wiktork Jan 26, 2023
ba1fac5
Fix issue with empty quantiles
wiktork Jan 27, 2023
2c1beab
Merge pull request #3618 from wiktork/dev/wiktork/prFeedback
wiktork Jan 27, 2023
8f8bce2
Initial PR feedback (#3620)
wiktork Jan 27, 2023
b2971ce
Fixes outdated naming in test
kkeirstead Jan 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,83 @@

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
[Flags]
public enum MetricType
{
EventCounter = 0x1,
Meter = 0x2,
All = 0xFF
}

public sealed class MetricEventPipeProvider
{
public string Provider { get; set; }

public float? IntervalSeconds { get; set; }

public MetricType Type { get; set; } = MetricType.All;
}

public sealed class MetricSourceConfiguration : MonitoringSourceConfiguration
{
private readonly IList<EventPipeProvider> _eventPipeProviders;
public string SessionId { get; private set; }

public MetricSourceConfiguration(float metricIntervalSeconds, IEnumerable<string> customProviderNames)
public MetricSourceConfiguration(float metricIntervalSeconds, IEnumerable<string> eventCounterProviderNames)
: this(metricIntervalSeconds, CreateProviders(eventCounterProviderNames?.Any() == true ? eventCounterProviderNames : DefaultMetricProviders))
{
RequestRundown = false;
if (customProviderNames == null)
{
throw new ArgumentNullException(nameof(customProviderNames));
}
MetricIntervalSeconds = metricIntervalSeconds.ToString(CultureInfo.InvariantCulture);
}

IEnumerable<string> providers = null;
if (customProviderNames.Any())
public MetricSourceConfiguration(float metricIntervalSeconds, IEnumerable<MetricEventPipeProvider> providers, int maxHistograms = 20, int maxTimeSeries = 1000)
{
if (providers == null)
{
providers = customProviderNames;
throw new ArgumentNullException(nameof(providers));
}
else

RequestRundown = false;

_eventPipeProviders = providers.Where(provider => provider.Type.HasFlag(MetricType.EventCounter))
.Select((MetricEventPipeProvider provider) => new EventPipeProvider(provider.Provider,
EventLevel.Informational,
(long)ClrTraceEventParser.Keywords.None,
new Dictionary<string, string>()
{
{
"EventCounterIntervalSec", (provider.IntervalSeconds ?? metricIntervalSeconds).ToString(CultureInfo.InvariantCulture)
}
})).ToList();

IEnumerable<MetricEventPipeProvider> meterProviders = providers.Where(provider => provider.Type.HasFlag(MetricType.Meter));

if (meterProviders.Any())
{
providers = new[] { SystemRuntimeEventSourceName, MicrosoftAspNetCoreHostingEventSourceName, GrpcAspNetCoreServer };
}
const long TimeSeriesValues = 0x2;
string metrics = string.Join(',', meterProviders.Select(p => p.Provider));

SessionId = Guid.NewGuid().ToString();

_eventPipeProviders = providers.Select((string provider) => new EventPipeProvider(provider,
EventLevel.Informational,
(long)ClrTraceEventParser.Keywords.None,
new Dictionary<string, string>()
{
{ "EventCounterIntervalSec", MetricIntervalSeconds }
})).ToList();
EventPipeProvider metricsEventSourceProvider =
new EventPipeProvider("System.Diagnostics.Metrics", EventLevel.Informational, TimeSeriesValues,
new Dictionary<string, string>()
{
{ "SessionId", SessionId },
{ "Metrics", metrics },
{ "RefreshInterval", metricIntervalSeconds.ToString(CultureInfo.InvariantCulture) },
{ "MaxTimeSeries", maxTimeSeries.ToString() },
{ "MaxHistograms", maxHistograms.ToString() }
}
);

_eventPipeProviders = _eventPipeProviders.Append(metricsEventSourceProvider).ToArray();
}
}

private string MetricIntervalSeconds { get; }
private static IEnumerable<MetricEventPipeProvider> CreateProviders(IEnumerable<string> providers) =>
providers.Select(provider => new MetricEventPipeProvider {
Provider = provider,
Type = MetricType.EventCounter
});

public override IList<EventPipeProvider> GetProviders() => _eventPipeProviders;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,20 @@ public void AddFilter(string providerName, string[] counters)

public IEnumerable<string> GetProviders() => _enabledCounters.Keys;

public int IntervalSeconds => _intervalMilliseconds / 1000;

public bool IsIncluded(string providerName, string counterName, int intervalMilliseconds)
{
if (_intervalMilliseconds != intervalMilliseconds)
{
return false;
}

return IsIncluded(providerName, counterName);
}

public bool IsIncluded(string providerName, string counterName)
{
if (_enabledCounters.Count == 0)
{
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

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
{
#if NETSTANDARD
private static readonly IReadOnlyDictionary<string, string> Empty = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(0));
#else
private static readonly IReadOnlyDictionary<string, string> Empty = System.Collections.Immutable.ImmutableDictionary<string, string>.Empty;
#endif

public CounterPayload(DateTime timestamp,
string provider,
string name,
Expand All @@ -24,7 +22,7 @@ public CounterPayload(DateTime timestamp,
double value,
CounterType counterType,
float interval,
Dictionary<string, string> metadata)
string metadata)
{
Timestamp = timestamp;
Name = name;
Expand All @@ -34,14 +32,27 @@ public CounterPayload(DateTime timestamp,
CounterType = counterType;
Provider = provider;
Interval = interval;
Metadata = metadata ?? Empty;
Metadata = metadata;
EventType = EventType.Gauge;
}

// Copied from dotnet-counters
public CounterPayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp, string type, EventType eventType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

displayName is not used; either remove it, or (preferably) have it set the DisplayName property, change the property to get-only, and call all upstreams to pass in the coerced display name value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a separate change for cleaning up all these constructors.

{
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; }
public string DisplayName { get; protected set; }

public string Unit { get; }

Expand All @@ -55,6 +66,81 @@ public CounterPayload(DateTime timestamp,

public string Provider { get; }

public IReadOnlyDictionary<string, string> Metadata { get; }
public string Metadata { get; }

public EventType EventType { get; set; }
}

internal class GaugePayload : CounterPayload
{
public GaugePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, DateTime timestamp) :
base(providerName, name, displayName, displayUnits, metadata, value, timestamp, "Metric", EventType.Gauge)
{
// In case these properties are not provided, set them to appropriate values.
string counterName = string.IsNullOrEmpty(displayName) ? name : displayName;
DisplayName = !string.IsNullOrEmpty(displayUnits) ? $"{counterName} ({displayUnits})" : counterName;
}
}

internal class CounterEndedPayload : CounterPayload
{
public CounterEndedPayload(string providerName, string name, string displayName, DateTime timestamp)
: base(providerName, name, displayName, string.Empty, null, 0.0, timestamp, "Metric", EventType.CounterEnded)
{

}
}

internal class RatePayload : CounterPayload
{
public RatePayload(string providerName, string name, string displayName, string displayUnits, string metadata, double value, double intervalSecs, DateTime timestamp) :
base(providerName, name, displayName, displayUnits, metadata, value, timestamp, "Rate", EventType.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 metadata, IEnumerable<(double quantile, double value)> quantiles, DateTime timestamp) :
base(providerName, name, displayName, displayUnits, metadata, 0.0, timestamp, "Metric", 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.Select(v => new Quantile(v.quantile, v.value)).ToArray();
}

public Quantile[] Quantiles { get; }
}

internal record struct Quantile(double Percentage, double Value);

internal class ErrorPayload : CounterPayload
{
public ErrorPayload(string errorMessage) : this(errorMessage, DateTime.UtcNow)
{
}

public ErrorPayload(string errorMessage, DateTime timestamp) :
base(string.Empty, string.Empty, string.Empty, string.Empty, null, 0.0, timestamp, "Metric", EventType.Error)
{
ErrorMessage = errorMessage;
}

public string ErrorMessage { get; private set; }
}

internal enum EventType : int
{
Rate,
Gauge,
Histogram,
Error,
CounterEnded
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@
using Microsoft.Diagnostics.Tracing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal class EventCounterPipeline : EventSourcePipeline<EventPipeCounterPipelineSettings>
internal class CounterPipeline : EventSourcePipeline<CounterPipelineSettings>
{
private readonly IEnumerable<ICountersLogger> _loggers;
private readonly CounterFilter _filter;
private string _sessionId;

public EventCounterPipeline(DiagnosticsClient client,
EventPipeCounterPipelineSettings settings,
public CounterPipeline(DiagnosticsClient client,
CounterPipelineSettings settings,
IEnumerable<ICountersLogger> loggers) : base(client, settings)
{
_loggers = loggers ?? throw new ArgumentNullException(nameof(loggers));
Expand All @@ -38,18 +40,28 @@ public EventCounterPipeline(DiagnosticsClient client,

protected override MonitoringSourceConfiguration CreateConfiguration()
{
return new MetricSourceConfiguration(Settings.CounterIntervalSeconds, _filter.GetProviders());
var config = new MetricSourceConfiguration(Settings.CounterIntervalSeconds, Settings.CounterGroups.Select((EventPipeCounterGroup counterGroup) => new MetricEventPipeProvider
{
Provider = counterGroup.ProviderName,
IntervalSeconds = counterGroup.IntervalSeconds,
Type = (MetricType)counterGroup.Type
}),
Settings.MaxHistograms, Settings.MaxTimeSeries);

_sessionId = config.SessionId;

return config;
}

protected override async Task OnEventSourceAvailable(EventPipeEventSource eventSource, Func<Task> stopSessionAsync, CancellationToken token)
{
ExecuteCounterLoggerAction((metricLogger) => metricLogger.PipelineStarted());
await ExecuteCounterLoggerActionAsync((metricLogger) => metricLogger.PipelineStarted(token));

eventSource.Dynamic.All += traceEvent =>
{
try
{
if (traceEvent.TryGetCounterPayload(_filter, out ICounterPayload counterPayload))
if (traceEvent.TryGetCounterPayload(_filter, _sessionId, out ICounterPayload counterPayload))
{
ExecuteCounterLoggerAction((metricLogger) => metricLogger.Log(counterPayload));
}
Expand All @@ -67,7 +79,21 @@ protected override async Task OnEventSourceAvailable(EventPipeEventSource eventS

await sourceCompletedTaskSource.Task;

ExecuteCounterLoggerAction((metricLogger) => metricLogger.PipelineStopped());
await ExecuteCounterLoggerActionAsync((metricLogger) => metricLogger.PipelineStopped(token));
}

private async Task ExecuteCounterLoggerActionAsync(Func<ICountersLogger, Task> action)
{
foreach (ICountersLogger logger in _loggers)
{
try
{
await action(logger);
}
catch (ObjectDisposedException)
{
}
}
}

private void ExecuteCounterLoggerAction(Action<ICountersLogger> action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,35 @@

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal class EventPipeCounterPipelineSettings : EventSourcePipelineSettings
internal class CounterPipelineSettings : EventSourcePipelineSettings
{
public EventPipeCounterGroup[] CounterGroups { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this in favor of using MetricEventPipeProvider only instead of using an intermediate representation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long term, I think this should be cleaned up, but I don't want to make this change now. The api layers here are:

  • Dotnet monitor configuration layer. Truly public api, no breaking changes.
  • MetricsPipelineSettings/EventPipeCounterGroup represents configuration for our pipeline. That includes specific events we are interested in. Currently this is an internal api, but in the future it might become public. This has significant overlap with the layer below.
  • MonitoringSourceConfiguration/MetricEventPipeProvider used for mapping settings to EventSource providers. Currently public (but not documented or anything). We do not need event names at this layer, since providers don't do that kind of filtering (the pipeline in the layer above does this).
  • EventPipeProvider - NETCore.Client level configuration extracted from MonitoringSourceConfiguration.


//Do not use TimeSpan here since we may need to synchronize this pipeline interval
//with a different session and want to make sure the values are identical.
public float CounterIntervalSeconds { get; set; }

public int MaxHistograms { get; set; }

public int MaxTimeSeries { get; set; }
}

[Flags]
internal enum CounterGroupType
{
EventCounter = 0x1,
Meter = 0x2,
All = 0xFF
}

internal class EventPipeCounterGroup
{
public string ProviderName { get; set; }

public string[] CounterNames { get; set; }

public CounterGroupType Type { get; set; } = CounterGroupType.All;

public float? IntervalSeconds { get; set; }
}
}
Loading