diff --git a/Microsoft.Extensions.DependencyInjection.AutoActivation.json b/Microsoft.Extensions.DependencyInjection.AutoActivation.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/eng/pipelines/templates/BuildAndTest.yml b/eng/pipelines/templates/BuildAndTest.yml index 7f3215fb406..a1ea2f08baf 100644 --- a/eng/pipelines/templates/BuildAndTest.yml +++ b/eng/pipelines/templates/BuildAndTest.yml @@ -45,6 +45,15 @@ steps: /bl:${{ parameters.repoLogPath }}/restore.binlog displayName: Restore + - pwsh: | + $(Build.SourcesDirectory)/scripts/Slngen.ps1 -All -NoLaunch + displayName: Create solution + + - script: ${{ parameters.buildScript }} + -restore + /bl:${{ parameters.repoLogPath }}/restore2.binlog + displayName: Restore solution + - script: ${{ parameters.buildScript }} -build -configuration ${{ parameters.buildConfig }} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersCollectorOptions.cs b/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersCollectorOptions.cs deleted file mode 100644 index 47b66798f6c..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersCollectorOptions.cs +++ /dev/null @@ -1,65 +0,0 @@ -// 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.ComponentModel.DataAnnotations; -using System.Diagnostics.CodeAnalysis; -using Microsoft.Shared.Data.Validation; -using Microsoft.Shared.DiagnosticIds; - -namespace Microsoft.Extensions.Telemetry.Metering; - -/// -/// Configuration options for . -/// -public class EventCountersCollectorOptions -{ - private static readonly TimeSpan _defaultSamplingInterval = TimeSpan.FromSeconds(1); - - /// - /// Gets or sets a list of EventSources and CounterNames to listen for. - /// - /// - /// The default value is an empty dictionary. - /// - /// - /// It is a dictionary of EventSource to the set of counters that needs to be collected from the event source. - /// See - /// for well known event counters and their availability. - /// - [Required] -#pragma warning disable CA2227 // Collection properties should be read only -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - public IDictionary> Counters { get; set; } -#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code -#pragma warning restore CA2227 // Collection properties should be read only - = new Dictionary>(StringComparer.OrdinalIgnoreCase); - - /// - /// Gets or sets a sampling interval for counters. - /// - /// - /// The default value is 1 second. - /// - [TimeSpan("00:00:01", "00:10:00")] - public TimeSpan SamplingInterval { get; set; } = _defaultSamplingInterval; - - /// - /// Gets or sets a value indicating whether to include recommended default counters. - /// - /// - /// The default value is . If is empty, the default value is so the - /// default recommended counters are included when the listener is created. - /// - /// - /// Includes the recommended default event counters in addition to the counters specified in . - /// EventSource: "System.Runtime", Counters: - /// - "cpu-usage", "working-set", "time-in-gc", "alloc-rate", "exception-count", "gen-2-gc-count", "gen-2-size", - /// - "monitor-lock-contention-count", "active-timer-count", "threadpool-queue-length", "threadpool-thread-count", - /// EventSource: "Microsoft-AspNetCore-Server-Kestrel", Counters: - /// - "connection-queue-length", "request-queue-length". - /// - [Experimental(diagnosticId: Experiments.Telemetry, UrlFormat = Experiments.UrlFormat)] - public bool IncludeRecommendedDefault { get; set; } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersExtensions.cs b/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersExtensions.cs deleted file mode 100644 index a52dd03cd85..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersExtensions.cs +++ /dev/null @@ -1,72 +0,0 @@ -// 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.Diagnostics.CodeAnalysis; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Telemetry.Metering.Internal; -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Extensions.Telemetry.Metering; - -/// -/// Extensions for registering . -/// -public static class EventCountersExtensions -{ - /// - /// Adds to the specified . - /// - /// The to add the service to. - /// An to configure the provided . - /// The so that additional calls can be chained. - /// Either or is . - public static IServiceCollection AddEventCounterCollector(this IServiceCollection services, Action configure) - { - _ = Throw.IfNull(services); - _ = Throw.IfNull(configure); - - _ = AddEventCounterCollectorInternal(services) - .Configure(configure); - - return services; - } - - /// - /// Adds to the specified . - /// - /// The to add the service to. - /// An to configure the provided . - /// The so that additional calls can be chained. - /// Either or is . - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(EventCountersCollectorOptions))] - [UnconditionalSuppressMessage( - "Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = "Addressed by [DynamicDependency]")] - public static IServiceCollection AddEventCounterCollector(this IServiceCollection services, IConfigurationSection section) - { - _ = Throw.IfNull(services); - _ = Throw.IfNull(section); - - _ = AddEventCounterCollectorInternal(services) - .Bind(section); - - return services; - } - - private static OptionsBuilder AddEventCounterCollectorInternal(IServiceCollection services) - { - var optionsBuilder = services - .AddOptionsWithValidateOnStart(); - - services.TryAddEnumerable(ServiceDescriptor.Singleton, EventCountersValidator>()); - _ = services.RegisterMetering(); - _ = services.AddActivatedSingleton(); - - return optionsBuilder; - } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersListener.cs b/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersListener.cs deleted file mode 100644 index c5ca35d91e7..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/EventCountersListener.cs +++ /dev/null @@ -1,264 +0,0 @@ -// 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.Concurrent; -using System.Collections.Frozen; -using System.Collections.Generic; -using System.Diagnostics.Metrics; -using System.Diagnostics.Tracing; -using System.Globalization; -using System.Linq; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Telemetry.Metering.Internal; -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Extensions.Telemetry.Metering; - -/// -/// An that allows to send data written by -/// instances of an to the metering pipeline. -/// -internal sealed class EventCountersListener : EventListener -{ - internal readonly ConcurrentDictionary> CounterInstruments = new(); - internal readonly ConcurrentDictionary> HistogramInstruments = new(); - internal readonly FrozenDictionary> Counters; - - private readonly bool _isInitialized; - private readonly Dictionary _eventSourceSettings; - private readonly Meter _meter; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The options. - /// The meter provider. - /// Logger instance. - public EventCountersListener( - IOptions options, - Meter meter, - ILogger? logger = null) - { - var value = Throw.IfMemberNull(options, options.Value); - Counters = CreateCountersDictionary(value); - _meter = meter; - _logger = logger ?? NullLoggerFactory.Instance.CreateLogger(); - _eventSourceSettings = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - ["EventCounterIntervalSec"] = value.SamplingInterval.TotalSeconds.ToString(CultureInfo.InvariantCulture) - }; - - _logger.ConfiguredEventCountersOptions(value); - - _isInitialized = true; - foreach (var eventSource in EventSource.GetSources()) - { - EnableIfNeeded(eventSource); - } - } - - /// - /// Called for all existing event sources when the event listener is created and - /// when a new event source is attached to the listener. - /// - /// The event source. - /// - /// This method is called whenever a new eventSource is 'attached' to the dispatcher. - /// This can happen for all existing EventSources when the EventListener is created - /// as well as for any EventSources that come into existence after the EventListener has been created. - /// These 'catch up' events are called during the construction of the EventListener. - /// Subclasses need to be prepared for that. In a multi-threaded environment, - /// it is possible that 'OnEventWritten' callbacks for a particular eventSource to occur - /// BEFORE the OnEventSourceCreated is issued. - /// - protected override void OnEventSourceCreated(EventSource eventSource) - { - if (!_isInitialized || eventSource == null) - { - return; - } - - EnableIfNeeded(eventSource); - } - - /// - /// Called whenever an event has been written by an event source for which the event listener has enabled events. - /// - /// The event arguments that describe the event. - protected override void OnEventWritten(EventWrittenEventArgs eventData) - { - if (!_isInitialized) - { - return; - } - - if (eventData.EventName == null) - { - _logger.EventNameIsNull(eventData.EventSource.Name); - return; - } - - if (eventData.Payload == null) - { - _logger.PayloadIsNull(eventData.EventSource.Name, eventData.EventName); - return; - } - - if (!eventData.EventName.Equals("EventCounters", StringComparison.OrdinalIgnoreCase)) - { - _logger.EventNameIsNotEventCounters(eventData.EventName); - return; - } - - if (Counters.TryGetValue(eventData.EventSource.Name, out var counters)) - { - for (var i = 0; i < eventData.Payload.Count; ++i) - { - if (eventData.Payload[i] is not IDictionary eventPayload) - { - continue; - } - - if (!eventPayload.TryGetValue("CounterType", out var counterType)) - { - _logger.EventPayloadDoesNotContainCounterType(eventData.EventSource.Name, eventData.EventName); - continue; - } - - if (!eventPayload.TryGetValue("Name", out var counterName)) - { - _logger.EventPayloadDoesNotContainName(eventData.EventSource.Name, eventData.EventName); - continue; - } - - var name = counterName.ToString(); - - if (string.IsNullOrEmpty(name)) - { - _logger.CounterNameIsEmpty(eventData.EventSource.Name, eventData.EventName); - continue; - } - - if (!counters.Contains("*") && !counters.Contains(name)) - { - _logger.CounterNotEnabled(name, eventData.EventSource.Name, eventData.EventName); - continue; - } - - var type = counterType.ToString(); - - _logger.ReceivedEventOfType(eventData.EventSource.Name, counterName, type); - if ("Sum".Equals(type, StringComparison.OrdinalIgnoreCase)) - { - var fullName = $"{eventData.EventSource.Name}|{counterName}"; - RecordSumEvent(eventPayload, fullName); - } - else if ("Mean".Equals(type, StringComparison.OrdinalIgnoreCase)) - { - var fullName = $"{eventData.EventSource.Name}|{counterName}"; - RecordMeanEvent(eventPayload, fullName); - } - } - } - } - - private static FrozenDictionary> CreateCountersDictionary(EventCountersCollectorOptions options) - { - var d = new Dictionary>(StringComparer.OrdinalIgnoreCase); - - foreach (var kvp in options.Counters) - { - if (kvp.Value == null || kvp.Value.Count == 0) - { - continue; - } - - d.Add(kvp.Key, new HashSet(kvp.Value, StringComparer.OrdinalIgnoreCase)); - } - - if (options.IncludeRecommendedDefault) - { - foreach (var kvp in Internal.Constants.DefaultCounters) - { - if (d.TryGetValue(kvp.Key, out var valueSet) && valueSet != null) - { - foreach (var kv in kvp.Value) - { - _ = valueSet.Add(kv); - } - } - else - { - d.Add(kvp.Key, new HashSet(kvp.Value, StringComparer.OrdinalIgnoreCase)); - } - } - } - - return d.Select(x => new KeyValuePair>(x.Key, x.Value.ToFrozenSet(StringComparer.Ordinal))).ToFrozenDictionary(StringComparer.OrdinalIgnoreCase); - } - - private bool TryConvertToLong(object? value, out long convertedValue) - { - try - { - convertedValue = Convert.ToInt64(value, CultureInfo.InvariantCulture); - return true; - } - catch (OverflowException) - { - // The number is invalid, ignore processing it and don't bubble up exception. - _logger.OverflowExceptionWhileConversion(value); - } - catch (FormatException) - { - // The number is invalid, ignore processing it and don't bubble up exception. - _logger.FormatExceptionWhileConversion(value); - } - - convertedValue = 0; - return false; - } - - private void EnableIfNeeded(EventSource eventSource) - { - if (!Counters.ContainsKey(eventSource.Name)) - { - return; - } - - _logger.EnablingEventSource(eventSource.Name); - EnableEvents(eventSource, EventLevel.Critical, EventKeywords.None, _eventSourceSettings); - } - - private void RecordSumEvent(IDictionary eventPayload, string counterName) - { - if (!eventPayload.TryGetValue("Increment", out var payloadValue)) - { - return; - } - - if (TryConvertToLong(payloadValue, out long metricValue)) - { - var counter = CounterInstruments.GetOrAdd(counterName, name => _meter.CreateCounter(name)); - counter.Add(metricValue); - } - } - - private void RecordMeanEvent(IDictionary eventPayload, string counterName) - { - if (!eventPayload.TryGetValue("Mean", out var payloadValue)) - { - return; - } - - if (TryConvertToLong(payloadValue, out long metricValue)) - { - var histogram = HistogramInstruments.GetOrAdd(counterName, name => _meter.CreateHistogram(name)); - histogram.Record(metricValue); - } - } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/Constants.cs b/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/Constants.cs deleted file mode 100644 index 35a4b6d09a7..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/Constants.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; - -namespace Microsoft.Extensions.Telemetry.Metering.Internal; - -internal sealed class Constants -{ - public static readonly Dictionary> DefaultCounters = new() - { - ["System.Runtime"] = new HashSet - { - "cpu-usage", - "working-set", - "time-in-gc", - "alloc-rate", - "exception-count", - "gen-2-gc-count", - "gen-2-size", - "monitor-lock-contention-count", - "active-timer-count", - "threadpool-queue-length", - "threadpool-thread-count" - }, - ["Microsoft-AspNetCore-Server-Kestrel"] = new HashSet - { - "connection-queue-length", - "request-queue-length", - }, - }; -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/EventCountersCollectorOptionsValidator.cs b/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/EventCountersCollectorOptionsValidator.cs deleted file mode 100644 index 0c70de10791..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/EventCountersCollectorOptionsValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Extensions.Options; - -namespace Microsoft.Extensions.Telemetry.Metering.Internal; - -[OptionsValidator] -internal sealed partial class EventCountersCollectorOptionsValidator : IValidateOptions -{ -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/EventCountersValidator.cs b/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/EventCountersValidator.cs deleted file mode 100644 index 8de12bb54db..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/EventCountersValidator.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.ComponentModel.DataAnnotations; -using System.Diagnostics.CodeAnalysis; -using Microsoft.Extensions.Options; -using Microsoft.Shared.Data.Validation; - -namespace Microsoft.Extensions.Telemetry.Metering.Internal; - -internal sealed class EventCountersValidator : IValidateOptions -{ - private const string MemberName = nameof(EventCountersCollectorOptions.Counters); - - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(EventCountersCollectorOptions))] - [UnconditionalSuppressMessage("Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = "Addressed by [DynamicDependency]")] - public ValidateOptionsResult Validate(string? name, EventCountersCollectorOptions options) - { - if (options.Counters is null) - { - // Nullness is covered in source-generated validator - return ValidateOptionsResult.Skip; - } - - var baseName = string.IsNullOrEmpty(name) - ? nameof(EventCountersCollectorOptions) - : name; - - var context = new ValidationContext(options); - var builder = new ValidateOptionsResultBuilder(); - var requiredAttribute = new RequiredAttribute(); - var lengthAttribute = new Microsoft.Shared.Data.Validation.LengthAttribute(1); - foreach (var pair in options.Counters) - { - if (pair.Value is null) - { - context.MemberName = MemberName + "[\"" + pair.Key + "\"]"; - context.DisplayName = baseName + "." + context.MemberName; - builder.AddResult(requiredAttribute.GetValidationResult(pair.Value, context)); - } - else if (pair.Value.Count == 0) - { - context.MemberName = MemberName + "[\"" + pair.Key + "\"]"; - context.DisplayName = baseName + "." + context.MemberName; - builder.AddResult(lengthAttribute.GetValidationResult(pair.Value, context)); - } - } - - return builder.Build(); - } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/Log.cs b/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/Log.cs deleted file mode 100644 index a0eaa079ba2..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Metering.Collectors.EventCounters/Internal/Log.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Extensions.Logging; - -namespace Microsoft.Extensions.Telemetry.Metering.Internal; - -#pragma warning disable S109 // Magic numbers should not be used -internal static partial class Log -{ - /// - /// Logs `Configured EventCounters options: {value}` at `Information` level. - /// - [LoggerMessage(0, LogLevel.Information, "Configured EventCounters options: {value}")] - internal static partial void ConfiguredEventCountersOptions(this ILogger logger, EventCountersCollectorOptions value); - - /// - /// Logs `Enabling event source: {name}` at `Information` level. - /// - [LoggerMessage(1, LogLevel.Information, "Enabling event source: {name}")] - internal static partial void EnablingEventSource(this ILogger logger, string name); - - /// - /// Logs `Invalid value {value} resulting in overflow exception during conversion` at `Warning` level. - /// - [LoggerMessage(2, LogLevel.Warning, "Invalid value {value} resulting in overflow exception during conversion")] - internal static partial void OverflowExceptionWhileConversion(this ILogger logger, object? value); - - /// - /// Logs `Invalid value {value} resulting in Format exception during conversion` at `Warning` level. - /// - [LoggerMessage(3, LogLevel.Warning, "Invalid value {value} resulting in Format exception during conversion")] - internal static partial void FormatExceptionWhileConversion(this ILogger logger, object? value); - - /// - /// Logs `Event name is null, eventSource {name}` at `Debug` level. - /// - [LoggerMessage(4, LogLevel.Debug, "Event name is null, eventSource {name}")] - internal static partial void EventNameIsNull(this ILogger logger, string name); - - /// - /// Logs `Payload is null for event {name}:{eventName}` at `Debug` level. - /// - [LoggerMessage(5, LogLevel.Debug, "Payload is null for event {eventSourceName}:{eventName}")] - internal static partial void PayloadIsNull(this ILogger logger, string eventSourceName, string eventName); - - /// - /// Logs `EventName: `{eventName}` is not `EventCounters`` at `Debug` level. - /// - [LoggerMessage(6, LogLevel.Debug, "EventName: `{eventName}` is not `EventCounters`")] - internal static partial void EventNameIsNotEventCounters(this ILogger logger, string eventName); - - /// - /// Logs `No counters registered for eventSource: {eventSourceName}` at `Debug` level. - /// - [LoggerMessage(7, LogLevel.Debug, "No counters registered for eventSource: {eventSourceName}")] - internal static partial void NoCountersRegisteredForEventSource(this ILogger logger, string eventSourceName); - - /// - /// Logs `Event payload for event {eventSourceName}:{eventName} does not contain `CounterType`` at `Trace` level. - /// - [LoggerMessage(8, LogLevel.Trace, "Event payload for event {eventSourceName}:{eventName} does not contain `CounterType`")] - internal static partial void EventPayloadDoesNotContainCounterType(this ILogger logger, string eventSourceName, string eventName); - - /// - /// Logs `Event payload for event {eventSourceName}:{eventName} does not contain `Name`` at `Trace` level. - /// - [LoggerMessage(9, LogLevel.Trace, "Event payload for event {eventSourceName}:{eventName} does not contain `Name`")] - internal static partial void EventPayloadDoesNotContainName(this ILogger logger, string eventSourceName, string eventName); - - /// - /// Logs `Counter name is empty for event {eventSourceName}:{eventName}` at `Trace` level. - /// - [LoggerMessage(10, LogLevel.Trace, "Counter name is empty for event {eventSourceName}:{eventName}")] - internal static partial void CounterNameIsEmpty(this ILogger logger, string eventSourceName, string eventName); - - /// - /// Logs `Counter `{counterName}` for eventSource {eventSourceName}:{eventName} not enabled` at `Trace` level. - /// - [LoggerMessage(11, LogLevel.Trace, "Counter `{counterName}` for eventSource {eventSourceName}:{eventName} not enabled")] - internal static partial void CounterNotEnabled(this ILogger logger, string counterName, string eventSourceName, string eventName); - - /// - /// Logs `Received event {eventSourceName}:{counterName} of type {type}` at `Trace` level. - /// - [LoggerMessage(12, LogLevel.Trace, "Received event {eventSourceName}:{counterName} of type {type}")] - internal static partial void ReceivedEventOfType(this ILogger logger, string eventSourceName, object counterName, string? type); -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.json b/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.json index d1c1ba5eb26..4dfaa9d1f2c 100644 --- a/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.json +++ b/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.json @@ -1,44 +1,6 @@ { "Name": "Microsoft.Extensions.Telemetry, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "Types": [ - { - "Type": "class Microsoft.Extensions.Telemetry.Metering.EventCountersCollectorOptions", - "Stage": "Stable", - "Methods": [ - { - "Member": "Microsoft.Extensions.Telemetry.Metering.EventCountersCollectorOptions.EventCountersCollectorOptions();", - "Stage": "Stable" - } - ], - "Properties": [ - { - "Member": "System.Collections.Generic.IDictionary> Microsoft.Extensions.Telemetry.Metering.EventCountersCollectorOptions.Counters { get; set; }", - "Stage": "Stable" - }, - { - "Member": "bool Microsoft.Extensions.Telemetry.Metering.EventCountersCollectorOptions.IncludeRecommendedDefault { get; set; }", - "Stage": "Experimental" - }, - { - "Member": "System.TimeSpan Microsoft.Extensions.Telemetry.Metering.EventCountersCollectorOptions.SamplingInterval { get; set; }", - "Stage": "Stable" - } - ] - }, - { - "Type": "static class Microsoft.Extensions.Telemetry.Metering.EventCountersExtensions", - "Stage": "Stable", - "Methods": [ - { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.Telemetry.Metering.EventCountersExtensions.AddEventCounterCollector(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure);", - "Stage": "Stable" - }, - { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.Telemetry.Metering.EventCountersExtensions.AddEventCounterCollector(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.Configuration.IConfigurationSection section);", - "Stage": "Stable" - } - ] - }, { "Type": "static class Microsoft.Extensions.Telemetry.Latency.LatencyConsoleExtensions", "Stage": "Stable", diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/Auxiliary/TestEventSource.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/Auxiliary/TestEventSource.cs deleted file mode 100644 index 70f8e94fc76..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/Auxiliary/TestEventSource.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.Tracing; - -namespace Microsoft.Extensions.Telemetry.Metering.Test.Auxiliary; - -internal class TestEventSource : EventSource -{ - public EventCommandEventArgs? EventCommandEventArgs { get; set; } - - public TestEventSource(string eventSourceName) - : base(eventSourceName) - { - } - - protected override void OnEventCommand(EventCommandEventArgs command) - { - EventCommandEventArgs = command; - base.OnEventCommand(command); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/Auxiliary/TestUtils.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/Auxiliary/TestUtils.cs deleted file mode 100644 index a91d9e9ccf1..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/Auxiliary/TestUtils.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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 Microsoft.Extensions.Options; - -using static Microsoft.Extensions.Options.Options; - -namespace Microsoft.Extensions.Telemetry.Metering.Test.Auxiliary; - -public static class TestUtils -{ - public const string SystemRuntime = "System.Runtime"; - - public static IOptions CreateOptions(string eventSource, string counterName) - { - var options = Create(new EventCountersCollectorOptions()); - options.Value.Counters.Add(eventSource, new HashSet { counterName }); - options.Value.SamplingInterval = TimeSpan.FromMilliseconds(1); - - return options; - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersExtensionsTest.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersExtensionsTest.cs deleted file mode 100644 index 5f0bbb79976..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersExtensionsTest.cs +++ /dev/null @@ -1,190 +0,0 @@ -// 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.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Hosting.Testing; -using Microsoft.Extensions.Options; -using Moq; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Metering.Test; - -public class EventCountersExtensionsTest -{ - [Fact] - public void AddEventCounterCollector_Throws_WhenServiceCollectionNull() - { - Assert.Throws(() => EventCountersExtensions.AddEventCounterCollector(null!, Mock.Of())); - Assert.Throws(() => EventCountersExtensions.AddEventCounterCollector(null!, options => { })); - } - - [Fact] - public void AddEventCounterCollector_Throws_WhenActionNull() - { - var services = new ServiceCollection(); - Assert.Throws(() => services.AddEventCounterCollector((Action)null!)); - } - - [Fact] - public void AddEventCounterCollector_Throws_WhenConfigSectionNull() - { - var services = new ServiceCollection(); - Assert.Throws(() => services.AddEventCounterCollector((IConfigurationSection)null!)); - } - - [Fact] - public async Task AddEventCounterCollector_ValidatesOptionsInSection() - { - using var host = FakeHost.CreateBuilder() - .ConfigureAppConfiguration(static x => x.AddJsonFile("appsettings.json")) - .ConfigureServices(static (context, services) => services - .AddEventCounterCollector(context.Configuration.GetSection("InvalidConfig"))) - .Build(); - - await Assert.ThrowsAsync(() => host.RunAsync()); - } - - [Fact] - public async Task AddEventCounterCollector_ValidatesNullCountersInOptions() - { - using var host = FakeHost.CreateBuilder() - .ConfigureServices(static services => services - .AddEventCounterCollector(static x => x.Counters = null!)) - .Build(); - - await Assert.ThrowsAsync(() => host.RunAsync()); - } - - [Fact] - public async Task AddEventCounterCollector_ValidatesCountersNullValueInOptions() - { - using var host = FakeHost.CreateBuilder() - .ConfigureServices(static services => services - .AddEventCounterCollector(static x => x.Counters.Add("key", null!))) - .Build(); - - await Assert.ThrowsAsync(() => host.RunAsync()); - } - - [Fact] - public async Task AddEventCounterCollector_ValidatesCountersEmptyValueInOptions() - { - using var host = FakeHost.CreateBuilder() - .ConfigureServices(static services => services - .AddEventCounterCollector(static x => x.Counters.Add("key", new HashSet()))) - .Build(); - - await Assert.ThrowsAsync(() => host.RunAsync()); - } - - [Theory] - [InlineData(0)] - [InlineData(-1)] - [InlineData(601)] - public async Task AddEventCounterCollector_ValidatesSamplingIntervalInOptions(int interval) - { - using var host = FakeHost.CreateBuilder() - .ConfigureServices(services => services - .AddEventCounterCollector(x => x.SamplingInterval = TimeSpan.FromSeconds(interval))) - .Build(); - - await Assert.ThrowsAsync(() => host.RunAsync()); - } - - [Fact] - public void AddEventCounterCollector_AddsOptions() - { - var services = new ServiceCollection(); - services.AddEventCounterCollector(static x => x.Counters.Add("key", new SortedSet { "foo" })); - - using var serviceProvider = services.BuildServiceProvider(); - var options = serviceProvider.GetRequiredService>(); - Assert.NotNull(options); - Assert.NotNull(options.Value); - Assert.NotNull(options.Value.Counters); - Assert.NotEmpty(options.Value.Counters); - Assert.Equal(1D, options.Value.SamplingInterval.TotalSeconds); - } - - [Fact] - public void AddEventCounterCollectorWithAction_AddsOptions() - { - var services = new ServiceCollection(); - services.AddEventCounterCollector(static o => - o.Counters.Add("eventSource", new HashSet { "eventCounter" })); - - using var serviceProvider = services.BuildServiceProvider(); - var options = serviceProvider.GetRequiredService>(); - Assert.Contains("eventSource", options.Value.Counters); - } - - [Fact] - public void AddEventCounterCollectorWithSection_AddsOptions() - { - EventCountersCollectorOptions? opts = null; - var configRoot = new ConfigurationBuilder() - .AddInMemoryCollection( - new Dictionary - { - { ConfigurationPath.Combine("SectionName", nameof(opts.SamplingInterval)), "00:01:00" }, - { ConfigurationPath.Combine("SectionName", nameof(opts.Counters), "Key1", "0"), "foo" }, - { ConfigurationPath.Combine("SectionName", nameof(opts.Counters), "Key1", "1"), "bar" }, - { ConfigurationPath.Combine("SectionName", nameof(opts.Counters), "Key1", "2"), "bar" }, // Duplicate value - { ConfigurationPath.Combine("SectionName", nameof(opts.Counters), "Key2", "0"), "baz" }, - }) - .Build(); - - var serviceCollection = new ServiceCollection(); - serviceCollection.AddEventCounterCollector(configRoot.GetSection("SectionName")); - - using var serviceProvider = serviceCollection.BuildServiceProvider(); - var options = serviceProvider.GetRequiredService>(); - Assert.NotNull(options); - Assert.NotNull(options.Value); - - opts = options.Value; - Assert.NotNull(opts.Counters); - Assert.Equal(2, opts.Counters.Count); - Assert.Equal(2, opts.Counters["Key1"].Count); - Assert.Equal(1, opts.Counters["Key2"].Count); - - Assert.Contains("foo", opts.Counters["Key1"]); - Assert.Contains("bar", opts.Counters["Key1"]); - Assert.Contains("baz", opts.Counters["Key2"]); - Assert.Equal(1D, opts.SamplingInterval.TotalMinutes); - } - - [Fact] - public void AddEventCounterCollectorWithSectionFromFile_AddsOptions() - { - var configRoot = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .Build(); - - var serviceCollection = new ServiceCollection(); - serviceCollection.AddEventCounterCollector(configRoot.GetSection("ValidConfig")); - - using var serviceProvider = serviceCollection.BuildServiceProvider(); - var options = serviceProvider.GetRequiredService>(); - Assert.NotNull(options); - Assert.NotNull(options.Value); - - var opts = options.Value; - Assert.NotNull(opts.Counters); - Assert.Equal(2, opts.Counters.Count); - Assert.Equal(4, opts.Counters["Key1"].Count); - Assert.Equal(1, opts.Counters["Key2"].Count); - - Assert.Contains("one", opts.Counters["Key1"]); - Assert.Contains("two", opts.Counters["Key1"]); - Assert.Contains("three", opts.Counters["Key1"]); - Assert.Contains("four", opts.Counters["Key1"]); - Assert.Contains("ABC", opts.Counters["Key2"]); - Assert.Equal(2D, opts.SamplingInterval.TotalMinutes); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersListenerTest.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersListenerTest.cs deleted file mode 100644 index af2bbc24af2..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersListenerTest.cs +++ /dev/null @@ -1,986 +0,0 @@ -// 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.Diagnostics.Metrics; -using System.Diagnostics.Tracing; -#if !NETFRAMEWORK -using System.Threading; -#endif -using AutoFixture; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Microsoft.Extensions.Telemetry.Metering; -using Microsoft.Extensions.Telemetry.Metering.Test.Auxiliary; -using Microsoft.Extensions.Telemetry.Testing.Metering; -using Xunit; - -using static Microsoft.Extensions.Options.Options; - -namespace Microsoft.Extensions.Telemetry.Metering.Test; - -public class EventCountersListenerTest -{ - private const string EventName = "EventCounters"; - private readonly Fixture _fixture = new(); - private readonly IOptions _options; - private readonly string _eventSourceName; - private readonly string _counterName; - private readonly ILogger _logger; - - public EventCountersListenerTest() - { - _options = Create(new EventCountersCollectorOptions()); - _eventSourceName = _fixture.Create(); - _counterName = _fixture.Create(); - _options.Value.Counters.Add(_eventSourceName, new HashSet { _counterName }); - _logger = NullLoggerFactory.Instance.CreateLogger(); - } - - [Fact] - public void EventCountersListener_WhenNullOptions_Throws() - { - using var meter = new Meter(); - Assert.Throws(() => new EventCountersListener(Create(null!), meter)); - } - - private sealed class InstrumentCounter : IDisposable - { - private readonly MeterListener _meterListener = new(); - - public InstrumentCounter(Meter meter) - { - _meterListener = new MeterListener - { - InstrumentPublished = (instrument, _) => - { - if (instrument.Meter == meter) - { - Count++; - } - } - }; - - _meterListener.Start(); - } - - public void Dispose() => _meterListener.Dispose(); - public int Count { get; private set; } - } - - [Fact] - public void EventCountersListener_Ignores_NonStructuredEvents() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter, _logger); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - CounterType = "Sum", - Name = _counterName, - Increment = 1 - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_UnknownCounterTypes() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = _fixture.Create(), Name = _counterName, Increment = 1 } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_ValidateEventCounterInterval_SetCorrectly() - { - using var meter = new Meter(); - - var intervalInSeconds = 2; - - IOptions options = Create(new EventCountersCollectorOptions - { - SamplingInterval = TimeSpan.FromSeconds(intervalInSeconds), - }); - - options.Value.Counters.Add(_eventSourceName, new HashSet { _counterName }); - - using var eventSource = new TestEventSource(_eventSourceName); - using var listener = new EventCountersListener(options, meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - Assert.True(eventSource.EventCommandEventArgs?.Arguments?.ContainsKey("EventCounterIntervalSec")); - Assert.Equal($"{intervalInSeconds}", eventSource.EventCommandEventArgs?.Arguments?["EventCounterIntervalSec"]); - } - - [Fact] - public void EventCountersListener_OnEventWritten_Ignores_WithNullCounter() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { _eventSourceName, null! } - }; - - IOptions options = Create(new EventCountersCollectorOptions - { - Counters = counters - }); - - using var eventSource = new TestEventSource(_eventSourceName); - using var listener = new EventCountersListener(options, meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_OnEventWritten_Ignores_WithEmptyCounterName() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - string emptyCounterName = string.Empty; - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = emptyCounterName, Increment = 1 } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_OnEventWritten_Ignores_WithoutEventName() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new TestEventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - eventSource.Write(null, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_OnEventWritten_Ignores_WithoutPayload() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new TestEventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - eventSource.Write(_eventSourceName, - new EventSourceOptions { Level = EventLevel.LogAlways }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_OnEventWritten_Ignores_WithoutEventData() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new TestEventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - eventSource.Write(_eventSourceName); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_UnknownEventSources() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - SendMeanEvent(meter, - MeanEventProperties.All, - eventSourceName: _fixture.Create(), - counterName: _counterName, - eventName: EventName); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_UnknownEvents() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - SendMeanEvent(meter, - MeanEventProperties.All, - eventSourceName: _eventSourceName, - counterName: _counterName, - eventName: _fixture.Create()); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_UnknownCounters() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - _options.Value.Counters.Clear(); - - SendMeanEvent(meter, MeanEventProperties.All); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_EventsWithoutCounterType() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - SendMeanEvent(meter, MeanEventProperties.All ^ MeanEventProperties.WithType); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_EventsWithoutName() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - SendMeanEvent(meter, MeanEventProperties.All ^ MeanEventProperties.WithName); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_MeanEventsWithoutMean() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - SendMeanEvent(meter, MeanEventProperties.All ^ MeanEventProperties.WithMean); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_Empty_Counters_Maps() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - var eventSourceName = Guid.NewGuid().ToString(); - var options = new EventCountersCollectorOptions(); - options.Counters.Add(eventSourceName, new HashSet()); - using var eventSource = new EventSource(eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Theory] - [InlineData(EventLevel.Verbose)] - [InlineData(EventLevel.Informational)] - [InlineData(EventLevel.Warning)] - [InlineData(EventLevel.Error)] - public void EventCountersListener_Ignores_LowEventLevels(EventLevel eventLevel) - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = eventLevel }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Theory] - [InlineData(EventLevel.Critical)] - [InlineData(EventLevel.LogAlways)] - public void EventCountersListener_Emits_WhenSumCounterMatches(EventLevel eventLevel) - { - using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = eventLevel }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - var latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - } - - [Fact] - public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoublePositiveInfinity() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = double.PositiveInfinity } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoubleNegativeInfinity() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = double.NegativeInfinity } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoubleNan() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = double.NaN } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIncorrectlyFormatted() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = "?/str." } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Emits_WhenSourceIsCreatedAfterListener() - { - using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); - - using var listener = new EventCountersListener(_options, meter); - using var eventSource = new EventSource(_eventSourceName); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - var latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - } - - [Fact] - public void EventCountersListener_Ignores_SumCounterWithoutIncrement() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Emits_WhenMeanCounterMatches() - { - using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); - - SendMeanEvent(meter, MeanEventProperties.All); - - var latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - } - - [Fact] - public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedHistogram() - { - using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Mean", Name = _counterName, Mean = 1 } - }); - - var latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - Assert.Single(listener.HistogramInstruments); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Mean", Name = _counterName, Mean = 1 } - }); - - latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.Single(listener.HistogramInstruments); - } - - [Fact] - public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedCounter() - { - using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - var latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - Assert.Single(listener.CounterInstruments); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.Single(listener.CounterInstruments); - } - - [Fact] - public void EventCountersListener_Ignores_WhenMeanCounterMatches_MeanValueDoubleNan() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - SendMeanEvent(meter, MeanEventProperties.All, _eventSourceName, _counterName, EventName, meanValue: double.NaN); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_WhenMeanCounterMatches_MeanValuePositiveInfinity() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - SendMeanEvent(meter, MeanEventProperties.All, _eventSourceName, _counterName, EventName, meanValue: double.PositiveInfinity); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_WhenMeanCounterMatches_MeanValueNegativeInfinity() - { - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - SendMeanEvent(meter, MeanEventProperties.All, _eventSourceName, _counterName, EventName, meanValue: double.NegativeInfinity); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_Ignores_WhenCounterNameIsNotRegistered() - { - var options = Create(new EventCountersCollectorOptions()); - options.Value.Counters.Add(_eventSourceName, new HashSet { _counterName }); - - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(options, meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = "randomCounterName", Increment = 1 } - }); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_IncludeRecommendedDefault_AddsDefaultCountersToCounterList() - { - using var meter = new Meter(); - using var listener = new EventCountersListener(Create(new EventCountersCollectorOptions { IncludeRecommendedDefault = true }), meter); - - Assert.NotEmpty(listener.Counters); - - foreach (var counterSet in listener.Counters) - { - if (counterSet.Key == "System.Runtime") - { - Assert.Equal(11, counterSet.Value.Count); - Assert.Contains("cpu-usage", counterSet.Value); - Assert.Contains("working-set", counterSet.Value); - Assert.Contains("time-in-gc", counterSet.Value); - Assert.Contains("alloc-rate", counterSet.Value); - Assert.Contains("exception-count", counterSet.Value); - Assert.Contains("gen-2-gc-count", counterSet.Value); - Assert.Contains("gen-2-size", counterSet.Value); - Assert.Contains("monitor-lock-contention-count", counterSet.Value); - Assert.Contains("active-timer-count", counterSet.Value); - Assert.Contains("threadpool-queue-length", counterSet.Value); - Assert.Contains("threadpool-thread-count", counterSet.Value); - } - else if (counterSet.Key == "Microsoft-AspNetCore-Server-Kestrel") - { - Assert.Equal(2, counterSet.Value.Count); - Assert.Contains("connection-queue-length", counterSet.Value); - Assert.Contains("request-queue-length", counterSet.Value); - } - else - { - Assert.Fail($"Unexpected counter set {counterSet.Key}"); - } - } - } - - [Fact] - public void EventCountersListener_DuplicateEntriesAreIgnored() - { - using var meter = new Meter(); - IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { _eventSourceName, new HashSet { _counterName } }, - { TestUtils.SystemRuntime, new HashSet { "active-timer-count", "cpu-usage" } } - }; - using var listener = new EventCountersListener(Create(new EventCountersCollectorOptions { IncludeRecommendedDefault = true, Counters = counters }), meter); - - Assert.NotEmpty(listener.Counters); - - foreach (var counterSet in listener.Counters) - { - if (counterSet.Key == "System.Runtime") - { - Assert.Equal(11, counterSet.Value.Count); - Assert.Contains("cpu-usage", counterSet.Value); - Assert.Contains("working-set", counterSet.Value); - Assert.Contains("time-in-gc", counterSet.Value); - Assert.Contains("alloc-rate", counterSet.Value); - Assert.Contains("exception-count", counterSet.Value); - Assert.Contains("gen-2-gc-count", counterSet.Value); - Assert.Contains("gen-2-size", counterSet.Value); - Assert.Contains("monitor-lock-contention-count", counterSet.Value); - Assert.Contains("active-timer-count", counterSet.Value); - Assert.Contains("threadpool-queue-length", counterSet.Value); - Assert.Contains("threadpool-thread-count", counterSet.Value); - } - else if (counterSet.Key == "Microsoft-AspNetCore-Server-Kestrel") - { - Assert.Equal(2, counterSet.Value.Count); - Assert.Contains("connection-queue-length", counterSet.Value); - Assert.Contains("request-queue-length", counterSet.Value); - } - else if (counterSet.Key == _eventSourceName) - { - Assert.Single(counterSet.Value); - Assert.Contains(_counterName, counterSet.Value); - } - else - { - Assert.Fail($"Unexpected counter set {counterSet.Key}"); - } - } - } - - [Fact] - public void EventCountersListener_UsingWildcard_EnablesAllCountersForSource() - { - var firstCounterName = "randomCounterName"; - var secondCounterName = "randomCounterName2"; - - using var meter = new Meter(); - using var metricCollector1 = new MetricCollector(meter, $"{_eventSourceName}|{firstCounterName}"); - using var metricCollector2 = new MetricCollector(meter, $"{_eventSourceName}|{secondCounterName}"); - IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { _eventSourceName, new HashSet { "*" } } - }; - - using var eventSource = new EventSource(_eventSourceName); - using var listener = new EventCountersListener(Create(new EventCountersCollectorOptions { Counters = counters }), meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Mean", Name = firstCounterName, Mean = 1 } - }); - - var latest = metricCollector1.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - Assert.Single(listener.HistogramInstruments); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Mean", Name = secondCounterName, Mean = 1 } - }); - - latest = metricCollector2.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - Assert.Equal(2, listener.HistogramInstruments.Count); - } - - [Flags] - private enum MeanEventProperties - { - None = 0, - WithName = 1, - WithType = 2, - WithMean = 4, - All = WithMean | WithType - } - - private void SendMeanEvent(Meter meter, MeanEventProperties meanEventProperties) - { - SendMeanEvent(meter, meanEventProperties, _eventSourceName, _counterName, EventName); - } - - private void SendMeanEvent(Meter meter, - MeanEventProperties meanEventProperties, - string eventSourceName, - string counterName, - string eventName, - double meanValue = 1) - { - using var eventSource = new EventSource(eventSourceName); - using var listener = new EventCountersListener(_options, meter); - - switch (meanEventProperties) - { - case MeanEventProperties.All: - eventSource.Write(eventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Mean", Name = counterName, Mean = meanValue } - }); - break; - - case MeanEventProperties.All ^ MeanEventProperties.WithMean: - eventSource.Write(eventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Mean", Name = counterName } - }); - break; - - case MeanEventProperties.All ^ MeanEventProperties.WithName: - eventSource.Write(eventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Mean", Mean = meanValue } - }); - break; - - case MeanEventProperties.All ^ MeanEventProperties.WithType: - eventSource.Write(eventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { Name = counterName, Mean = meanValue } - }); - break; - - default: - throw new ArgumentOutOfRangeException(nameof(meanEventProperties), meanEventProperties, "unsupported combination"); - } - } - -#if !NETFRAMEWORK - [Fact] - public void MeanCounter() - { - string counterName = "active-timer-count"; - string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; - var options = TestUtils.CreateOptions(TestUtils.SystemRuntime, counterName); - - using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - - using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter, metricName); - RunListener(options, meter, eventWaitHandle); - - var latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.True(latest.Value >= 0); - } - - [Fact] - public void SumCounter() - { - string counterName = "alloc-rate"; - string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; - var options = TestUtils.CreateOptions(TestUtils.SystemRuntime, counterName); - - using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - - using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter, metricName); - RunListener(options, meter, eventWaitHandle); - - var latest = metricCollector.LastMeasurement; - Assert.NotNull(latest); - Assert.True(latest.Value >= 0); - } - - [Fact] - public void EventCountersListener_IncludeRecommendedDefault_AddsDefaultCounters() - { - string counterName = "alloc-rate"; - string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; - - using var meter = new Meter(); - using var metricCollector1 = new MetricCollector(meter, metricName); - using var metricCollector2 = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); - - IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { _eventSourceName, new HashSet { _counterName } }, - }; - - IOptions options = Create(new EventCountersCollectorOptions - { - IncludeRecommendedDefault = true, - Counters = counters, - SamplingInterval = TimeSpan.FromMilliseconds(10) - }); - - using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - RunListener(options, meter, eventWaitHandle); - - var latest = metricCollector1.LastMeasurement; - Assert.NotNull(latest); - Assert.True(latest.Value >= 0); - - using var eventSource = new TestEventSource(_eventSourceName); - using var listener = new EventCountersListener(options, meter); - - eventSource.Write(EventName, - new EventSourceOptions { Level = EventLevel.LogAlways }, - new - { - payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } - }); - - latest = metricCollector2.LastMeasurement; - Assert.NotNull(latest); - Assert.Equal(1, latest.Value); - } - - [Fact] - public void EventCountersListener_IncludeRecommendedDefault_AndDuplicateEntryInProvidedCounters() - { - string counterName = "alloc-rate"; - string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; - - using var meter = new Meter(); - using var metricCollector1 = new MetricCollector(meter, metricName); - using var metricCollector2 = new MetricCollector(meter, $"{TestUtils.SystemRuntime}|active-timer-count"); - - IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { TestUtils.SystemRuntime, new HashSet { "active-timer-count" } } - }; - - IOptions options = Create(new EventCountersCollectorOptions - { - IncludeRecommendedDefault = true, - Counters = counters, - SamplingInterval = TimeSpan.FromMilliseconds(10) - }); - - using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - RunListener(options, meter, eventWaitHandle); - - var latest = metricCollector1.LastMeasurement; - Assert.NotNull(latest); - Assert.True(latest.Value >= 0); - - latest = metricCollector2.LastMeasurement; - Assert.NotNull(latest); - Assert.True(latest.Value >= 0); - } - - [Fact] - public void EventCountersListener_IncludeRecommendedDefault_SetToFalse_DoesNot_IncludesDefaultCounters() - { - string counterName = "alloc-rate"; - string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; - - using var meter = new Meter(); - using var instrumentCounter = new InstrumentCounter(meter); - - IOptions options = Create(new EventCountersCollectorOptions()); - - using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - RunListener(options, meter, eventWaitHandle); - - Assert.Equal(0, instrumentCounter.Count); - } - - [Fact] - public void EventCountersListener_UsingWildcard_IncludesAllCountersFromSource() - { - string counterName = "alloc-rate"; - string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; - - using var meter = new Meter(); - using var metricCollector1 = new MetricCollector(meter, metricName); - using var metricCollector2 = new MetricCollector(meter, $"{TestUtils.SystemRuntime}|active-timer-count"); - - IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { TestUtils.SystemRuntime, new HashSet { "*" } } - }; - - IOptions options = Create(new EventCountersCollectorOptions - { - Counters = counters, - SamplingInterval = TimeSpan.FromMilliseconds(10) - }); - - using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); - RunListener(options, meter, eventWaitHandle); - - var latest = metricCollector1.LastMeasurement; - Assert.NotNull(latest); - Assert.True(latest.Value >= 0); - - latest = metricCollector2.LastMeasurement; - Assert.NotNull(latest); - Assert.True(latest.Value >= 0); - } - - private static void RunListener(IOptions options, Meter meter, WaitHandle eventWaitHandle) - { - using var eventListener = new EventCountersListener(options, meter); - eventWaitHandle.WaitOne(TimeSpan.FromMilliseconds(1000)); - } -#endif -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersValidatorTest.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersValidatorTest.cs deleted file mode 100644 index 749fe0ad028..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersValidatorTest.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using Microsoft.Extensions.Telemetry.Metering.Internal; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Metering.Test; - -public class EventCountersValidatorTest -{ - private const string FieldName = nameof(EventCountersCollectorOptions.Counters); - - private readonly EventCountersValidator _validator = new(); - - [Fact] - public void ShouldValidateCustomNamedOptions_NullSet() - { - const string OptionsName = "MyCustomName"; - - var options = new EventCountersCollectorOptions(); - options.Counters["key"] = null!; - - var result = _validator.Validate(OptionsName, options); - Assert.True(result.Failed); - Assert.Equal($"Counters[\"key\"]: The {OptionsName}.{FieldName}[\"key\"] field is required.", result.FailureMessage); - } - - [Fact] - public void ShouldValidateCustomNamedOptions_EmptySet() - { - const string OptionsName = nameof(EventCountersCollectorOptions); - - var options = new EventCountersCollectorOptions(); - options.Counters["key"] = new HashSet(); - - var result = _validator.Validate(string.Empty, options); - Assert.True(result.Failed); - Assert.Equal($"Counters[\"key\"]: The field {OptionsName}.{FieldName}[\"key\"] length must be greater or equal than 1.", result.FailureMessage); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/appsettings.json b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/appsettings.json index 975140356d3..2f8a0b8a772 100644 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/appsettings.json +++ b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/appsettings.json @@ -25,15 +25,5 @@ "Probability": 1.0 } } - }, - "ValidConfig": { - "SamplingInterval": "00:02:00", - "Counters": { - "Key1": [ "one", "two", "three", "four" ], - "Key2": [ "ABC" ] - } - }, - "InvalidConfig": { - "SamplingInterval": "00:00:00" } } diff --git a/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/TimerTests.cs b/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/TimerTests.cs index f0c7f3e02eb..4ef71e78ce5 100644 --- a/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/TimerTests.cs +++ b/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/TimerTests.cs @@ -238,7 +238,7 @@ public void WaiterRemovedAfterDispose() } #if RELEASE // In Release only since this might not work if the timer reference being tracked by the debugger - [Fact] + [Fact(Skip = "Flaky on .NET Framework")] public void WaiterRemovedWhenCollectedWithoutDispose() { var timer1Counter = 0;