From 2feb2f390ba41723684f051073e13ed6a16b9803 Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 19 Jun 2023 06:52:41 -0700 Subject: [PATCH] MetricCollector revamp. - This now has a considerably simpler API surface allowing you to collect data for a single instrument at a time. - You can now snapshot all recorded measurements and apply various filters over lists of measurements. - General shape of the API looks similar in style to the FakeLogger. You can now take snapshots of existing state, as a client you never get your hands on "live" state, which helps with racy code. - There is now a WaitForMeasurementsAsync function which is helpful to simplify testing async logic. --- ...nsions.ObjectPool.DependencyInjection.json | 43 + .../Logging/FakeLogCollector.cs | 13 +- .../Metering/CollectedMeasurement.cs | 112 +++ .../Metering/Internal/AggregationType.cs | 11 - .../Metering/MeasurementExtensions.cs | 105 +++ .../Metering/MetricCollector.MeterListener.cs | 152 ---- .../Metering/MetricCollector.cs | 562 +++++------- .../Metering/MetricCollectorT.cs | 30 - .../Metering/MetricValue.cs | 112 --- .../Metering/MetricValuesHolder.cs | 234 ----- ...icrosoft.Extensions.Telemetry.Testing.json | 198 ++--- .../HeaderParsingFeatureTests.cs | 16 +- .../Metering/HttpMeteringTests.cs | 195 +++-- .../TelemetryHealthChecksPublisherTest.cs | 17 +- .../FaultInjectionTelemetryHandlerTests.cs | 24 +- .../Metering/HttpMeteringHandlerTests.Ext.cs | 80 +- .../Metering/HttpMeteringHandlerTests.cs | 349 ++++---- .../FaultInjectionTelemetryHandlerTests.cs | 10 +- .../Polly/Internals/PolicyMeteringTests.cs | 68 +- .../Internal/PipelineMeteringTests.cs | 51 +- .../Metering/MeasurementExtensionsTests.cs | 241 ++++++ .../Metering/MetricCollectorTests.Counter.cs | 210 ----- .../MetricCollectorTests.Histogram.cs | 209 ----- .../MetricCollectorTests.ObservableCounter.cs | 298 ------- .../MetricCollectorTests.ObservableGauge.cs | 297 ------- ...cCollectorTests.ObservableUpdownCounter.cs | 297 ------- .../MetricCollectorTests.UpDownCounter.cs | 215 ----- .../Metering/MetricCollectorTests.cs | 809 ++++++------------ .../Metering/MetricValueTests.cs | 20 - .../Metering/MetricValuesHolderTests.cs | 247 ------ .../EventCountersListenerTest.cs | 212 ++--- 31 files changed, 1591 insertions(+), 3846 deletions(-) create mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/CollectedMeasurement.cs delete mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/Internal/AggregationType.cs create mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MeasurementExtensions.cs delete mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.MeterListener.cs delete mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollectorT.cs delete mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValue.cs delete mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValuesHolder.cs create mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MeasurementExtensionsTests.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Counter.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Histogram.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableCounter.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableGauge.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableUpdownCounter.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.UpDownCounter.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValueTests.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValuesHolderTests.cs diff --git a/src/Libraries/Microsoft.Extensions.ObjectPool.DependencyInjection/Microsoft.Extensions.ObjectPool.DependencyInjection.json b/src/Libraries/Microsoft.Extensions.ObjectPool.DependencyInjection/Microsoft.Extensions.ObjectPool.DependencyInjection.json index e69de29bb2d..4937e76101e 100644 --- a/src/Libraries/Microsoft.Extensions.ObjectPool.DependencyInjection/Microsoft.Extensions.ObjectPool.DependencyInjection.json +++ b/src/Libraries/Microsoft.Extensions.ObjectPool.DependencyInjection/Microsoft.Extensions.ObjectPool.DependencyInjection.json @@ -0,0 +1,43 @@ +{ + "Name": "Microsoft.Extensions.ObjectPool.DependencyInjection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "Types": [ + { + "Type": "sealed class Microsoft.Extensions.ObjectPool.DependencyInjectionPoolOptions", + "Stage": "Experimental", + "Methods": [ + { + "Member": "Microsoft.Extensions.ObjectPool.DependencyInjectionPoolOptions.DependencyInjectionPoolOptions();", + "Stage": "Experimental" + } + ], + "Properties": [ + { + "Member": "int Microsoft.Extensions.ObjectPool.DependencyInjectionPoolOptions.Capacity { get; set; }", + "Stage": "Experimental" + } + ] + }, + { + "Type": "static class Microsoft.Extensions.ObjectPool.ObjectPoolServiceCollectionExtensions", + "Stage": "Experimental", + "Methods": [ + { + "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.ObjectPool.ObjectPoolServiceCollectionExtensions.AddPooled(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action? configure = null);", + "Stage": "Experimental" + }, + { + "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.ObjectPool.ObjectPoolServiceCollectionExtensions.AddPooled(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action? configure = null);", + "Stage": "Experimental" + }, + { + "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.ObjectPool.ObjectPoolServiceCollectionExtensions.ConfigurePool(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure);", + "Stage": "Experimental" + }, + { + "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.ObjectPool.ObjectPoolServiceCollectionExtensions.ConfigurePools(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.Configuration.IConfigurationSection section);", + "Stage": "Experimental" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Logging/FakeLogCollector.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Logging/FakeLogCollector.cs index 33d731291c6..dfa91b65a24 100644 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Logging/FakeLogCollector.cs +++ b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Logging/FakeLogCollector.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Options; using Microsoft.Shared.Diagnostics; @@ -63,15 +62,21 @@ public void Clear() /// /// Gets the records that are held by the collector. /// + /// Setting this to will atomically clear the set of accumulated log records. /// /// The list of records tracked to date by the collector. /// - [SuppressMessage("Minor Code Smell", "S4049:Properties should be preferred", Justification = "Not suitable for a property since it allocates.")] - public IReadOnlyList GetSnapshot() + public IReadOnlyList GetSnapshot(bool clear = false) { lock (_records) { - return _records.ToArray(); + var records = _records.ToArray(); + if (clear) + { + _records.Clear(); + } + + return records; } } diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/CollectedMeasurement.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/CollectedMeasurement.cs new file mode 100644 index 00000000000..c3624db32c0 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/CollectedMeasurement.cs @@ -0,0 +1,112 @@ +// 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; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Microsoft.Shared.Diagnostics; + +namespace Microsoft.Extensions.Telemetry.Testing.Metering; + +/// +/// Represents a single measurement performed by an instrument. +/// +/// The type of metric measurement value. +[Experimental] +[DebuggerDisplay("{DebuggerToString(),nq}")] +public sealed class CollectedMeasurement + where T : struct +{ + /// + /// Initializes a new instance of the class. + /// + /// The measurement's value. + /// The dimensions of this measurement. + /// The time that the measurement occurred at. + internal CollectedMeasurement(T value, ReadOnlySpan> tags, DateTimeOffset timestamp) + { + var d = new Dictionary(); + foreach (var tag in tags) + { + d[tag.Key] = tag.Value; + } + + Tags = d; + Timestamp = timestamp; + Value = value; + } + + /// + /// Gets a measurement's value. + /// + public T Value { get; } + + /// + /// Gets a timestamp indicating when the measurement was recorded. + /// + public DateTimeOffset Timestamp { get; } + + /// + /// Gets the measurement's dimensions. + /// + public IReadOnlyDictionary Tags { get; } + + /// + /// Checks that the measurement includes a specific set of tags with specific values. + /// + /// The set of tags to check. + /// if all the tags exist in the measurement with matching values, otherwise . + public bool ContainsTags(params KeyValuePair[] tags) + { + foreach (var kvp in Throw.IfNull(tags)) + { + if (!Tags.TryGetValue(kvp.Key, out var value)) + { + return false; + } + + if (!object.Equals(kvp.Value, value)) + { + return false; + } + } + + return true; + } + + /// + /// Checks that the measurement includes a specific set of tags with any value. + /// + /// The set of tag names to check. + /// if all the tags exist in the measurement, otherwise . + public bool ContainsTags(params string[] tags) + { + foreach (var key in Throw.IfNull(tags)) + { + if (!Tags.ContainsKey(key)) + { + return false; + } + } + + return true; + } + + /// + /// Checks that the measurement has an exactly matching set of tags with specific values. + /// + /// The set of tags to check. + /// if all the tags exist in the measurement with matching values, otherwise . + public bool MatchesTags(params KeyValuePair[] tags) => ContainsTags(tags) && (Tags.Count == tags.Length); + + /// + /// Checks that the measurement has a exactly matching set of tags with any value. + /// + /// The set of tag names to check. + /// if all the tag names exist in the measurement, otherwise . + public bool MatchesTags(params string[] tags) => ContainsTags(Throw.IfNull(tags)) && (Tags.Count == tags.Length); + + internal string DebuggerToString() => $"{Value} @ {Timestamp.ToString("HH:mm:ss.ffff", CultureInfo.InvariantCulture)}"; +} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/Internal/AggregationType.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/Internal/AggregationType.cs deleted file mode 100644 index aa7e4fa783c..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/Internal/AggregationType.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. - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Internal; - -internal enum AggregationType -{ - Save, - Aggregate, - SaveOrUpdate -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MeasurementExtensions.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MeasurementExtensions.cs new file mode 100644 index 00000000000..9986306f002 --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MeasurementExtensions.cs @@ -0,0 +1,105 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Metrics; +using System.Linq; + +namespace Microsoft.Extensions.Telemetry.Testing.Metering; + +/// +/// Extensions to simplify working with lists of measurements. +/// +[Experimental] +public static class MeasurementExtensions +{ + /// + /// Filters a list of measurements based on subset tags matching. + /// + /// The type of measurement value. + /// The original full list of measurements. + /// The set of tags to match against. Only measurements that have at least these matching tags are returned. + /// A list of matching measurements. + public static IEnumerable> ContainsTags(this IEnumerable> measurements, params KeyValuePair[] tags) + where T : struct + => measurements.Where(m => m.ContainsTags(tags)); + + /// + /// Filters a list of measurements based on subset tag matching. + /// + /// The type of measurement value. + /// The original full list of measurements. + /// The set of tags to match against. Only measurements that have at least these matching tag names are returned. + /// A list of matching measurements. + public static IEnumerable> ContainsTags(this IEnumerable> measurements, params string[] tags) + where T : struct + => measurements.Where(m => m.ContainsTags(tags)); + + /// + /// Filters a list of measurements based on exact tag matching. + /// + /// The type of measurement value. + /// The original full list of measurements. + /// The set of tags to match against. Only measurements that have exactly those matching tags are returned. + /// A list of matching measurements. + public static IEnumerable> MatchesTags(this IEnumerable> measurements, params KeyValuePair[] tags) + where T : struct + => measurements.Where(m => m.MatchesTags(tags)); + + /// + /// Filters a list of measurements based on exact tag name matching. + /// + /// The type of measurement value. + /// The original full list of measurements. + /// The set of tags to match against. Only measurements that have exactly those matching tag names are returned. + /// A list of matching measurements. + public static IEnumerable> MatchesTags(this IEnumerable> measurements, params string[] tags) + where T : struct + => measurements.Where(m => m.MatchesTags(tags)); + + /// + /// Process the series of measurements adding all values together to produce a final count, identical to what a instrument would produce. + /// + /// The type of measurement value. + /// The list of measurements to process. + /// The resulting count. + public static T EvaluateAsCounter(this IEnumerable> measurements) + where T : struct + { +#pragma warning disable CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + return measurements switch + { + IEnumerable> l => (T)(object)ByteSum(l), + IEnumerable> l => (T)(object)ShortSum(l), + IEnumerable> l => (T)(object)l.Sum(m => m.Value), + IEnumerable> l => (T)(object)l.Sum(m => m.Value), + IEnumerable> l => (T)(object)l.Sum(m => m.Value), + IEnumerable> l => (T)(object)l.Sum(m => m.Value), + IEnumerable> l => (T)(object)l.Sum(m => m.Value), + }; +#pragma warning restore CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + + static byte ByteSum(IEnumerable> measurements) + { + byte sum = 0; + foreach (var measurement in measurements) + { + sum += measurement.Value; + } + + return sum; + } + + static short ShortSum(IEnumerable> measurements) + { + short sum = 0; + foreach (var measurement in measurements) + { + sum += measurement.Value; + } + + return sum; + } + } +} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.MeterListener.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.MeterListener.cs deleted file mode 100644 index 2385fd01bed..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.MeterListener.cs +++ /dev/null @@ -1,152 +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.Generic; -using System.Diagnostics.Metrics; -using System.Linq; -using Microsoft.Extensions.Telemetry.Testing.Metering; -using Microsoft.Extensions.Telemetry.Testing.Metering.Internal; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering; - -public partial class MetricCollector -{ - private void OnInstrumentPublished(Instrument instrument, MeterListener listener) - { - var matchedByMeter = _meter is not null && ReferenceEquals(instrument.Meter, _meter); - var matchedByMeterName = _meter is null && (_meterNames!.Length == 0 || _meterNames!.Any(x => x.StartsWith(instrument.Meter.Name, StringComparison.Ordinal))); - - if (matchedByMeter || matchedByMeterName) - { - RegisterInstrument(instrument); - - listener.EnableMeasurementEvents(instrument, this); - } - } - - private void CollectMeasurement(Instrument instrument, T value, ReadOnlySpan> tags, object? state) - where T : struct - { - if (state == this) - { - var metricValuesHolder = GetMetricValuesHolder(instrument); - metricValuesHolder.ReceiveValue(value, tags); - } - } - - private void RegisterInstrument(Instrument instrument) - { - Type genericDefinedType = instrument.GetType().GetGenericTypeDefinition(); - - AggregationType aggregationType = AggregationType.Save; - Dictionary allValuesDictionary = null!; - - if (genericDefinedType == typeof(Counter<>)) - { - aggregationType = AggregationType.Aggregate; - allValuesDictionary = _allCounters; - } - else if (genericDefinedType == typeof(Histogram<>)) - { - aggregationType = AggregationType.Save; - allValuesDictionary = _allHistograms; - } - else if (genericDefinedType == typeof(UpDownCounter<>)) - { - aggregationType = AggregationType.Aggregate; - allValuesDictionary = _allUpDownCounters; - } - else if (genericDefinedType == typeof(ObservableCounter<>)) - { - aggregationType = AggregationType.SaveOrUpdate; - allValuesDictionary = _allObservableCounters; - } - else if (genericDefinedType == typeof(ObservableGauge<>)) - { - aggregationType = AggregationType.Save; - allValuesDictionary = _allObservableGauges; - } - else if (genericDefinedType == typeof(ObservableUpDownCounter<>)) - { - aggregationType = AggregationType.SaveOrUpdate; - allValuesDictionary = _allObservableUpDownCounters; - } - - Type measurementValueType = instrument.GetType().GetGenericArguments()[0]; - - if (measurementValueType == typeof(int)) - { - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(int)]; - _ = metricsValuesDictionary.GetOrAdd(instrument.Name, new MetricValuesHolder(_timeProvider, aggregationType, instrument)); - } - else if (measurementValueType == typeof(byte)) - { - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(byte)]; - _ = metricsValuesDictionary.GetOrAdd(instrument.Name, new MetricValuesHolder(_timeProvider, aggregationType, instrument)); - } - else if (measurementValueType == typeof(short)) - { - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(short)]; - _ = metricsValuesDictionary.GetOrAdd(instrument.Name, new MetricValuesHolder(_timeProvider, aggregationType, instrument)); - } - else if (measurementValueType == typeof(long)) - { - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(long)]; - _ = metricsValuesDictionary.GetOrAdd(instrument.Name, new MetricValuesHolder(_timeProvider, aggregationType, instrument)); - } - else if (measurementValueType == typeof(double)) - { - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(double)]; - _ = metricsValuesDictionary.GetOrAdd(instrument.Name, new MetricValuesHolder(_timeProvider, aggregationType, instrument)); - } - else if (measurementValueType == typeof(float)) - { - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(float)]; - _ = metricsValuesDictionary.GetOrAdd(instrument.Name, new MetricValuesHolder(_timeProvider, aggregationType, instrument)); - } - else if (measurementValueType == typeof(decimal)) - { - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(decimal)]; - _ = metricsValuesDictionary.GetOrAdd(instrument.Name, new MetricValuesHolder(_timeProvider, aggregationType, instrument)); - } - } - - private MetricValuesHolder GetMetricValuesHolder(Instrument instrument) - where T : struct - { - var instrumentType = instrument.GetType(); - - Dictionary allValuesDictionary = null!; - - if (instrumentType == typeof(Counter)) - { - allValuesDictionary = _allCounters; - } - else if (instrumentType == typeof(Histogram)) - { - allValuesDictionary = _allHistograms; - } - else if (instrumentType == typeof(UpDownCounter)) - { - allValuesDictionary = _allUpDownCounters; - } - else if (instrumentType == typeof(ObservableCounter)) - { - allValuesDictionary = _allObservableCounters; - } - else if (instrumentType == typeof(ObservableGauge)) - { - allValuesDictionary = _allObservableGauges; - } - else if (instrumentType == typeof(ObservableUpDownCounter)) - { - allValuesDictionary = _allObservableUpDownCounters; - } - - var metricsValuesDictionary = (ConcurrentDictionary>)allValuesDictionary[typeof(T)]; - - return metricsValuesDictionary[instrument.Name]; - } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.cs index 87825c9011f..6ab1eb3e3ce 100644 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.cs +++ b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollector.cs @@ -2,446 +2,336 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Metrics; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Shared.Diagnostics; namespace Microsoft.Extensions.Telemetry.Testing.Metering; /// -/// The helper class to automatically capture metering data that has been recorded -/// by instruments created by . +/// Collects the measurements published from an or . /// -/// -/// This type has been designed to be used only for testing purposes. -/// +/// The type of metric data being recorded. [Experimental] -[DebuggerDisplay("Count = {Count}")] -public partial class MetricCollector : IDisposable +[DebuggerDisplay("{_measurements.Count} measurements")] +public sealed class MetricCollector : IDisposable + where T : struct { - private readonly MeterListener _listener; - private readonly string[]? _meterNames; -#pragma warning disable CA2213 // Disposable fields should be disposed - private readonly Meter? _meter; -#pragma warning restore CA2213 // Disposable fields should be disposed + private static readonly HashSet _supportedTs = new() + { + typeof(int), + typeof(byte), + typeof(short), + typeof(long), + typeof(float), + typeof(double), + typeof(decimal), + }; + + private readonly MeterListener _meterListener = new(); + private readonly List> _measurements = new(); + private readonly List _waiters = new(); private readonly TimeProvider _timeProvider; - - private readonly Dictionary _allCounters; - private readonly Dictionary _allHistograms; - private readonly Dictionary _allUpDownCounters; - private readonly Dictionary _allObservableCounters; - private readonly Dictionary _allObservableGauges; - private readonly Dictionary _allObservableUpDownCounters; + private bool _disposed; + private Instrument? _instrument; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The names of .NET to capture measurements from. - /// The instance. - /// - /// This constructor is applicable for the scenario when metering data generated - /// by active instances which matches - /// the one from the list is to be captured. - /// - public MetricCollector(IEnumerable meterNames, TimeProvider? timeProvider = null) - : this(meterNames, null, timeProvider, false) + /// The to record measurements from. + /// The time provider to use, or to use the system time provider. + public MetricCollector(Instrument instrument, TimeProvider? timeProvider = null) + : this(timeProvider) { + _instrument = Throw.IfNull(instrument); + _meterListener.SetMeasurementEventCallback(OnMeasurementRecorded); + _meterListener.EnableMeasurementEvents(instrument); + _meterListener.Start(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The instance. - /// - /// This constructor is applicable for the scenario when metering data - /// generated by all the active instances - /// in the application is to be captured. - /// - public MetricCollector(TimeProvider? timeProvider = null) - : this(Array.Empty(), null, timeProvider, false) + /// The to record measurements from. + /// The time provider to use, or to use the system time provider. + public MetricCollector(ObservableInstrument instrument, TimeProvider? timeProvider = null) + : this(timeProvider) { + _instrument = Throw.IfNull(instrument); + _meterListener.SetMeasurementEventCallback(OnMeasurementRecorded); + _meterListener.EnableMeasurementEvents(instrument); + _meterListener.Start(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The instance to capture metering data. - /// The instance. + /// The scope of the meter that publishes the instrument to record. + /// Take caution when using Meters in the global scope (scope == null). This interacts with + /// static mutable data and tests doing this should not be run in parallel with each other. + /// + /// The name of the meter that publishes the instrument to record. + /// The name of the instrument to record. + /// The time provider to use, or to use the system time provider. /// - /// This constructor is applicable for the scenario when metering data - /// generated by a specific instance is to be captured. + /// Both the meter name and scope are used to identity the meter of interest. /// - public MetricCollector(Meter meter, TimeProvider? timeProvider = null) - : this(null, meter, timeProvider, true) + public MetricCollector(object? meterScope, string meterName, string instrumentName, TimeProvider? timeProvider = null) + : this(timeProvider) { + _ = Throw.IfNullOrEmpty(meterName); + _ = Throw.IfNullOrEmpty(instrumentName); + + Initialize(instrument => Equals(instrument.Meter.Scope, meterScope) && instrument.Meter.Name == meterName && instrument.Name == instrumentName); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The instance to capture metering data. - /// The instance. - /// - /// This constructor is applicable for the scenario when metering data - /// generated by a specific instance is to be captured. - /// - public MetricCollector(Instrument instrument, TimeProvider? timeProvider = null) - : this(new[] { Throw.IfNull(instrument).Name }, instrument.Meter, timeProvider, true) + /// The meter that publishes the instrument to record. + /// The name of the instrument to record. + /// The time provider to use, or to use the system time provider. + public MetricCollector(Meter meter, string instrumentName, TimeProvider? timeProvider = null) + : this(timeProvider) { + _ = Throw.IfNull(meter); + _ = Throw.IfNullOrEmpty(instrumentName); + + Initialize(instrument => ReferenceEquals(instrument.Meter, meter) && instrument.Name == instrumentName); } - private MetricCollector(IEnumerable? meterNames, Meter? meter, TimeProvider? timeProvider, bool applyMeterFiltering) + private MetricCollector(TimeProvider? timeProvider) { - if (applyMeterFiltering) - { - _meter = Throw.IfNull(meter); - } - else + if (!_supportedTs.Contains(typeof(T))) { - _meterNames = Throw.IfNull(meterNames).ToArray(); + var str = string.Join(", ", _supportedTs.Select(t => t.Name).ToArray()); + throw new InvalidOperationException($"MetricCollector can only be created for the following types: {str}."); } _timeProvider = timeProvider ?? TimeProvider.System; - - _listener = new MeterListener - { - InstrumentPublished = OnInstrumentPublished - }; - - _listener.SetMeasurementEventCallback(CollectMeasurement); - _listener.SetMeasurementEventCallback(CollectMeasurement); - _listener.SetMeasurementEventCallback(CollectMeasurement); - _listener.SetMeasurementEventCallback(CollectMeasurement); - _listener.SetMeasurementEventCallback(CollectMeasurement); - _listener.SetMeasurementEventCallback(CollectMeasurement); - _listener.SetMeasurementEventCallback(CollectMeasurement); - - _allCounters = CreateMetricsValuesDictionary(); - _allHistograms = CreateMetricsValuesDictionary(); - _allUpDownCounters = CreateMetricsValuesDictionary(); - _allObservableGauges = CreateMetricsValuesDictionary(); - _allObservableCounters = CreateMetricsValuesDictionary(); - _allObservableUpDownCounters = CreateMetricsValuesDictionary(); - - _listener.Start(); } /// - /// Clears all the captured metering data. + /// Disposes the and stops recording measurements. /// - public void Clear() + public void Dispose() { - ClearValues(_allCounters); - ClearValues(_allHistograms); - ClearValues(_allUpDownCounters); - ClearValues(_allObservableCounters); - ClearValues(_allObservableGauges); - ClearValues(_allObservableUpDownCounters); - } - - /// - /// Gets the total number of measurements held by the collector. - /// - public int Count => CountValues(_allCounters) - + CountValues(_allHistograms) - + CountValues(_allUpDownCounters) - + CountValues(_allObservableCounters) - + CountValues(_allObservableGauges) - + CountValues(_allObservableUpDownCounters); + lock (_measurements) + { + if (_disposed) + { + return; + } - /// - /// Gets the object containing all the captured metering data that has been recorded by a counter instrument. - /// - /// The type of metric measurement value. - /// The metric name. - /// - /// The instance or if a counter with given was not found. - /// - public MetricValuesHolder? GetCounterValues(string counterName) - where T : struct => GetInstrumentCapturedData(_allCounters, counterName); + _disposed = true; - /// - /// Gets the object containing all the captured metering data that has been recorded by a histogram instrument. - /// - /// The type of metric measurement value. - /// The metric name. - /// - /// The instance or if a histogram with given was not found. - /// - public MetricValuesHolder? GetHistogramValues(string histogramName) - where T : struct => GetInstrumentCapturedData(_allHistograms, histogramName); + _meterListener.Dispose(); + _measurements.Clear(); - /// - /// Gets the object containing all the captured metering data that has been recorded by an updown counter instrument. - /// - /// The type of metric measurement value. - /// The metric name. - /// - /// The instance or if an updown counter with given was not found. - /// - public MetricValuesHolder? GetUpDownCounterValues(string updownCounterName) - where T : struct => GetInstrumentCapturedData(_allUpDownCounters, updownCounterName); + // wake up anybody still waiting and inform them of the bad news: their horse is dead... + foreach (var w in _waiters) + { + w.TaskSource.SetException(MakeObjectDisposedException()); + } + } + } /// - /// Gets the object containing all the captured metering data that has been recorded by an observable gauge instrument. + /// Gets the that is being recorded. /// - /// The type of metric measurement value. - /// The metric name. - /// - /// The instance or if an observable gauge with given was not found. - /// - public MetricValuesHolder? GetObservableGaugeValues(string observableGaugeName) - where T : struct => GetInstrumentCapturedData(_allObservableGauges, observableGaugeName); + /// + /// This may be until the instrument is published. + /// + public Instrument? Instrument => _instrument; /// - /// Gets the object containing all the captured metering data that has been recorded by an observable counter instrument. + /// Removes all accumulated measurements from the collector. /// - /// The type of metric measurement value. - /// The metric name. - /// - /// The instance or if an observable counter with given was not found. - /// - public MetricValuesHolder? GetObservableCounterValues(string observableCounterName) - where T : struct => GetInstrumentCapturedData(_allObservableCounters, observableCounterName); + public void Clear() + { + lock (_measurements) + { + ThrowIfDisposed(); + _measurements.Clear(); + } + } /// - /// Gets the object containing all the captured metering data that has been recorded by an observable updown counter instrument. + /// Gets a snapshot of measurements collected by this collector. /// - /// The type of metric measurement value. - /// The metric name. - /// - /// The instance or if an observable updown counter with given was not found. - /// - public MetricValuesHolder? GetObservableUpDownCounterValues(string observableUpDownCounterName) - where T : struct => GetInstrumentCapturedData(_allObservableUpDownCounters, observableUpDownCounterName); + /// Setting this to will atomically clear the set of accumulated measurements. + /// The measurements recorded by this collector, ordered by recording time. + public IReadOnlyList> GetMeasurementSnapshot(bool clear = false) + { + lock (_measurements) + { + ThrowIfDisposed(); - /// - /// Gets a measurement value recorded by a counter instrument. - /// - /// The type of metric measurement value. - /// The metric name. - /// The dimensions collection describing the measurement value. - /// The measurement value or if the value was not recorded. - public T? GetCounterValue(string counterName, params KeyValuePair[] tags) - where T : struct => GetCounterValues(counterName)?.GetValue(tags); + var measurements = _measurements.ToArray(); + if (clear) + { + _measurements.Clear(); + } - /// - /// Gets a measurement value recorded by a histogram instrument. - /// - /// The type of metric measurement value. - /// The metric name. - /// The dimensions collection describing the measurement value. - /// The measurement value or if the value was not recorded. - public T? GetHistogramValue(string histogramName, params KeyValuePair[] tags) - where T : struct => GetHistogramValues(histogramName)?.GetValue(tags); + return measurements; + } + } /// - /// Gets a measurement value recorded by an updown counter instrument. + /// Gets the latest measurement collected, if any. /// - /// The type of metric measurement value. - /// The metric name. - /// The dimensions collection describing the measurement value. - /// The measurement value or if the value was not recorded. - public T? GetUpDownCounterValue(string updownCounterName, params KeyValuePair[] tags) - where T : struct => GetUpDownCounterValues(updownCounterName)?.GetValue(tags); + public CollectedMeasurement? LastMeasurement + { + get + { + lock (_measurements) + { + ThrowIfDisposed(); + return _measurements.Count > 0 ? _measurements[_measurements.Count - 1] : null; + } + } + } /// - /// Gets a measurement value recorded by an observable counter instrument. + /// Returns a task that completes when the collector has collected a minimum number of measurements. /// - /// The type of metric measurement value. - /// The metric name. - /// The dimensions collection describing the measurement value. - /// The measurement value or if the value was not recorded. - public T? GetObservableCounterValue(string observableCounterName, params KeyValuePair[] tags) - where T : struct => GetObservableCounterValues(observableCounterName)?.GetValue(tags); + /// The minimum number of measurements to wait for. + /// A task that completes when the collector has collected the requisite number of measurements. + [SuppressMessage("Resilience", "R9A061:The async method doesn't support cancellation", Justification = "Not relevant in this case")] + public Task WaitForMeasurementsAsync(int numMeasurements) + { + _ = Throw.IfLessThan(numMeasurements, 1); - /// - /// Gets a measurement value recorded by an observable gauge instrument. - /// - /// The type of metric measurement value. - /// The metric name. - /// The dimensions collection describing the measurement value. - /// The measurement value or if the value was not recorded. - public T? GetObservableGaugeValue(string observableGaugeName, params KeyValuePair[] tags) - where T : struct => GetObservableGaugeValues(observableGaugeName)?.GetValue(tags); + lock (_measurements) + { + ThrowIfDisposed(); - /// - /// Gets a measurement value recorded by an observable updown counter instrument. - /// - /// The type of metric measurement value. - /// The metric name. - /// The dimensions collection describing the measurement value. - /// The measurement value or if the value was not recorded. - public T? GetObservableUpDownCounterValue(string observableUpDownCounterName, params KeyValuePair[] tags) - where T : struct => GetObservableUpDownCounterValues(observableUpDownCounterName)?.GetValue(tags); - -#pragma warning disable S4049 // Properties should be preferred - /// - /// Gets a list of all counters registered with this metrics collector. - /// - /// The type of metric measurement value. - /// Read only dictionary of . - public IReadOnlyDictionary>? GetAllCounters() - where T : struct => GetAllInstruments(_allCounters); + if (_measurements.Count >= numMeasurements) + { + return Task.CompletedTask; + } - /// - /// Gets a list of all UpDown counters registered with this metrics collector. - /// - /// The type of metric measurement value. - /// Read only dictionary of . - public IReadOnlyDictionary>? GetAllUpDownCounters() - where T : struct => GetAllInstruments(_allUpDownCounters); + var w = new Waiter(numMeasurements); + _waiters.Add(w); - /// - /// Gets a list of all histograms registered with this metrics collector. - /// - /// The type of metric measurement value. - /// Read only dictionary of . - public IReadOnlyDictionary>? GetAllHistograms() - where T : struct => GetAllInstruments(_allHistograms); + return w.TaskSource.Task; + } + } /// - /// Gets a list of all observable counters registered with this metrics collector. + /// Returns a task that completes when the collector has collected a minimum number of measurements. /// - /// The type of metric measurement value. - /// Read only dictionary of . - public IReadOnlyDictionary>? GetAllObservableCounters() - where T : struct => GetAllInstruments(_allObservableCounters); + /// The minimum number of measurements to wait for. + /// How long to wait. + /// The time provider against which the timeout executes. If this is then the provider + /// selected when creating the metric collector is used. + /// A task that completes when the collector has collected the requisite number of measurements. + [SuppressMessage("Resilience", "R9A061:The async method doesn't support cancellation", Justification = "Not relevant in this case")] + public Task WaitForMeasurementsAsync(int numMeasurements, TimeSpan timeout, TimeProvider? timeProvider = null) + { + _ = Throw.IfLessThan(numMeasurements, 1); + _ = Throw.IfLessThan(timeout.Ticks, 0); - /// - /// Gets a list of all observable UpDown counters registered with this metrics collector. - /// - /// The type of metric measurement value. - /// Read only dictionary of . - public IReadOnlyDictionary>? GetAllObservableUpDownCounters() - where T : struct => GetAllInstruments(_allObservableUpDownCounters); + lock (_measurements) + { + ThrowIfDisposed(); - /// - /// Gets a list of all observable Gauges registered with this metrics collector. - /// - /// The type of metric measurement value. - /// Read only dictionary of . - public IReadOnlyDictionary>? GetAllObservableGauges() - where T : struct => GetAllInstruments(_allObservableGauges); -#pragma warning restore S4049 // Properties should be preferred + if (_measurements.Count >= numMeasurements) + { + return Task.CompletedTask; + } - /// - /// Collects all observable instruments and records their measurements. - /// - public void CollectObservableInstruments() - { - _listener.RecordObservableInstruments(); - } + var w = new Waiter(numMeasurements); + _waiters.Add(w); - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); + return w.TaskSource.Task.WaitAsync(timeout, timeProvider ?? _timeProvider, CancellationToken.None); + } } /// - /// Dispose the el. + /// Scan all registered observable instruments. /// - /// Disposing. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _listener.Dispose(); - } - } - - private static void ClearValues(Dictionary instrumentValues) + public void RecordObservableInstruments() { - ClearValuesOf(instrumentValues); - ClearValuesOf(instrumentValues); - ClearValuesOf(instrumentValues); - ClearValuesOf(instrumentValues); - ClearValuesOf(instrumentValues); - ClearValuesOf(instrumentValues); - ClearValuesOf(instrumentValues); + ThrowIfDisposed(); + _meterListener.RecordObservableInstruments(); } - private static void ClearValuesOf(Dictionary instrumentValues) - where T : struct + private void Initialize(Func instrumentPredicate) { - var valuesDictionary = (ConcurrentDictionary>)instrumentValues[typeof(T)]; - - foreach (var kvp in valuesDictionary) + _meterListener.InstrumentPublished = (instrument, listener) => { - kvp.Value.Clear(); - } - } + if ((instrument is ObservableInstrument or Instrument) && instrumentPredicate(instrument)) + { + if (Interlocked.CompareExchange(ref _instrument, instrument, null) is null) + { + // no need to hear about new instruments being published + listener.InstrumentPublished = null; + + // get ready to listen to measurement events + listener.SetMeasurementEventCallback(OnMeasurementRecorded); + + // let the flood gates open + listener.EnableMeasurementEvents(instrument); + } + } + }; - private static int CountValues(Dictionary instrumentValues) - { - return - CountValuesOf(instrumentValues) - + CountValuesOf(instrumentValues) - + CountValuesOf(instrumentValues) - + CountValuesOf(instrumentValues) - + CountValuesOf(instrumentValues) - + CountValuesOf(instrumentValues) - + CountValuesOf(instrumentValues); + // start listening to stuff... + _meterListener.Start(); } - private static int CountValuesOf(Dictionary instrumentValues) - where T : struct + private void OnMeasurementRecorded(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) { - var valuesDictionary = (ConcurrentDictionary>)instrumentValues[typeof(T)]; + var m = new CollectedMeasurement(measurement, tags, _timeProvider.GetUtcNow()); - int count = 0; - foreach (var kvp in valuesDictionary) + lock (_measurements) { - count += kvp.Value.Count; + if (!_disposed) + { + // record the measurement + _measurements.Add(m); + + // wake up any waiters that need it + for (int i = _waiters.Count - 1; i >= 0; i--) + { + if (_measurements.Count >= _waiters[i].NumMeasurements) + { + // we use TrySetResult since the task may already be in the Cancelled state due to a timeout. + _ = _waiters[i].TaskSource.TrySetResult(true); + _waiters.RemoveAt(i); + } + } + } } - - return count; } - private static MetricValuesHolder? GetInstrumentCapturedData(Dictionary allInstrumentsValues, string instrumentName) - where T : struct + private void ThrowIfDisposed() { - if (!allInstrumentsValues.TryGetValue(typeof(T), out object? value)) + if (_disposed) { - throw new InvalidOperationException($"The type {typeof(T)} is not supported as a type for a metric measurement value"); + throw MakeObjectDisposedException(); } - - var instrumentsDictionary = (IReadOnlyDictionary>)value!; - _ = instrumentsDictionary.TryGetValue(instrumentName, out var metricValuesHolder); - - return metricValuesHolder; } - private static IReadOnlyDictionary>? GetAllInstruments(Dictionary allInstruments) - where T : struct + private ObjectDisposedException MakeObjectDisposedException() + => _instrument != null + ? new(nameof(MetricCollector), $"The metric collector instance for instrument '{_instrument.Name}' of meter '{_instrument.Meter.Name}' has been disposed.") + : new(nameof(MetricCollector)); + + private readonly struct Waiter { - if (!allInstruments.TryGetValue(typeof(T), out object? value)) + public Waiter(int numMeasurements) { - throw new InvalidOperationException($"The type {typeof(T)} is not supported as a type for a metric measurement value"); + NumMeasurements = numMeasurements; } - return (IReadOnlyDictionary>)value!; - } - - private static Dictionary CreateMetricsValuesDictionary() - { -#pragma warning disable CPR121 // Specify 'concurrencyLevel' and 'capacity' in the ConcurrentDictionary ctor. - return new Dictionary - { - [typeof(byte)] = new ConcurrentDictionary>(StringComparer.Ordinal), - [typeof(short)] = new ConcurrentDictionary>(StringComparer.Ordinal), - [typeof(int)] = new ConcurrentDictionary>(StringComparer.Ordinal), - [typeof(long)] = new ConcurrentDictionary>(StringComparer.Ordinal), - [typeof(float)] = new ConcurrentDictionary>(StringComparer.Ordinal), - [typeof(double)] = new ConcurrentDictionary>(StringComparer.Ordinal), - [typeof(decimal)] = new ConcurrentDictionary>(StringComparer.Ordinal), - }; -#pragma warning restore CPR121 // Specify 'concurrencyLevel' and 'capacity' in the ConcurrentDictionary ctor. + public int NumMeasurements { get; } + public TaskCompletionSource TaskSource { get; } = new(); } } diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollectorT.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollectorT.cs deleted file mode 100644 index ed7a6b56529..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricCollectorT.cs +++ /dev/null @@ -1,30 +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 System.Diagnostics.Metrics; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering; - -/// -/// The helper class to automatically capture metering information that has been recorded -/// by instruments created by . -/// -/// -/// This type has been designed to be used only for testing purposes. -/// -/// The type whose name is used as the instance name. -[Experimental] -#pragma warning disable SA1649 // File name should match first type name -public sealed class MetricCollector : MetricCollector -#pragma warning restore SA1649 // File name should match first type name -{ - /// - /// Initializes a new instance of the class. - /// - public MetricCollector() - : base(new[] { typeof(TMeterName).FullName! }) - { - } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValue.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValue.cs deleted file mode 100644 index 049775573e0..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValue.cs +++ /dev/null @@ -1,112 +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.CodeAnalysis; -using System.Threading; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering; - -/// -/// Represents the whole information about a single metric measurement. -/// -/// The type of metric measurement value. -[Experimental] -public sealed class MetricValue - where T : struct -{ - private int _isLockTaken; - - internal MetricValue(T measurement, KeyValuePair[] tags, DateTimeOffset timestamp) - { - Tags = tags; - Timestamp = timestamp; - Value = measurement; - } - - /// - /// Gets a measurement's value. - /// - public T Value { get; internal set; } - - /// - /// Gets a timestamp indicating when a measurement was recorded. - /// - public DateTimeOffset Timestamp { get; } - - /// - /// Gets a collection of measurement's dimensions. - /// - public IReadOnlyCollection> Tags { get; } - - /// - /// Gets a dimension value by a dimension name. - /// - /// The dimension name. - /// The dimension value or if the dimension value was not recorded. - public object? GetDimension(string dimensionName) - { - foreach (var kvp in Tags) - { - if (kvp.Key == dimensionName) - { - return kvp.Value; - } - } - - return null; - } - - internal void Add(T value) - { - SafeUpdate( - () => - { - var valueObj = (object)Value; - var valueToAddObj = (object)value; - -#pragma warning disable CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). - object result = value switch - { - byte => (byte)((byte)valueObj + (byte)valueToAddObj), - short => (short)((short)valueObj + (short)valueToAddObj), - int => (int)valueObj + (int)valueToAddObj, - long => (long)valueObj + (long)valueToAddObj, - float => (float)valueObj + (float)valueToAddObj, - double => (double)valueObj + (double)valueToAddObj, - decimal => (decimal)valueObj + (decimal)valueToAddObj, - _ => throw new InvalidOperationException($"The type {typeof(T).FullName} is not supported as a metering measurement value type."), - }; -#pragma warning restore CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). - - Value = (T)result; - }); - } - - internal void Update(T value) - { - SafeUpdate(() => Value = value); - } - - [ExcludeFromCodeCoverage] - private void SafeUpdate(Action action) - { - var sw = default(SpinWait); - - while (true) - { - if (Interlocked.Exchange(ref _isLockTaken, 1) == 0) - { - // Lock acquired - action(); - - // Release lock - _ = Interlocked.Exchange(ref _isLockTaken, 0); - break; - } - - sw.SpinOnce(); - } - } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValuesHolder.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValuesHolder.cs deleted file mode 100644 index ce4df6104ae..00000000000 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Metering/MetricValuesHolder.cs +++ /dev/null @@ -1,234 +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.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Metrics; -using System.Linq; -using System.Text; -using Microsoft.Extensions.Telemetry.Testing.Metering.Internal; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering; - -/// -/// The metric measurements holder that contains information belonging to one named metric. -/// -/// The type of metric measurement value. -[Experimental] -[DebuggerDisplay("Count = {Count}, LatestWrittenValue = {LatestWrittenValue}")] -public sealed class MetricValuesHolder - where T : struct -{ - private static readonly HashSet _supportedValueTypesAsDimensionValue = new() - { - typeof(int), - typeof(byte), - typeof(short), - typeof(long), - typeof(float), - typeof(double), - typeof(char), - }; - - private readonly TimeProvider _timeProvider; - private readonly AggregationType _aggregationType; - private readonly ConcurrentDictionary> _valuesTable; -#if NETCOREAPP3_1_OR_GREATER - private readonly ConcurrentBag> _values; -#else - private ConcurrentBag> _values; -#endif - private string? _latestWrittenKey; - - internal MetricValuesHolder(TimeProvider timeProvider, AggregationType aggregationType, Instrument instrument) - { - _timeProvider = timeProvider; - _aggregationType = aggregationType; - Instrument = instrument; - _values = new(); - _valuesTable = new(); - } - - /// - /// Gets the instrument that produces the values maintained by this holder. - /// - public Instrument Instrument { get; } - - /// - /// Gets all metric values recorded by the metric's instruments. - /// - public IReadOnlyCollection> AllValues => _values; - - /// - /// Gets the total number of values held by this instance. - /// - public int Count => _values.Count; - - /// - /// Gets the latest recorded metric measurement value. - /// - public T? LatestWrittenValue => LatestWritten?.Value; - - /// - /// Gets the object containing whole information about the latest recorded metric measurement. - /// - public MetricValue? LatestWritten => _latestWrittenKey == null ? null : _valuesTable[_latestWrittenKey]; - - /// - /// Gets a recorded metric measurement value by given dimensions. - /// - /// The dimensions of a metric measurement. - /// The metric measurement value or if it does not exist. - public T? GetValue(params KeyValuePair[] tags) - { - var tagsCopy = tags.ToArray(); - Array.Sort(tagsCopy, (x, y) => StringComparer.Ordinal.Compare(x.Key, y.Key)); - - var key = CreateKey(tagsCopy); - - _ = _valuesTable.TryGetValue(key, out var value); - - return value?.Value; - } - - /// - /// Clears all metric measurements information. - /// - public void Clear() - { -#if NETCOREAPP3_1_OR_GREATER - _values.Clear(); -#else - _values = new(); -#endif - _valuesTable.Clear(); - _latestWrittenKey = null; - } - - internal void ReceiveValue(T value, ReadOnlySpan> tags) - { - var tagsArray = tags.ToArray(); - Array.Sort(tagsArray, (x, y) => StringComparer.Ordinal.Compare(x.Key, y.Key)); - var key = CreateKey(tagsArray); - - switch (_aggregationType) - { - case AggregationType.Save: - Save(value, tagsArray, key); - break; - - case AggregationType.Aggregate: - SaveAndAggregate(value, tagsArray, key); - break; - - case AggregationType.SaveOrUpdate: - SaveOrUpdate(value, tagsArray, key); - break; - - default: - throw new InvalidOperationException($"Aggregation type {_aggregationType} is not supported."); - } - } - - private static string CreateKey(params KeyValuePair[] tags) - { - if (tags.Length == 0) - { - return string.Empty; - } - - const char TagSeparator = ';'; - const char KeyValueSeparator = ':'; - const char ArrayMemberSeparator = ','; - - var keyBuilder = new StringBuilder(); - - foreach (var kvp in tags) - { - _ = keyBuilder - .Append(kvp.Key) - .Append(KeyValueSeparator); - - if (kvp.Value is null) - { - _ = keyBuilder.Append(string.Empty); - } - else - { - var valueType = kvp.Value.GetType(); - - if (valueType == typeof(string) || (!valueType.IsArray && _supportedValueTypesAsDimensionValue.Contains(valueType))) - { - _ = keyBuilder.Append(kvp.Value); - } - else if (valueType.IsArray && _supportedValueTypesAsDimensionValue.Contains(valueType.GetElementType()!)) - { - var array = (Array)kvp.Value; - - _ = keyBuilder.Append('['); - - foreach (var item in array) - { - _ = keyBuilder - .Append(item) - .Append(ArrayMemberSeparator); - } - - _ = keyBuilder.Append(']'); - } - else - { - throw new InvalidOperationException($"The type {valueType.FullName} is not supported as a dimension value type."); - } - } - - _ = keyBuilder.Append(TagSeparator); - } - - return keyBuilder.ToString(); - } - - private void Save(T value, KeyValuePair[] tagsArray, string key) - { - var metricValue = new MetricValue(value, tagsArray, _timeProvider.GetUtcNow()); - - _latestWrittenKey = key; - - SaveMetricValue(key, metricValue); - } - - private void SaveAndAggregate(T value, KeyValuePair[] tagsArray, string key) - { - _latestWrittenKey = key; - - GetOrAdd(key, tagsArray).Add(value); - } - - private void SaveOrUpdate(T value, KeyValuePair[] tagsArray, string key) - { - _latestWrittenKey = key; - - GetOrAdd(key, tagsArray).Update(value); - } - - private MetricValue GetOrAdd(string key, KeyValuePair[] tagsArray) - { - return _valuesTable.GetOrAdd(key, - (_) => - { - var metricValue = new MetricValue(default, tagsArray, _timeProvider.GetUtcNow()); - _values.Add(metricValue); - - return metricValue; - }); - } - - private void SaveMetricValue(string key, MetricValue metricValue) - { - _values.Add(metricValue); - _ = _valuesTable.AddOrUpdate(key, metricValue, (_, _) => metricValue); - } -} diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Microsoft.Extensions.Telemetry.Testing.json b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Microsoft.Extensions.Telemetry.Testing.json index 647988cb56b..868f918f517 100644 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Microsoft.Extensions.Telemetry.Testing.json +++ b/src/Libraries/Microsoft.Extensions.Telemetry.Testing/Microsoft.Extensions.Telemetry.Testing.json @@ -1,6 +1,46 @@ { "Name": "Microsoft.Extensions.Telemetry.Testing, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "Types": [ + { + "Type": "sealed class Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement where T : struct", + "Stage": "Experimental", + "Methods": [ + { + "Member": "bool Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.ContainsTags(params System.Collections.Generic.KeyValuePair[] tags);", + "Stage": "Experimental" + }, + { + "Member": "bool Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.ContainsTags(params string[] tags);", + "Stage": "Experimental" + }, + { + "Member": "bool Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.MatchesTags(params System.Collections.Generic.KeyValuePair[] tags);", + "Stage": "Experimental" + }, + { + "Member": "bool Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.MatchesTags(params string[] tags);", + "Stage": "Experimental" + }, + { + "Member": "override string Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.ToString();", + "Stage": "Experimental" + } + ], + "Properties": [ + { + "Member": "System.Collections.Generic.IReadOnlyDictionary Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.Tags { get; }", + "Stage": "Experimental" + }, + { + "Member": "System.DateTimeOffset Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.Timestamp { get; }", + "Stage": "Experimental" + }, + { + "Member": "T Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement.Value { get; }", + "Stage": "Experimental" + } + ] + }, { "Type": "class Microsoft.Extensions.Telemetry.Testing.Logging.FakeLogCollector", "Stage": "Stable", @@ -22,7 +62,7 @@ "Stage": "Stable" }, { - "Member": "System.Collections.Generic.IReadOnlyList Microsoft.Extensions.Telemetry.Testing.Logging.FakeLogCollector.GetSnapshot();", + "Member": "System.Collections.Generic.IReadOnlyList Microsoft.Extensions.Telemetry.Testing.Logging.FakeLogCollector.GetSnapshot(bool clear = false);", "Stage": "Stable" } ], @@ -258,187 +298,79 @@ ] }, { - "Type": "class Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector : System.IDisposable", + "Type": "static class Microsoft.Extensions.Telemetry.Testing.Metering.MeasurementExtensions", "Stage": "Experimental", "Methods": [ { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector(System.Collections.Generic.IEnumerable meterNames, System.TimeProvider? timeProvider = null);", - "Stage": "Experimental" - }, - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector(System.TimeProvider? timeProvider = null);", - "Stage": "Experimental" - }, - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector(System.Diagnostics.Metrics.Meter meter, System.TimeProvider? timeProvider = null);", - "Stage": "Experimental" - }, - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector(System.Diagnostics.Metrics.Instrument instrument, System.TimeProvider? timeProvider = null);", - "Stage": "Experimental" - }, - { - "Member": "void Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.Clear();", - "Stage": "Experimental" - }, - { - "Member": "void Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.CollectObservableInstruments();", - "Stage": "Experimental" - }, - { - "Member": "void Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.Dispose();", - "Stage": "Experimental" - }, - { - "Member": "virtual void Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.Dispose(bool disposing);", - "Stage": "Experimental" - }, - { - "Member": "System.Collections.Generic.IReadOnlyDictionary>? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetAllCounters();", - "Stage": "Experimental" - }, - { - "Member": "System.Collections.Generic.IReadOnlyDictionary>? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetAllHistograms();", - "Stage": "Experimental" - }, - { - "Member": "System.Collections.Generic.IReadOnlyDictionary>? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetAllObservableCounters();", - "Stage": "Experimental" - }, - { - "Member": "System.Collections.Generic.IReadOnlyDictionary>? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetAllObservableGauges();", - "Stage": "Experimental" - }, - { - "Member": "System.Collections.Generic.IReadOnlyDictionary>? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetAllObservableUpDownCounters();", - "Stage": "Experimental" - }, - { - "Member": "System.Collections.Generic.IReadOnlyDictionary>? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetAllUpDownCounters();", - "Stage": "Experimental" - }, - { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetCounterValue(string counterName, params System.Collections.Generic.KeyValuePair[] tags);", - "Stage": "Experimental" - }, - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetCounterValues(string counterName);", - "Stage": "Experimental" - }, - { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetHistogramValue(string histogramName, params System.Collections.Generic.KeyValuePair[] tags);", + "Member": "static System.Collections.Generic.IEnumerable> Microsoft.Extensions.Telemetry.Testing.Metering.MeasurementExtensions.ContainsTags(this System.Collections.Generic.IEnumerable> measurements, params System.Collections.Generic.KeyValuePair[] tags);", "Stage": "Experimental" }, { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetHistogramValues(string histogramName);", + "Member": "static System.Collections.Generic.IEnumerable> Microsoft.Extensions.Telemetry.Testing.Metering.MeasurementExtensions.ContainsTags(this System.Collections.Generic.IEnumerable> measurements, params string[] tags);", "Stage": "Experimental" }, { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetObservableCounterValue(string observableCounterName, params System.Collections.Generic.KeyValuePair[] tags);", + "Member": "static T Microsoft.Extensions.Telemetry.Testing.Metering.MeasurementExtensions.EvaluateAsCounter(this System.Collections.Generic.IEnumerable> measurements);", "Stage": "Experimental" }, { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetObservableCounterValues(string observableCounterName);", + "Member": "static System.Collections.Generic.IEnumerable> Microsoft.Extensions.Telemetry.Testing.Metering.MeasurementExtensions.MatchesTags(this System.Collections.Generic.IEnumerable> measurements, params System.Collections.Generic.KeyValuePair[] tags);", "Stage": "Experimental" }, { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetObservableGaugeValue(string observableGaugeName, params System.Collections.Generic.KeyValuePair[] tags);", - "Stage": "Experimental" - }, - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetObservableGaugeValues(string observableGaugeName);", - "Stage": "Experimental" - }, - { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetObservableUpDownCounterValue(string observableUpDownCounterName, params System.Collections.Generic.KeyValuePair[] tags);", - "Stage": "Experimental" - }, - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetObservableUpDownCounterValues(string observableUpDownCounterName);", - "Stage": "Experimental" - }, - { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetUpDownCounterValue(string updownCounterName, params System.Collections.Generic.KeyValuePair[] tags);", - "Stage": "Experimental" - }, - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetUpDownCounterValues(string updownCounterName);", - "Stage": "Experimental" - } - ], - "Properties": [ - { - "Member": "int Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.Count { get; }", - "Stage": "Experimental" - } - ] - }, - { - "Type": "sealed class Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector : Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector", - "Stage": "Experimental", - "Methods": [ - { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector();", + "Member": "static System.Collections.Generic.IEnumerable> Microsoft.Extensions.Telemetry.Testing.Metering.MeasurementExtensions.MatchesTags(this System.Collections.Generic.IEnumerable> measurements, params string[] tags);", "Stage": "Experimental" } ] }, { - "Type": "sealed class Microsoft.Extensions.Telemetry.Testing.Metering.MetricValue where T : struct", + "Type": "sealed class Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector : System.IDisposable where T : struct", "Stage": "Experimental", "Methods": [ { - "Member": "object? Microsoft.Extensions.Telemetry.Testing.Metering.MetricValue.GetDimension(string dimensionName);", + "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector(System.Diagnostics.Metrics.Instrument instrument, System.TimeProvider? timeProvider = null);", "Stage": "Experimental" - } - ], - "Properties": [ + }, { - "Member": "System.Collections.Generic.IReadOnlyCollection> Microsoft.Extensions.Telemetry.Testing.Metering.MetricValue.Tags { get; }", + "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector(object? meterScope, string meterName, string instrumentName, System.TimeProvider? timeProvider = null);", "Stage": "Experimental" }, { - "Member": "System.DateTimeOffset Microsoft.Extensions.Telemetry.Testing.Metering.MetricValue.Timestamp { get; }", + "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.MetricCollector(System.Diagnostics.Metrics.Meter meter, string instrumentName, System.TimeProvider? timeProvider = null);", "Stage": "Experimental" }, { - "Member": "T Microsoft.Extensions.Telemetry.Testing.Metering.MetricValue.Value { get; internal set; }", + "Member": "void Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.Clear();", "Stage": "Experimental" - } - ] - }, - { - "Type": "sealed class Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder where T : struct", - "Stage": "Experimental", - "Methods": [ + }, { - "Member": "void Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder.Clear();", + "Member": "void Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.Dispose();", "Stage": "Experimental" }, { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder.GetValue(params System.Collections.Generic.KeyValuePair[] tags);", + "Member": "System.Collections.Generic.IReadOnlyList> Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.GetMeasurementSnapshot(bool clear = false);", "Stage": "Experimental" - } - ], - "Properties": [ + }, { - "Member": "System.Collections.Generic.IReadOnlyCollection> Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder.AllValues { get; }", + "Member": "void Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.RecordObservableInstruments();", "Stage": "Experimental" }, { - "Member": "int Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder.Count { get; }", + "Member": "System.Threading.Tasks.Task Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.WaitForMeasurementsAsync(int numMeasurements);", "Stage": "Experimental" }, { - "Member": "System.Diagnostics.Metrics.Instrument Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder.Instrument { get; }", + "Member": "System.Threading.Tasks.Task Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.WaitForMeasurementsAsync(int numMeasurements, System.TimeSpan timeout, System.TimeProvider? timeProvider = null);", "Stage": "Experimental" - }, + } + ], + "Properties": [ { - "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.MetricValue? Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder.LatestWritten { get; }", + "Member": "System.Diagnostics.Metrics.Instrument? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.Instrument { get; }", "Stage": "Experimental" }, { - "Member": "T? Microsoft.Extensions.Telemetry.Testing.Metering.MetricValuesHolder.LatestWrittenValue { get; }", + "Member": "Microsoft.Extensions.Telemetry.Testing.Metering.CollectedMeasurement? Microsoft.Extensions.Telemetry.Testing.Metering.MetricCollector.LastMeasurement { get; }", "Stage": "Experimental" } ] diff --git a/test/Libraries/Microsoft.AspNetCore.HeaderParsing.Tests/HeaderParsingFeatureTests.cs b/test/Libraries/Microsoft.AspNetCore.HeaderParsing.Tests/HeaderParsingFeatureTests.cs index 76b076883ae..8ec77acd22a 100644 --- a/test/Libraries/Microsoft.AspNetCore.HeaderParsing.Tests/HeaderParsingFeatureTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.HeaderParsing.Tests/HeaderParsingFeatureTests.cs @@ -127,7 +127,7 @@ public void TryParse_returns_default_on_header_not_found() public void TryParse_returns_false_on_error() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, @"R9.HeaderParsing.ParsingErrors"); Context.Request.Headers["Date"] = "Not a date."; var feature = new HeaderParsingFeature(Registry, _logger, meter) { Context = Context }; @@ -139,10 +139,10 @@ public void TryParse_returns_false_on_error() Assert.Equal("Can't parse header 'Date' due to 'Unable to parse date time offset value.'.", _logger.Collector.LatestRecord.Message); - var latest = metricCollector.GetCounterValues(@"R9.HeaderParsing.ParsingErrors")!.LatestWritten!; + var latest = metricCollector.LastMeasurement!; latest.Value.Should().Be(1); - latest.GetDimension("HeaderName").Should().Be("Date"); - latest.GetDimension("Kind").Should().Be("Unable to parse date time offset value."); + latest.Tags["HeaderName"].Should().Be("Date"); + latest.Tags["Kind"].Should().Be("Unable to parse date time offset value."); } [Fact] @@ -180,7 +180,7 @@ public void Dispose_resets_state_and_returns_to_pool() public void CachingWorks() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, @"R9.HeaderParsing.CacheAccess"); Context.Request.Headers[HeaderNames.CacheControl] = "max-age=604800"; @@ -194,9 +194,9 @@ public void CachingWorks() Assert.Same(value1, value2); Assert.Same(value1, value3); - var latest = metricCollector.GetCounterValues(@"R9.HeaderParsing.CacheAccess")!.LatestWritten!; + var latest = metricCollector.LastMeasurement!; latest.Value.Should().Be(1); - latest.GetDimension("HeaderName").Should().Be(HeaderNames.CacheControl); - latest.GetDimension("Type").Should().Be("Hit"); + latest.Tags["HeaderName"].Should().Be(HeaderNames.CacheControl); + latest.Tags["Type"].Should().Be("Hit"); } } diff --git a/test/Libraries/Microsoft.AspNetCore.Telemetry.Middleware.Tests/Metering/HttpMeteringTests.cs b/test/Libraries/Microsoft.AspNetCore.Telemetry.Middleware.Tests/Metering/HttpMeteringTests.cs index e9ae89ceda1..b74b7835e71 100644 --- a/test/Libraries/Microsoft.AspNetCore.Telemetry.Middleware.Tests/Metering/HttpMeteringTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Telemetry.Middleware.Tests/Metering/HttpMeteringTests.cs @@ -49,7 +49,7 @@ public HttpMeteringTests() [Fact] public async Task CanLogIncomingRequestMetric() { - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); using var host = await FakeHost.CreateBuilder() .ConfigureWebHost(webBuilder => webBuilder .UseTestServer() @@ -72,13 +72,13 @@ public async Task CanLogIncomingRequestMetric() using var client = host.GetTestClient(); using var response = client.GetAsync("/some/route/123").Result; - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("localhost", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET /some/route/{routeId}", latest.GetDimension(Metric.ReqName)); - Assert.Equal("401", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal("localhost", latest.Tags[Metric.ReqHost]); + Assert.Equal("GET /some/route/{routeId}", latest.Tags[Metric.ReqName]); + Assert.Equal("401", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); await host.StopAsync(); } @@ -88,7 +88,7 @@ public async Task CanLogIncomingRequestMetricWithEnricher() { IIncomingRequestMetricEnricher testEnricher = new TestEnricher(2, "2"); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); using var host = await FakeHost.CreateBuilder() .ConfigureWebHost(webBuilder => webBuilder @@ -119,16 +119,16 @@ public async Task CanLogIncomingRequestMetricWithEnricher() using var client = host.GetTestClient(); using var response = await client.GetAsync("/some/route/123").ConfigureAwait(false); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("localhost", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET /some/route/{routeId}", latest.GetDimension(Metric.ReqName)); - Assert.Equal("401", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); - Assert.Equal("test_value_1", latest.GetDimension("test_property_1")); - Assert.Equal("test_value_21", latest.GetDimension("test_property_21")); - Assert.Equal("test_value_22", latest.GetDimension("test_property_22")); + Assert.Equal("localhost", latest.Tags[Metric.ReqHost]); + Assert.Equal("GET /some/route/{routeId}", latest.Tags[Metric.ReqName]); + Assert.Equal("401", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); + Assert.Equal("test_value_1", latest.Tags["test_property_1"]); + Assert.Equal("test_value_21", latest.Tags["test_property_21"]); + Assert.Equal("test_value_22", latest.Tags["test_property_22"]); await host.StopAsync(); } @@ -136,8 +136,7 @@ public async Task CanLogIncomingRequestMetricWithEnricher() [Fact] public async Task CanLogIncomingRequestMetric_UseRoutingAfterMiddleware() { - using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); using var host = await FakeHost.CreateBuilder() .ConfigureWebHost(webBuilder => webBuilder @@ -164,13 +163,13 @@ public async Task CanLogIncomingRequestMetric_UseRoutingAfterMiddleware() using var client = host.GetTestClient(); using var response = await client.GetAsync("/some/route/456").ConfigureAwait(false); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("localhost", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET /some/route/{routeId}", latest.GetDimension(Metric.ReqName)); - Assert.Equal("503", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal("localhost", latest.Tags[Metric.ReqHost]); + Assert.Equal("GET /some/route/{routeId}", latest.Tags[Metric.ReqName]); + Assert.Equal("503", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); await host.StopAsync(); } @@ -178,8 +177,7 @@ public async Task CanLogIncomingRequestMetric_UseRoutingAfterMiddleware() [Fact] public async Task CanLogIncomingRequestMetric_TimeoutException() { - using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); using var host = await FakeHost.CreateBuilder() .ConfigureWebHost(webBuilder => webBuilder .UseTestServer() @@ -219,34 +217,33 @@ public async Task CanLogIncomingRequestMetric_TimeoutException() using var client = host.GetTestClient(); await Assert.ThrowsAsync(async () => await client.GetAsync("/some/route/456")); - - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement!; Assert.NotNull(latest); - Assert.Equal("localhost", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET /some/route/{routeId}", latest.GetDimension(Metric.ReqName)); - Assert.Equal("500", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(exceptionTypeName, latest.GetDimension(Metric.ExceptionType)); + Assert.Equal("localhost", latest.Tags[Metric.ReqHost]); + Assert.Equal("GET /some/route/{routeId}", latest.Tags[Metric.ReqName]); + Assert.Equal("500", latest.Tags[Metric.RspResultCode]); + Assert.Equal(exceptionTypeName, latest.Tags[Metric.ExceptionType]); metricCollector.Clear(); await Assert.ThrowsAsync(async () => await client.GetAsync("/some/route2/456")); - latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + latest = metricCollector.LastMeasurement!; Assert.NotNull(latest); - Assert.Equal("localhost", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET /some/route2/{routeId}", latest.GetDimension(Metric.ReqName)); - Assert.Equal("400", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(exceptionTypeName, latest.GetDimension(Metric.ExceptionType)); + Assert.Equal("localhost", latest.Tags[Metric.ReqHost]); + Assert.Equal("GET /some/route2/{routeId}", latest.Tags[Metric.ReqName]); + Assert.Equal("400", latest.Tags[Metric.RspResultCode]); + Assert.Equal(exceptionTypeName, latest.Tags[Metric.ExceptionType]); metricCollector.Clear(); await Assert.ThrowsAsync(async () => await client.GetAsync("/some/route3/456")); - latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("localhost", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET /some/route3/{routeId}", latest.GetDimension(Metric.ReqName)); - Assert.Equal("500", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(exceptionTypeName, latest.GetDimension(Metric.ExceptionType)); + Assert.Equal("localhost", latest.Tags[Metric.ReqHost]); + Assert.Equal("GET /some/route3/{routeId}", latest.Tags[Metric.ReqName]); + Assert.Equal("500", latest.Tags[Metric.RspResultCode]); + Assert.Equal(exceptionTypeName, latest.Tags[Metric.ExceptionType]); metricCollector.Clear(); await host.StopAsync(); @@ -260,7 +257,7 @@ public void InvokeAsync_HttpMeteringMiddleware_Success() string route = "/tenant/{tenantid}/users/{userId}"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List()); var context = new DefaultHttpContext(); @@ -272,13 +269,13 @@ public void InvokeAsync_HttpMeteringMiddleware_Success() middleware.InvokeAsync(context, _advanceTimeRequestDelegate).Wait(); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(hostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET " + route, latest.GetDimension(Metric.ReqName)); - Assert.Equal("200", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal(hostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("GET " + route, latest.Tags[Metric.ReqName]); + Assert.Equal("200", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); } [Fact] @@ -291,7 +288,7 @@ public void PerfStopwatch_ReturnsTotalMilliseconds_InsteadOfFraction() string route = "/tenant/{tenantid}/users/{userId}"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List()); var context = new DefaultHttpContext(); @@ -309,13 +306,13 @@ public void PerfStopwatch_ReturnsTotalMilliseconds_InsteadOfFraction() middleware.InvokeAsync(context, next).Wait(); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(hostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET " + route, latest.GetDimension(Metric.ReqName)); - Assert.Equal("200", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal(hostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("GET " + route, latest.Tags[Metric.ReqName]); + Assert.Equal("200", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); } [Fact] @@ -325,7 +322,7 @@ public void InvokeAsync_HttpMeteringMiddleware_NullRoute() string hostString = "teams.microsoft.com"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List()); var context = new DefaultHttpContext(); @@ -335,13 +332,13 @@ public void InvokeAsync_HttpMeteringMiddleware_NullRoute() middleware.InvokeAsync(context, _advanceTimeRequestDelegate).Wait(); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(hostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("POST unsupported_route", latest.GetDimension(Metric.ReqName)); - Assert.Equal("409", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal(hostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("POST unsupported_route", latest.Tags[Metric.ReqName]); + Assert.Equal("409", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); } [Fact] @@ -351,7 +348,7 @@ public void InvokeAsync_HttpMeteringMiddleware_NullHostString() string expectedHostString = "unknown_host_name"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List()); var context = new DefaultHttpContext(); @@ -360,13 +357,13 @@ public void InvokeAsync_HttpMeteringMiddleware_NullHostString() middleware.InvokeAsync(context, _advanceTimeRequestDelegate).Wait(); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(expectedHostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("POST unsupported_route", latest.GetDimension(Metric.ReqName)); - Assert.Equal("409", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal(expectedHostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("POST unsupported_route", latest.Tags[Metric.ReqName]); + Assert.Equal("409", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); } [Fact] @@ -376,7 +373,7 @@ public void InvokeAsync_HttpMeteringMiddleware_InternalServerError() string hostString = "teams.microsoft.com"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List()); var context = new DefaultHttpContext(); @@ -387,13 +384,13 @@ public void InvokeAsync_HttpMeteringMiddleware_InternalServerError() Assert.Throws(() => middleware.InvokeAsync(context, next).RunSynchronously()); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(hostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("POST unsupported_route", latest.GetDimension(Metric.ReqName)); - Assert.Equal("500", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(typeof(InvalidOperationException).FullName!, latest.GetDimension(Metric.ExceptionType)); + Assert.Equal(hostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("POST unsupported_route", latest.Tags[Metric.ReqName]); + Assert.Equal("500", latest.Tags[Metric.RspResultCode]); + Assert.Equal(typeof(InvalidOperationException).FullName!, latest.Tags[Metric.ExceptionType]); } [Fact] @@ -404,7 +401,7 @@ public void SendAsync_MultiEnrich() for (int i = 1; i <= 15; i++) { using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List { new TestEnricher(i) @@ -417,17 +414,17 @@ public void SendAsync_MultiEnrich() middleware.InvokeAsync(context, Mock.Of()).Wait(); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(hostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET unsupported_route", latest.GetDimension(Metric.ReqName)); - Assert.Equal("409", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal(hostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("GET unsupported_route", latest.Tags[Metric.ReqName]); + Assert.Equal("409", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); for (int j = 0; j < i; j++) { - Assert.Equal($"test_value_{j + 1}", latest.GetDimension($"test_property_{j + 1}")); + Assert.Equal($"test_value_{j + 1}", latest.Tags.GetValueOrDefault($"test_property_{j + 1}")); } } } @@ -438,7 +435,7 @@ public void InvokeAsync_MultipleEnrichers() string hostString = "teams.microsoft.com"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List { new TestEnricher(2), @@ -452,18 +449,18 @@ public void InvokeAsync_MultipleEnrichers() middleware.InvokeAsync(context, Mock.Of()).Wait(); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(hostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("GET unsupported_route", latest.GetDimension(Metric.ReqName)); - Assert.Equal("409", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); - - Assert.Equal("test_value_1", latest.GetDimension("test_property_1")); - Assert.Equal("test_value_2", latest.GetDimension("test_property_2")); - Assert.Equal("test_value_21", latest.GetDimension("test_property_21")); - Assert.Equal("test_value_22", latest.GetDimension("test_property_22")); + Assert.Equal(hostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("GET unsupported_route", latest.Tags[Metric.ReqName]); + Assert.Equal("409", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); + + Assert.Equal("test_value_1", latest.Tags["test_property_1"]); + Assert.Equal("test_value_2", latest.Tags["test_property_2"]); + Assert.Equal("test_value_21", latest.Tags["test_property_21"]); + Assert.Equal("test_value_22", latest.Tags["test_property_22"]); } [Fact] @@ -473,7 +470,7 @@ public async Task InvokeAsync_HttpMeteringMiddleware_PropertyBagEdgeCase() string hostString = "teams.microsoft.com"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); var middleware = SetupMockMiddleware(meter: meter, new List { new PropertyBagEdgeCaseEnricher() @@ -486,27 +483,27 @@ public async Task InvokeAsync_HttpMeteringMiddleware_PropertyBagEdgeCase() await middleware.InvokeAsync(context, Mock.Of()); - var latest = metricCollector.GetHistogramValues(Metric.IncomingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal(hostString, latest.GetDimension(Metric.ReqHost)); - Assert.Equal("POST unsupported_route", latest.GetDimension(Metric.ReqName)); - Assert.Equal("409", latest.GetDimension(Metric.RspResultCode)); - Assert.Equal("no_exception", latest.GetDimension(Metric.ExceptionType)); + Assert.Equal(hostString, latest.Tags[Metric.ReqHost]); + Assert.Equal("POST unsupported_route", latest.Tags[Metric.ReqName]); + Assert.Equal("409", latest.Tags[Metric.RspResultCode]); + Assert.Equal("no_exception", latest.Tags[Metric.ExceptionType]); - Assert.Equal("test_val", latest.GetDimension("non_null_object_property")); + Assert.Equal("test_val", latest.Tags["non_null_object_property"]); } [Fact] public void HttpMeteringMiddleware_Fail_16DEnrich() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); Assert.Throws(() => SetupMockMiddleware( meter: meter, new List { - new TestEnricher(16) + new TestEnricher(16) })); } @@ -514,7 +511,7 @@ public void HttpMeteringMiddleware_Fail_16DEnrich() public void HttpMeteringMiddleware_Fail_RepeatCustomDimensions() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); Assert.Throws(() => SetupMockMiddleware( meter: meter, new List @@ -528,7 +525,7 @@ public void HttpMeteringMiddleware_Fail_RepeatCustomDimensions() public void HttpMeteringMiddleware_Fail_RepeatDefaultDimensions() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(new List { typeof(HttpMeteringMiddleware).FullName! }); + using var metricCollector = new MetricCollector(null, typeof(HttpMeteringMiddleware).FullName!, Metric.IncomingRequestMetricName); Assert.Throws(() => SetupMockMiddleware( meter: meter, new List diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common.Tests/TelemetryHealthChecksPublisherTest.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common.Tests/TelemetryHealthChecksPublisherTest.cs index 0edab8d2179..6003202bb4b 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common.Tests/TelemetryHealthChecksPublisherTest.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.HealthChecks.Common.Tests/TelemetryHealthChecksPublisherTest.cs @@ -90,7 +90,8 @@ public async Task PublishAsync( string expectedMetricStatus) { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var healthyMetricCollector = new MetricCollector(meter, HealthReportMetricName); + using var unhealthyMetricCollector = new MetricCollector(meter, UnhealthyHealthCheckMetricName); var logger = new FakeLogger(); var collector = logger.Collector; @@ -110,13 +111,13 @@ public async Task PublishAsync( Assert.Equal(expectedLogLevel, collector.LatestRecord.Level); } - var latest = metricCollector.GetCounterValues(HealthReportMetricName)!.LatestWritten!; + var latest = healthyMetricCollector.LastMeasurement!; latest.Value.Should().Be(1); - latest.GetDimension("healthy").Should().Be(expectedMetricHealthy); - latest.GetDimension("status").Should().Be(expectedMetricStatus); + latest.Tags["healthy"].Should().Be(expectedMetricHealthy); + latest.Tags["status"].Should().Be(expectedMetricStatus); - var unhealthyCounters = metricCollector.GetCounterValues(UnhealthyHealthCheckMetricName)!.AllValues; + var unhealthyCounters = unhealthyMetricCollector.GetMeasurementSnapshot(); for (int i = 0; i < healthStatuses.Count; i++) { @@ -136,12 +137,12 @@ public void Ctor_ThrowsWhenOptionsValueNull() Assert.Throws(() => new TelemetryHealthCheckPublisher(meter, logger, Microsoft.Extensions.Options.Options.Create(null!))); } - private static long GetValue(IReadOnlyCollection> counters, string healthy, string status) + private static long GetValue(IReadOnlyCollection> counters, string healthy, string status) { foreach (var counter in counters) { - if (counter!.GetDimension("name")!.ToString() == healthy && - counter!.GetDimension("status")!.ToString() == status) + if (counter!.Tags["name"]?.ToString() == healthy && + counter!.Tags["status"]?.ToString() == status) { return counter.Value; } diff --git a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/FaultInjection/Internal/FaultInjectionTelemetryHandlerTests.cs b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/FaultInjection/Internal/FaultInjectionTelemetryHandlerTests.cs index 55880f41daa..2c156015d16 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/FaultInjection/Internal/FaultInjectionTelemetryHandlerTests.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/FaultInjection/Internal/FaultInjectionTelemetryHandlerTests.cs @@ -20,7 +20,7 @@ public void LogAndMeter_WithHttpContentKey() var logger = Mock.Of>(); using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, MetricName); var counter = meter.CreateCounter(MetricName); var metricCounter = new HttpClientFaultInjectionMetricCounter(counter); @@ -31,14 +31,14 @@ public void LogAndMeter_WithHttpContentKey() FaultInjectionTelemetryHandler.LogAndMeter(logger, metricCounter, GroupName, FaultType, InjectedValue, HttpContentKey); - var latest = metricCollector.GetCounterValues(MetricName)!.LatestWritten; + var latest = metricCollector.LastMeasurement!; Assert.NotNull(latest); Assert.Equal(1, latest.Value); - Assert.Equal(GroupName, latest.GetDimension(FaultInjectionEventMeterDimensions.FaultInjectionGroupName)); - Assert.Equal(FaultType, latest.GetDimension(FaultInjectionEventMeterDimensions.FaultType)); - Assert.Equal(InjectedValue, latest.GetDimension(FaultInjectionEventMeterDimensions.InjectedValue)); - Assert.Equal(HttpContentKey, latest.GetDimension(FaultInjectionEventMeterDimensions.HttpContentKey)); + Assert.Equal(GroupName, latest.Tags[FaultInjectionEventMeterDimensions.FaultInjectionGroupName]); + Assert.Equal(FaultType, latest.Tags[FaultInjectionEventMeterDimensions.FaultType]); + Assert.Equal(InjectedValue, latest.Tags[FaultInjectionEventMeterDimensions.InjectedValue]); + Assert.Equal(HttpContentKey, latest.Tags[FaultInjectionEventMeterDimensions.HttpContentKey]); } [Fact] @@ -47,7 +47,7 @@ public void LogAndMeter_WithoutHttpContentKey() var logger = Mock.Of>(); using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, MetricName); var counter = meter.CreateCounter(MetricName); var metricCounter = new HttpClientFaultInjectionMetricCounter(counter); @@ -58,13 +58,13 @@ public void LogAndMeter_WithoutHttpContentKey() FaultInjectionTelemetryHandler.LogAndMeter(logger, metricCounter, GroupName, FaultType, InjectedValue, httpContentKey: null); - var latest = metricCollector.GetCounterValues(MetricName)!.LatestWritten; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); - Assert.Equal(GroupName, latest.GetDimension(FaultInjectionEventMeterDimensions.FaultInjectionGroupName)); - Assert.Equal(FaultType, latest.GetDimension(FaultInjectionEventMeterDimensions.FaultType)); - Assert.Equal(InjectedValue, latest.GetDimension(FaultInjectionEventMeterDimensions.InjectedValue)); - Assert.Equal(HttpContentKey, latest.GetDimension(FaultInjectionEventMeterDimensions.HttpContentKey)); + Assert.Equal(GroupName, latest.Tags[FaultInjectionEventMeterDimensions.FaultInjectionGroupName]); + Assert.Equal(FaultType, latest.Tags[FaultInjectionEventMeterDimensions.FaultType]); + Assert.Equal(InjectedValue, latest.Tags[FaultInjectionEventMeterDimensions.InjectedValue]); + Assert.Equal(HttpContentKey, latest.Tags[FaultInjectionEventMeterDimensions.HttpContentKey]); } } diff --git a/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.Ext.cs b/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.Ext.cs index bbd3d4d2b61..fc908d01bcc 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.Ext.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.Ext.cs @@ -15,74 +15,73 @@ namespace Microsoft.Extensions.Http.Telemetry.Metering.Test; -#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits public sealed partial class HttpMeteringHandlerTests : IDisposable { [Fact] - public void SendAsync_expectedFailure_EmptyOutgoingRequestMetricEnricher() + public async Task SendAsync_expectedFailure_EmptyOutgoingRequestMetricEnricher() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, _expectedFailureUri); - using var _ = client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token).Result; + using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-expectedfailure.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(Metric.DependencyName)); - Assert.Equal($"GET {TelemetryConstants.Unknown}", latest.GetDimension(Metric.ReqName)); - Assert.Equal(400, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.ExpectedFailure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-expectedfailure.com", latest.Tags[Metric.ReqHost]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[Metric.DependencyName]); + Assert.Equal($"GET {TelemetryConstants.Unknown}", latest.Tags[Metric.ReqName]); + Assert.Equal(400, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.ExpectedFailure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } [Fact] public async Task SendAsync_TaskCanceledException() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, _failure1Uri); await Assert.ThrowsAsync(async () => await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token)); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure1.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(Metric.DependencyName)); - Assert.Equal($"POST {TelemetryConstants.Unknown}", latest.GetDimension(Metric.ReqName)); - Assert.Equal((int)HttpStatusCode.GatewayTimeout, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-failure1.com", latest.Tags[Metric.ReqHost]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[Metric.DependencyName]); + Assert.Equal($"POST {TelemetryConstants.Unknown}", latest.Tags[Metric.ReqName]); + Assert.Equal((int)HttpStatusCode.GatewayTimeout, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } [Fact] public async Task SendAsync_InvalidOperationException() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, _failure2Uri); await Assert.ThrowsAsync(async () => await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token)); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure2.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(Metric.DependencyName)); - Assert.Equal($"POST {TelemetryConstants.Unknown}", latest.GetDimension(Metric.ReqName)); - Assert.Equal((int)HttpStatusCode.InternalServerError, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-failure2.com", latest.Tags[Metric.ReqHost]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[Metric.DependencyName]); + Assert.Equal($"POST {TelemetryConstants.Unknown}", latest.Tags[Metric.ReqName]); + Assert.Equal((int)HttpStatusCode.InternalServerError, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } [Fact] public async Task SendAsync_Exception_OutgoingRequestMetricEnricherOnly() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter, new List { @@ -99,40 +98,39 @@ public async Task SendAsync_Exception_OutgoingRequestMetricEnricherOnly() await Assert.ThrowsAsync(async () => await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token)); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("failure_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("POST TestRequestName", latest.GetDimension(Metric.ReqName)); - Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); - Assert.Equal("test_value_1", latest.GetDimension("test_property_1")); + Assert.Equal("www.example-failure.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("failure_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("POST TestRequestName", latest.Tags[Metric.ReqName]); + Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); + Assert.Equal("test_value_1", latest.Tags["test_property_1"]); } [Fact] public async Task SendAsync_HttpRequestException() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, _failure3Uri); await Assert.ThrowsAsync(async () => await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token)); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure3.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(Metric.DependencyName)); - Assert.Equal($"POST {TelemetryConstants.Unknown}", latest.GetDimension(Metric.ReqName)); + Assert.Equal("www.example-failure3.com", latest.Tags[Metric.ReqHost]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[Metric.DependencyName]); + Assert.Equal($"POST {TelemetryConstants.Unknown}", latest.Tags[Metric.ReqName]); #if NET8_0_OR_GREATER // Whilst these API are marked as NET6_0_OR_GREATER we don't build .NET 6.0, // and as such the API is available in .NET 8 onwards. - Assert.Equal((int)HttpStatusCode.BadGateway, latest.GetDimension(Metric.RspResultCode)); + Assert.Equal((int)HttpStatusCode.BadGateway, latest.Tags[Metric.RspResultCode]); #else - Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.GetDimension(Metric.RspResultCode)); + Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.Tags[Metric.RspResultCode]); #endif - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } -#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits } diff --git a/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.cs b/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.cs index f0fc891115f..857e9dd949a 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Telemetry.Tests/Metering/HttpMeteringHandlerTests.cs @@ -30,9 +30,6 @@ namespace Microsoft.Extensions.Http.Telemetry.Metering.Test; -#pragma warning disable CA2000 // Not necessary to dispose all resources in test class. -#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits - public sealed partial class HttpMeteringHandlerTests : IDisposable { private const long DefaultClockAdvanceMs = 200; @@ -119,31 +116,31 @@ private static bool CheckHandlerUnderlyingMeterDisposed(Action testActio } [Fact] - public void SendAsync_Success_NoNamesSet() + public async Task SendAsync_Success_NoNamesSet() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, _successfulUri); - using var _ = client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token).Result; + using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(Metric.DependencyName)); - Assert.Equal($"PUT {TelemetryConstants.Unknown}", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[Metric.DependencyName]); + Assert.Equal($"PUT {TelemetryConstants.Unknown}", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); } [Fact] - public void SendAsync_Success() + public async Task SendAsync_Success() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, _successfulUri); @@ -153,25 +150,25 @@ public void SendAsync_Success() RequestRoute = "/foo" }); - using var _ = client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token).Result; + using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal($"GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal($"GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); } [Fact] - public void PerfStopwatch_ReturnsTotalMilliseconds_InsteadOfFraction() + public async Task PerfStopwatch_ReturnsTotalMilliseconds_InsteadOfFraction() { const long TimeAdvanceMs = 1500L; // We need to use any value greater than 1000 (1 second) const string ServiceName = "success_service"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, _successfulUri); @@ -183,23 +180,22 @@ public void PerfStopwatch_ReturnsTotalMilliseconds_InsteadOfFraction() _clockAdvanceMs = TimeAdvanceMs; - using var _ = client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token).Result; - - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); } [Fact] public async Task SendAsync_Exception() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, _failureUri); @@ -215,21 +211,20 @@ await Assert.ThrowsAsync(async () => using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); }); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; - + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("failure_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("POST TestRequestName", latest.GetDimension(Metric.ReqName)); - Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-failure.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("failure_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("POST TestRequestName", latest.Tags[Metric.ReqName]); + Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } [Fact] - public void SendAsync_SetReqMetadata_OnAsyncContext_Success() + public async Task SendAsync_SetReqMetadata_OnAsyncContext_Success() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); var requestMetadataContextMock = new Mock(); using var client = CreateClientWithHandler(meter, requestMetadataContextMock.Object); @@ -240,26 +235,25 @@ public void SendAsync_SetReqMetadata_OnAsyncContext_Success() }; requestMetadataContextMock.Setup(m => m.RequestMetadata).Returns(requestMetadata); - using var _ = client.GetAsync(_successfulUri, _cancellationTokenSource.Token).Result; - - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + using var _ = await client.GetAsync(_successfulUri, _cancellationTokenSource.Token); + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); } [Fact] - public void PerfStopwatch_SetReqMetadata_OnAsyncContext_ReturnsTotalMilliseconds_InsteadOfFraction() + public async Task PerfStopwatch_SetReqMetadata_OnAsyncContext_ReturnsTotalMilliseconds_InsteadOfFraction() { const long TimeAdvanceMs = 1500L; // We need to use any value greater than 1000 (1 second) const string ServiceName = "success_service"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); var requestMetadataContextMock = new Mock(); using var client = CreateClientWithHandler(meter, requestMetadataContextMock.Object); @@ -273,23 +267,22 @@ public void PerfStopwatch_SetReqMetadata_OnAsyncContext_ReturnsTotalMilliseconds _clockAdvanceMs = TimeAdvanceMs; - using var _ = client.GetAsync(_successfulUri, _cancellationTokenSource.Token).Result; - - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + using var _ = await client.GetAsync(_successfulUri, _cancellationTokenSource.Token); + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); } [Fact] public async Task SendAsync_SetReqMetadata_OnAsyncContext_Exception() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); var requestMetadataContextMock = new Mock(); using var client = CreateClientWithHandler(meter, requestMetadataContextMock.Object); @@ -308,21 +301,21 @@ await Assert.ThrowsAsync(async () => using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); }); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("failure_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("POST TestRequestName", latest.GetDimension(Metric.ReqName)); - Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-failure.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("failure_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("POST TestRequestName", latest.Tags[Metric.ReqName]); + Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } [Fact] - public void SendAsync_WithDownstreamDependencyMetadata_OnAsyncContext_Success() + public async Task SendAsync_WithDownstreamDependencyMetadata_OnAsyncContext_Success() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); var downstreamDependencyMetadataManagerMock = new Mock(); using var client = CreateClientWithHandler(meter, downstreamDependencyMetadataManager: downstreamDependencyMetadataManagerMock.Object); @@ -336,26 +329,26 @@ public void SendAsync_WithDownstreamDependencyMetadata_OnAsyncContext_Success() .Setup(m => m.GetRequestMetadata(It.IsAny())) .Returns(requestMetadata); - using var _ = client.GetAsync(_successfulUri, _cancellationTokenSource.Token).Result; + using var _ = await client.GetAsync(_successfulUri, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); } [Fact] - public void PerfStopwatch_WithDownstreamDependencyMetadata_OnAsyncContext_ReturnsTotalMilliseconds_InsteadOfFraction() + public async Task PerfStopwatch_WithDownstreamDependencyMetadata_OnAsyncContext_ReturnsTotalMilliseconds_InsteadOfFraction() { const long TimeAdvanceMs = 1500L; // We need to use any value greater than 1000 (1 second) const string ServiceName = "success_service"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); var downstreamDependencyMetadataManagerMock = new Mock(); using var client = CreateClientWithHandler(meter, downstreamDependencyMetadataManager: downstreamDependencyMetadataManagerMock.Object); @@ -369,23 +362,23 @@ public void PerfStopwatch_WithDownstreamDependencyMetadata_OnAsyncContext_Return _clockAdvanceMs = TimeAdvanceMs; - using var _ = client.GetAsync(_successfulUri, _cancellationTokenSource.Token).Result; + using var _ = await client.GetAsync(_successfulUri, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); } [Fact] public async Task SendAsync_WithDownstreamDependencyMetadata_OnAsyncContext_Exception() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); var dependencyDataManagerMock = new Mock(); using var client = CreateClientWithHandler(meter, downstreamDependencyMetadataManager: dependencyDataManagerMock.Object); @@ -403,14 +396,14 @@ await Assert.ThrowsAsync(async () => using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); }); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("failure_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("POST TestRequestName", latest.GetDimension(Metric.ReqName)); - Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-failure.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("failure_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("POST TestRequestName", latest.Tags[Metric.ReqName]); + Assert.Equal((int)HttpStatusCode.ServiceUnavailable, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } [Fact] @@ -428,7 +421,7 @@ public async Task SendAsync_MultiEnrich() for (int i = 1; i <= 14; i++) { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter, new List { new TestEnricher(i), @@ -443,18 +436,18 @@ public async Task SendAsync_MultiEnrich() using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); for (int j = 0; j < i; j++) { - Assert.Equal($"test_value_{j + 1}", latest.GetDimension($"test_property_{j + 1}")); + Assert.Equal($"test_value_{j + 1}", latest.Tags[$"test_property_{j + 1}"]); } } } @@ -465,7 +458,7 @@ public async Task SendAsync_MultiEnrich_UsingMeter() for (int i = 1; i <= 14; i++) { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var handler = new HttpMeteringHandler(meter, new[] { new TestEnricher(i) }) { InnerHandler = new TestHandlerStub(InnerHandlerFunction) @@ -482,17 +475,17 @@ public async Task SendAsync_MultiEnrich_UsingMeter() using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); for (int j = 0; j < i; j++) { - Assert.Equal($"test_value_{j + 1}", latest.GetDimension($"test_property_{j + 1}")); + Assert.Equal($"test_value_{j + 1}", latest.Tags[$"test_property_{j + 1}"]); } } } @@ -501,12 +494,12 @@ public async Task SendAsync_MultiEnrich_UsingMeter() public async Task InvokeAsync_HttpMeteringHandler_MultipleEnrichers() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter, new List - { - new TestEnricher(2), - new TestEnricher(2, "2"), - }); + { + new TestEnricher(2), + new TestEnricher(2, "2"), + }); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, _successfulUri); httpRequestMessage.SetRequestMetadata(new RequestMetadata @@ -516,25 +509,25 @@ public async Task InvokeAsync_HttpMeteringHandler_MultipleEnrichers() }); using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); - Assert.Equal("test_value_1", latest.GetDimension("test_property_1")); - Assert.Equal("test_value_2", latest.GetDimension("test_property_2")); - Assert.Equal("test_value_21", latest.GetDimension("test_property_21")); - Assert.Equal("test_value_22", latest.GetDimension("test_property_22")); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); + Assert.Equal("test_value_1", latest.Tags["test_property_1"]); + Assert.Equal("test_value_2", latest.Tags["test_property_2"]); + Assert.Equal("test_value_21", latest.Tags["test_property_21"]); + Assert.Equal("test_value_22", latest.Tags["test_property_22"]); } [Fact] public async Task InvokeAsync_HttpMeteringHandler_PropertyBagEdgeCase() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter, new List { new PropertyBagEdgeCaseEnricher(), @@ -548,15 +541,15 @@ public async Task InvokeAsync_HttpMeteringHandler_PropertyBagEdgeCase() }); using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-success.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal("success_service", latest.GetDimension(Metric.DependencyName)); - Assert.Equal("GET /foo", latest.GetDimension(Metric.ReqName)); - Assert.Equal(201, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Success.ToString(), latest.GetDimension(Metric.RspResultCategory)); - Assert.Equal("test_val", latest.GetDimension("non_null_object_property")); + Assert.Equal("www.example-success.com", latest.Tags[Metric.ReqHost]); + Assert.Equal("success_service", latest.Tags[Metric.DependencyName]); + Assert.Equal("GET /foo", latest.Tags[Metric.ReqName]); + Assert.Equal(201, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Success.ToString(), latest.Tags[Metric.RspResultCategory]); + Assert.Equal("test_val", latest.Tags["non_null_object_property"]); } [Fact] @@ -860,7 +853,7 @@ public static void AddHttpClientMeteringForAllHttpClients_NullServiceCollection_ } [Fact] - public static void AddHttpClientMeteringForAllHttpClients_WithDownstreamDependencyMetadata_UsesIt() + public static async Task AddHttpClientMeteringForAllHttpClients_WithDownstreamDependencyMetadata_UsesIt() { var downstreamDependencyMetadataManagerMock = new Mock(); downstreamDependencyMetadataManagerMock @@ -882,7 +875,7 @@ public static void AddHttpClientMeteringForAllHttpClients_WithDownstreamDependen .GetRequiredService() .CreateClient(nameof(AddDefaultHttpClientMetering_WithDownstreamDependencyMetadata_UsesIt)); - using var _ = client.GetAsync("https://www.bing.com").Result; + using var _ = await client.GetAsync("https://www.bing.com"); HttpClientMeteringListener.UsingDiagnosticsSource = false; downstreamDependencyMetadataManagerMock.Verify(m => m.GetRequestMetadata(It.IsAny()), Times.AtLeastOnce); @@ -890,7 +883,7 @@ public static void AddHttpClientMeteringForAllHttpClients_WithDownstreamDependen } [Fact] - public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnException() + public static async Task AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnException() { var downstreamDependencyMetadataManagerMock = new Mock(); downstreamDependencyMetadataManagerMock @@ -911,22 +904,22 @@ public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnExceptio .CreateClient(nameof(AddDefaultHttpClientMetering_WithDownstreamDependencyMetadata_UsesIt)); var meter = host.Services.GetRequiredService>(); - using var meterCollector = new MetricCollector(meter); + using var meterCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); DateTimeOffset startTime = DateTimeOffset.UtcNow; - Assert.Throws(() => client.GetAsync("https://localhost:12345").Result); + await Assert.ThrowsAsync(async () => await client.GetAsync("https://localhost:12345")); HttpClientMeteringListener.UsingDiagnosticsSource = false; - var record = meterCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var record = meterCollector.LastMeasurement; Assert.NotNull(record); Assert.True(record.Value > 0); Assert.True(record.Value <= (DateTimeOffset.UtcNow - startTime).TotalMilliseconds); - Assert.Equal((int)HttpStatusCode.ServiceUnavailable, record.GetDimension(Metric.RspResultCode)); + Assert.Equal((int)HttpStatusCode.ServiceUnavailable, record.Tags.GetValueOrDefault(Metric.RspResultCode)); HttpClientMeteringListener.UsingDiagnosticsSource = false; } [Fact] - public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnTaskCancelledException() + public static async Task AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnTaskCancelledException() { var downstreamDependencyMetadataManagerMock = new Mock(); downstreamDependencyMetadataManagerMock @@ -947,27 +940,27 @@ public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnTaskCanc .CreateClient(nameof(AddDefaultHttpClientMetering_WithDownstreamDependencyMetadata_UsesIt)); var meter = host.Services.GetRequiredService>(); - using var meterCollector = new MetricCollector(meter); + using var meterCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); DateTimeOffset startTime = DateTimeOffset.UtcNow; var cts = new CancellationTokenSource(); using var testObserver = new RequestCancellationTestObserver(cts); - Assert.Throws(() => client.GetAsync("https://www.bing.com", cts.Token).Result); + await Assert.ThrowsAsync(async () => await client.GetAsync("https://www.bing.com", cts.Token)); HttpClientMeteringListener.UsingDiagnosticsSource = false; - var record = meterCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var record = meterCollector.LastMeasurement; Assert.NotNull(record); Assert.True(record.Value >= 0); Assert.True(record.Value <= (DateTimeOffset.UtcNow - startTime).TotalMilliseconds); - Assert.Equal((int)HttpStatusCode.GatewayTimeout, record.GetDimension(Metric.RspResultCode)); + Assert.Equal((int)HttpStatusCode.GatewayTimeout, record.Tags.GetValueOrDefault(Metric.RspResultCode)); HttpClientMeteringListener.UsingDiagnosticsSource = false; } [Fact] - public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnError() + public static async Task AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnError() { var downstreamDependencyMetadataManagerMock = new Mock(); downstreamDependencyMetadataManagerMock @@ -988,22 +981,22 @@ public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnError() .CreateClient(nameof(AddDefaultHttpClientMetering_WithDownstreamDependencyMetadata_UsesIt)); var meter = host.Services.GetRequiredService>(); - using var meterCollector = new MetricCollector(meter); + using var meterCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); DateTimeOffset startTime = DateTimeOffset.UtcNow; - _ = client.GetAsync("https://www.bing.com/request").Result; + _ = await client.GetAsync("https://www.bing.com/request"); HttpClientMeteringListener.UsingDiagnosticsSource = false; - var record = meterCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var record = meterCollector.LastMeasurement; Assert.NotNull(record); Assert.True(record.Value > 0); Assert.True(record.Value <= (DateTimeOffset.UtcNow - startTime).TotalMilliseconds); - Assert.Equal((int)HttpStatusCode.NotFound, record.GetDimension(Metric.RspResultCode)); + Assert.Equal((int)HttpStatusCode.NotFound, record.Tags.GetValueOrDefault(Metric.RspResultCode)); HttpClientMeteringListener.UsingDiagnosticsSource = false; } [Fact] - public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnSuccessResponse() + public static async Task AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnSuccessResponse() { var downstreamDependencyMetadataManagerMock = new Mock(); downstreamDependencyMetadataManagerMock @@ -1024,22 +1017,22 @@ public static void AddHttpClientMeteringForAllHttpClients_EmitMetrics_OnSuccessR .CreateClient(nameof(AddDefaultHttpClientMetering_WithDownstreamDependencyMetadata_UsesIt)); var meter = host.Services.GetRequiredService>(); - using var meterCollector = new MetricCollector(meter); + using var meterCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); DateTimeOffset startTime = DateTimeOffset.UtcNow; - using var _ = client.GetAsync("https://www.bing.com").Result; + using var _ = await client.GetAsync("https://www.bing.com"); HttpClientMeteringListener.UsingDiagnosticsSource = false; - var record = meterCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var record = meterCollector.LastMeasurement; Assert.NotNull(record); Assert.True(record.Value > 0); Assert.True(record.Value <= (DateTimeOffset.UtcNow - startTime).TotalMilliseconds); - Assert.Equal(200, record.GetDimension(Metric.RspResultCode)); + Assert.Equal(200, record.Tags.GetValueOrDefault(Metric.RspResultCode)); HttpClientMeteringListener.UsingDiagnosticsSource = false; } [Fact] - public static void AddHttpClientMeteringForAllHttpClients_CaptureMetrics_ForNonHttpClientFactoryClients() + public static async Task AddHttpClientMeteringForAllHttpClients_CaptureMetrics_ForNonHttpClientFactoryClients() { var downstreamDependencyMetadataManagerMock = new Mock(); downstreamDependencyMetadataManagerMock @@ -1053,24 +1046,24 @@ public static void AddHttpClientMeteringForAllHttpClients_CaptureMetrics_ForNonH host.Start(); var meter = host.Services.GetRequiredService>(); - using var meterCollector = new MetricCollector(meter); + using var meterCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = new System.Net.Http.HttpClient(); DateTimeOffset startTime = DateTimeOffset.UtcNow; - using var _ = client.GetAsync("https://www.bing.com").Result; + using var _ = await client.GetAsync("https://www.bing.com"); HttpClientMeteringListener.UsingDiagnosticsSource = false; - var record = meterCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var record = meterCollector.LastMeasurement; Assert.NotNull(record); Assert.True(record.Value > 0); Assert.True(record.Value <= (DateTimeOffset.UtcNow - startTime).TotalMilliseconds); - Assert.Equal(200, record.GetDimension(Metric.RspResultCode)); + Assert.Equal(200, record.Tags.GetValueOrDefault(Metric.RspResultCode)); HttpClientMeteringListener.UsingDiagnosticsSource = false; } - [Fact(Skip = "Flaky")] - public static void When_DiagSourceAndDelegatingHandler_BothConfigured_MetricsOnlyEmittedOnce() + [Fact] + public static async Task When_DiagSourceAndDelegatingHandler_BothConfigured_MetricsOnlyEmittedOnce() { using var host = FakeHost.CreateBuilder() .ConfigureServices((_, services) => services @@ -1085,20 +1078,20 @@ public static void When_DiagSourceAndDelegatingHandler_BothConfigured_MetricsOnl .CreateClient(nameof(AddDefaultHttpClientMetering_WithDownstreamDependencyMetadata_UsesIt)); var meter = host.Services.GetRequiredService>(); - using var meterCollector = new MetricCollector(meter); + using var meterCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); DateTimeOffset startTime = DateTimeOffset.UtcNow; - using var _ = client.GetAsync("https://www.bing.com").Result; + using var _ = await client.GetAsync("https://www.bing.com"); HttpClientMeteringListener.UsingDiagnosticsSource = false; - var records = meterCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!; + var records = meterCollector.GetMeasurementSnapshot(); Assert.NotNull(records); - Assert.Equal(1, records.AllValues.Count); + Assert.Equal(1, records.Count); - var record = records.LatestWritten!; + var record = meterCollector.LastMeasurement!; Assert.True(record.Value > 0); Assert.True(record.Value <= (DateTimeOffset.UtcNow - startTime).TotalMilliseconds); - Assert.Equal(200, record.GetDimension(Metric.RspResultCode)); + Assert.Equal(200, record.Tags.GetValueOrDefault(Metric.RspResultCode)); HttpClientMeteringListener.UsingDiagnosticsSource = false; } @@ -1140,38 +1133,38 @@ public void HttpClientRequestAdapter_OnRequestStop_TaskStatusNotFaultedOrCancell httpClientRequestAdapter.HttpClientListenerSubscribed(httpRequestMessage); httpClientRequestAdapter.OnRequestStart(httpRequestMessage); - using var meterCollector = new MetricCollector(meter); + using var meterCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); httpClientRequestAdapter.OnRequestStop(null, httpRequestMessage, TaskStatus.RanToCompletion); - var records = meterCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!; + var records = meterCollector.GetMeasurementSnapshot(); Assert.NotNull(records); - Assert.Equal(1, records.AllValues.Count); + Assert.Equal(1, records.Count); - var record = records.LatestWritten!; - Assert.Equal((int)HttpStatusCode.InternalServerError, record.GetDimension(Metric.RspResultCode)); + var record = meterCollector.LastMeasurement!; + Assert.Equal((int)HttpStatusCode.InternalServerError, record.Tags.GetValueOrDefault(Metric.RspResultCode)); HttpClientMeteringListener.UsingDiagnosticsSource = false; } #endif [Fact] - public void SendAsync_Failure_NoExceptionThrown() + public async Task SendAsync_Failure_NoExceptionThrown() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, Metric.OutgoingRequestMetricName); using var client = CreateClientWithHandler(meter); using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, _internalServerErrorUri); - using var _ = client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token).Result; + using var _ = await client.SendAsync(httpRequestMessage, _cancellationTokenSource.Token); - var latest = metricCollector.GetHistogramValues(Metric.OutgoingRequestMetricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); - Assert.Equal("www.example-failure.com", latest.GetDimension(Metric.ReqHost)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(Metric.DependencyName)); - Assert.Equal($"GET {TelemetryConstants.Unknown}", latest.GetDimension(Metric.ReqName)); - Assert.Equal((int)HttpStatusCode.InternalServerError, latest.GetDimension(Metric.RspResultCode)); - Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.GetDimension(Metric.RspResultCategory)); + Assert.Equal("www.example-failure.com", latest.Tags[Metric.ReqHost]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[Metric.DependencyName]); + Assert.Equal($"GET {TelemetryConstants.Unknown}", latest.Tags[Metric.ReqName]); + Assert.Equal((int)HttpStatusCode.InternalServerError, latest.Tags[Metric.RspResultCode]); + Assert.Equal(HttpRequestResultType.Failure.ToInvariantString(), latest.Tags[Metric.RspResultCategory]); } #pragma warning restore VSTHRD002 // Avoid problematic synchronous waits } diff --git a/test/Libraries/Microsoft.Extensions.Resilience.Tests/FaultInjection/Internals/FaultInjectionTelemetryHandlerTests.cs b/test/Libraries/Microsoft.Extensions.Resilience.Tests/FaultInjection/Internals/FaultInjectionTelemetryHandlerTests.cs index 78cb7beee2f..2d54be31c85 100644 --- a/test/Libraries/Microsoft.Extensions.Resilience.Tests/FaultInjection/Internals/FaultInjectionTelemetryHandlerTests.cs +++ b/test/Libraries/Microsoft.Extensions.Resilience.Tests/FaultInjection/Internals/FaultInjectionTelemetryHandlerTests.cs @@ -19,7 +19,7 @@ public void LogAndMeter() var logger = Mock.Of>(); using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, MetricName); var counter = meter.CreateCounter(MetricName); var metricCounter = new FaultInjectionMetricCounter(counter); @@ -29,12 +29,12 @@ public void LogAndMeter() FaultInjectionTelemetryHandler.LogAndMeter(logger, metricCounter, GroupName, FaultType, InjectedValue); - var latest = metricCollector.GetCounterValues(MetricName)!.LatestWritten; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); - Assert.Equal(GroupName, latest.GetDimension(FaultInjectionEventMeterDimensions.FaultInjectionGroupName)); - Assert.Equal(FaultType, latest.GetDimension(FaultInjectionEventMeterDimensions.FaultType)); - Assert.Equal(InjectedValue, latest.GetDimension(FaultInjectionEventMeterDimensions.InjectedValue)); + Assert.Equal(GroupName, latest.Tags[FaultInjectionEventMeterDimensions.FaultInjectionGroupName]); + Assert.Equal(FaultType, latest.Tags[FaultInjectionEventMeterDimensions.FaultType]); + Assert.Equal(InjectedValue, latest.Tags[FaultInjectionEventMeterDimensions.InjectedValue]); } } diff --git a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Polly/Internals/PolicyMeteringTests.cs b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Polly/Internals/PolicyMeteringTests.cs index c21d2955e40..f0996d9702e 100644 --- a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Polly/Internals/PolicyMeteringTests.cs +++ b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Polly/Internals/PolicyMeteringTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Diagnostics.ExceptionSummarization; @@ -32,7 +31,6 @@ public class PolicyMeteringTests : IDisposable private readonly Mock _summarizer; private readonly Mock _outgoingContext; private readonly Meter _meter; - private readonly MetricCollector _metricCollector; private PolicyMetering _metering; private FailureResultContext _context; @@ -42,7 +40,7 @@ public PolicyMeteringTests() _outgoingContext = new Mock(MockBehavior.Strict); _meter = new(); - _metricCollector = new(_meter); + Counter = new(_meter, MetricName); var services = new ServiceCollection(); services.TryAddSingleton(_outgoingContext.Object); @@ -53,7 +51,7 @@ public PolicyMeteringTests() public void Dispose() { - _metricCollector.Dispose(); + Counter.Dispose(); _meter.Dispose(); } @@ -75,7 +73,7 @@ public void Initialize_EnsurePipelineKeyRespected(string pipelineKey, string exp RecordEvent(true, "policy", "ev", null); - Assert.Equal(expectedKey, Counter.LatestWritten!.GetDimension(ResilienceDimensions.PipelineKey)); + Assert.Equal(expectedKey, Counter.LastMeasurement?.Tags[ResilienceDimensions.PipelineKey]); } [InlineData(true)] @@ -85,7 +83,7 @@ public void NotInitialized_DoesNotMeter(bool typed) { RecordEvent(typed, "policy", "ev", null); - Counter.AllValues.Should().BeEmpty(); + Assert.Null(Counter.LastMeasurement); } [Fact] @@ -100,8 +98,8 @@ public void NoOutgoingContext_ShouldNotThrow() RecordEvent(true, "policy", "ev", null); - Assert.Equal(TelemetryConstants.Unknown, Counter.LatestWritten!.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal(TelemetryConstants.Unknown, Counter.LatestWritten!.GetDimension(ResilienceDimensions.RequestName)); + Assert.Equal(TelemetryConstants.Unknown, Counter.LastMeasurement?.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal(TelemetryConstants.Unknown, Counter.LastMeasurement?.Tags[ResilienceDimensions.RequestName]); } [InlineData(true)] @@ -113,18 +111,18 @@ public void RecordEvent_NullException_Ok(bool typed) RecordEvent(typed, "policy", "ev", null); - var latest = Counter.LatestWritten; - - Assert.Equal(PipelineName, latest!.GetDimension(ResilienceDimensions.PipelineName)); - Assert.Equal(PipelineKey, latest.GetDimension(ResilienceDimensions.PipelineKey)); - Assert.Equal(ResultType, latest.GetDimension(ResilienceDimensions.ResultType)); - Assert.Equal("policy", latest.GetDimension(ResilienceDimensions.PolicyName)); - Assert.Equal("ev", latest.GetDimension(ResilienceDimensions.EventName)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.FailureSource)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.FailureReason)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.FailureSummary)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.RequestName)); + var latest = Counter.LastMeasurement!; + + Assert.Equal(PipelineName, latest.Tags[ResilienceDimensions.PipelineName]); + Assert.Equal(PipelineKey, latest.Tags[ResilienceDimensions.PipelineKey]); + Assert.Equal(ResultType, latest.Tags[ResilienceDimensions.ResultType]); + Assert.Equal("policy", latest.Tags[ResilienceDimensions.PolicyName]); + Assert.Equal("ev", latest.Tags[ResilienceDimensions.EventName]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureSource]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureReason]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureSummary]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.RequestName]); } [InlineData(true)] @@ -139,11 +137,11 @@ public void RecordEvent_Exception_Ok(bool typed) RecordEvent(typed, "policy", "ev", er); - var latest = Counter.LatestWritten; + var latest = Counter.LastMeasurement!; - Assert.Equal(TelemetryConstants.Unknown, latest!.GetDimension(ResilienceDimensions.FailureSource)); - Assert.Equal("InvalidOperationException", latest.GetDimension(ResilienceDimensions.FailureReason)); - Assert.Equal("type:desc:details", latest.GetDimension(ResilienceDimensions.FailureSummary)); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureSource]); + Assert.Equal("InvalidOperationException", latest.Tags[ResilienceDimensions.FailureReason]); + Assert.Equal("type:desc:details", latest.Tags[ResilienceDimensions.FailureSummary]); } [Fact] @@ -153,11 +151,11 @@ public void RecordEvent_FailureResult_Ok() _context = FailureResultContext.Create("src", "reason", "summary"); _metering.RecordEvent("policy", "ev", new DelegateResult("test"), new Context()); - var latest = Counter.LatestWritten; + var latest = Counter.LastMeasurement!; - Assert.Equal("src", latest!.GetDimension(ResilienceDimensions.FailureSource)); - Assert.Equal("reason", latest.GetDimension(ResilienceDimensions.FailureReason)); - Assert.Equal("summary", latest.GetDimension(ResilienceDimensions.FailureSummary)); + Assert.Equal("src", latest.Tags[ResilienceDimensions.FailureSource]); + Assert.Equal("reason", latest.Tags[ResilienceDimensions.FailureReason]); + Assert.Equal("summary", latest.Tags[ResilienceDimensions.FailureSummary]); } [InlineData(true)] @@ -173,10 +171,10 @@ public void RecordEvent_RequestMetadata(bool typed) _outgoingContext.Setup(o => o.RequestMetadata).Returns(metadata1); RecordEvent(typed, "policy", "ev", null, new Context()); - var latest = Counter.LatestWritten; + var latest = Counter.LastMeasurement!; - Assert.Equal("dep", latest!.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal("req", latest.GetDimension(ResilienceDimensions.RequestName)); + Assert.Equal("dep", latest.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal("req", latest.Tags[ResilienceDimensions.RequestName]); var ctx = new Context { @@ -184,9 +182,9 @@ public void RecordEvent_RequestMetadata(bool typed) }; RecordEvent(typed, "policy", "ev", null, ctx); - latest = Counter.LatestWritten; - Assert.Equal("dep2", latest!.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal("req2", latest.GetDimension(ResilienceDimensions.RequestName)); + latest = Counter.LastMeasurement!; + Assert.Equal("dep2", latest.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal("req2", latest.Tags[ResilienceDimensions.RequestName]); } private void RecordEvent(bool typed, string policyName, string eventName, Exception? exception, Context? context = null) @@ -201,7 +199,7 @@ private void RecordEvent(bool typed, string policyName, string eventName, Except } } - private MetricValuesHolder Counter => _metricCollector.GetCounterValues(MetricName)!; + private MetricCollector Counter { get; } private void Initialize(string pipelineKey = PipelineKey) { diff --git a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/Internal/PipelineMeteringTests.cs b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/Internal/PipelineMeteringTests.cs index d07b4bd2fa9..2d16bf3503a 100644 --- a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/Internal/PipelineMeteringTests.cs +++ b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/Internal/PipelineMeteringTests.cs @@ -30,7 +30,6 @@ public class PipelineMeteringTests : IDisposable private readonly Mock _summarizer; private readonly Mock _outgoingContext; private readonly Meter _meter; - private readonly MetricCollector _metricCollector; private PipelineMetering _metering; public PipelineMeteringTests() @@ -38,13 +37,13 @@ public PipelineMeteringTests() _summarizer = new Mock(MockBehavior.Strict); _outgoingContext = new Mock(MockBehavior.Strict); _meter = new(); - _metricCollector = new(_meter); + Counter = new(_meter, MetricName); _metering = new PipelineMetering(_meter, _summarizer.Object, new[] { _outgoingContext.Object }); } public void Dispose() { - _metricCollector.Dispose(); + Counter.Dispose(); _meter.Dispose(); } @@ -66,7 +65,7 @@ public void Initialize_EnsurePipelineKeyRespected(string pipelineKey, string exp RecordPipelineExecution(null); - Assert.Equal(expectedKey, Counter.LatestWritten!.GetDimension(ResilienceDimensions.PipelineKey)); + Assert.Equal(expectedKey, Counter.LastMeasurement?.Tags[ResilienceDimensions.PipelineKey]); } [Fact] @@ -87,8 +86,8 @@ public void NoOutgoingContext_ShouldNotThrow() RecordPipelineExecution(null); - Assert.Equal(TelemetryConstants.Unknown, Counter.LatestWritten!.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal(TelemetryConstants.Unknown, Counter.LatestWritten!.GetDimension(ResilienceDimensions.RequestName)); + Assert.Equal(TelemetryConstants.Unknown, Counter.LastMeasurement?.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal(TelemetryConstants.Unknown, Counter.LastMeasurement?.Tags[ResilienceDimensions.RequestName]); } [Fact] @@ -98,16 +97,16 @@ public void RecordEvent_NullException_Ok() RecordPipelineExecution(null); - var latest = Counter.LatestWritten!; + var latest = Counter.LastMeasurement!; - Assert.Equal(PipelineName, latest.GetDimension(ResilienceDimensions.PipelineName)); - Assert.Equal(PipelineKey, latest.GetDimension(ResilienceDimensions.PipelineKey)); - Assert.Equal(ResultType, latest.GetDimension(ResilienceDimensions.ResultType)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.FailureSource)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.FailureReason)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.FailureSummary)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.RequestName)); + Assert.Equal(PipelineName, latest.Tags[ResilienceDimensions.PipelineName]); + Assert.Equal(PipelineKey, latest.Tags[ResilienceDimensions.PipelineKey]); + Assert.Equal(ResultType, latest.Tags[ResilienceDimensions.ResultType]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureSource]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureReason]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureSummary]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.RequestName]); } [Fact] @@ -120,11 +119,11 @@ public void RecordEvent_Exception_Ok() RecordPipelineExecution(er); - var latest = Counter.LatestWritten!; + var latest = Counter.LastMeasurement!; - Assert.Equal(TelemetryConstants.Unknown, latest.GetDimension(ResilienceDimensions.FailureSource)); - Assert.Equal("InvalidOperationException", latest.GetDimension(ResilienceDimensions.FailureReason)); - Assert.Equal("type:desc:details", latest.GetDimension(ResilienceDimensions.FailureSummary)); + Assert.Equal(TelemetryConstants.Unknown, latest.Tags[ResilienceDimensions.FailureSource]); + Assert.Equal("InvalidOperationException", latest.Tags[ResilienceDimensions.FailureReason]); + Assert.Equal("type:desc:details", latest.Tags[ResilienceDimensions.FailureSummary]); } [Fact] @@ -138,10 +137,10 @@ public void RecordEvent_RequestMetadata() _outgoingContext.Setup(o => o.RequestMetadata).Returns(metadata1); RecordPipelineExecution(null, new Context()); - var latest = Counter.LatestWritten!; + var latest = Counter.LastMeasurement!; - Assert.Equal("dep", latest.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal("req", latest.GetDimension(ResilienceDimensions.RequestName)); + Assert.Equal("dep", latest.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal("req", latest.Tags[ResilienceDimensions.RequestName]); var ctx = new Context { @@ -149,10 +148,10 @@ public void RecordEvent_RequestMetadata() }; RecordPipelineExecution(null, ctx); - latest = Counter.LatestWritten!; + latest = Counter.LastMeasurement!; - Assert.Equal("dep2", latest.GetDimension(ResilienceDimensions.DependencyName)); - Assert.Equal("req2", latest.GetDimension(ResilienceDimensions.RequestName)); + Assert.Equal("dep2", latest.Tags[ResilienceDimensions.DependencyName]); + Assert.Equal("req2", latest.Tags[ResilienceDimensions.RequestName]); } private void RecordPipelineExecution(Exception? exception, Context? context = null) @@ -161,7 +160,7 @@ private void RecordPipelineExecution(Exception? exception, Context? context = nu } - private MetricValuesHolder Counter => _metricCollector.GetHistogramValues(MetricName)!; + private MetricCollector Counter { get; } private void Initialize(string pipelineKey = PipelineKey) { diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MeasurementExtensionsTests.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MeasurementExtensionsTests.cs new file mode 100644 index 00000000000..89539e6ea1b --- /dev/null +++ b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MeasurementExtensionsTests.cs @@ -0,0 +1,241 @@ +// 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.Linq; +using Xunit; + +namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; + +public static class MeasurementExtensionsTests +{ + [Fact] + public static void ContainsTagKeys() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1, new("A", "a"), new("B", "b")); + counter.Add(2, new("A", "a"), new("B", "b")); + counter.Add(3, new("X", "x"), new("Y", "y")); + + var fullSnap = collector.GetMeasurementSnapshot(); + + var filtered = fullSnap.ContainsTags("A").ToList(); + Assert.Equal(2, filtered.Count); + Assert.Equal(1, filtered[0].Value); + Assert.Equal(2, filtered[1].Value); + Assert.Equal(2, filtered[1].Tags.Count); + Assert.True(filtered[1].Tags.ContainsKey("A")); + Assert.True(filtered[1].Tags.ContainsKey("B")); + + filtered = fullSnap.ContainsTags("M").ToList(); + Assert.Empty(filtered); + } + + [Fact] + public static void ContainsTags() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1, new("A", "a"), new("B", "b")); + counter.Add(2, new("A", "a"), new("B", "b")); + counter.Add(3, new("X", "x"), new("Y", "y")); + + var fullSnap = collector.GetMeasurementSnapshot(); + + var filtered = fullSnap.ContainsTags(new KeyValuePair("A", "a")).ToList(); + Assert.Equal(2, filtered.Count); + Assert.Equal(1, filtered[0].Value); + Assert.Equal(2, filtered[1].Value); + Assert.Equal(2, filtered[1].Tags.Count); + Assert.True(filtered[1].Tags.ContainsKey("A")); + Assert.True(filtered[1].Tags.ContainsKey("B")); + + filtered = fullSnap.ContainsTags(new KeyValuePair("A", "X")).ToList(); + Assert.Empty(filtered); + } + + [Fact] + public static void MatchesTagKeys() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1, new("A", "a"), new("B", "b")); + counter.Add(2, new("A", "a"), new("B", "b")); + counter.Add(3, new("X", "x"), new("Y", "y")); + + var fullSnap = collector.GetMeasurementSnapshot(); + + var filtered = fullSnap.MatchesTags("A", "B").ToList(); + Assert.Equal(2, filtered.Count); + Assert.Equal(1, filtered[0].Value); + Assert.Equal(2, filtered[1].Value); + Assert.Equal(2, filtered[1].Tags.Count); + Assert.True(filtered[1].Tags.ContainsKey("A")); + Assert.True(filtered[1].Tags.ContainsKey("B")); + + filtered = fullSnap.MatchesTags("A").ToList(); + Assert.Empty(filtered); + + filtered = fullSnap.MatchesTags("A", "B", "C").ToList(); + Assert.Empty(filtered); + } + + [Fact] + public static void MatchesTags() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1, new("A", "a"), new("B", "b")); + counter.Add(2, new("A", "a"), new("B", "b")); + counter.Add(3, new("X", "x"), new("Y", "y")); + + var fullSnap = collector.GetMeasurementSnapshot(); + + var filtered = fullSnap.MatchesTags(new KeyValuePair("A", "a"), new("B", "b")).ToList(); + Assert.Equal(2, filtered.Count); + Assert.Equal(1, filtered[0].Value); + Assert.Equal(2, filtered[1].Value); + Assert.Equal(2, filtered[1].Tags.Count); + Assert.True(filtered[1].Tags.ContainsKey("A")); + Assert.True(filtered[1].Tags.ContainsKey("B")); + + filtered = fullSnap.MatchesTags(new KeyValuePair("A", "a")).ToList(); + Assert.Empty(filtered); + + filtered = fullSnap.MatchesTags(new KeyValuePair("A", "a"), new("B", "x")).ToList(); + Assert.Empty(filtered); + + filtered = fullSnap.MatchesTags(new KeyValuePair("A", "a"), new("B", "b"), new("C", "c")).ToList(); + Assert.Empty(filtered); + } + + [Fact] + public static void SumBytes() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1); + counter.Add(2); + counter.Add(200); + counter.Add(200); + + var fullSnap = collector.GetMeasurementSnapshot(); + var total = fullSnap.EvaluateAsCounter(); + Assert.Equal(147, total); + } + + [Fact] + public static void SumShorts() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1); + counter.Add(2); + counter.Add(200); + counter.Add(32760); + + var fullSnap = collector.GetMeasurementSnapshot(); + var total = fullSnap.EvaluateAsCounter(); + Assert.Equal(-32573, total); + } + + [Fact] + public static void SumInts() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1); + counter.Add(2); + counter.Add(200); + counter.Add(32760); + + var fullSnap = collector.GetMeasurementSnapshot(); + var total = fullSnap.EvaluateAsCounter(); + Assert.Equal(32963, total); + } + + [Fact] + public static void SumLongs() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1); + counter.Add(2); + counter.Add(200); + counter.Add(32760); + + var fullSnap = collector.GetMeasurementSnapshot(); + var total = fullSnap.EvaluateAsCounter(); + Assert.Equal(32963, total); + } + + [Fact] + public static void SumFloats() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1); + counter.Add(2); + counter.Add(200); + counter.Add(32760); + + var fullSnap = collector.GetMeasurementSnapshot(); + var total = fullSnap.EvaluateAsCounter(); + Assert.Equal(32963, total); + } + + [Fact] + public static void SumDouble() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1); + counter.Add(2); + counter.Add(200); + counter.Add(32760); + + var fullSnap = collector.GetMeasurementSnapshot(); + var total = fullSnap.EvaluateAsCounter(); + Assert.Equal(32963, total); + } + + [Fact] + public static void SumDecimals() + { + using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("MyCounter"); + using var collector = new MetricCollector(counter); + + counter.Add(1); + counter.Add(2); + counter.Add(200); + counter.Add(32760); + + var fullSnap = collector.GetMeasurementSnapshot(); + var total = fullSnap.EvaluateAsCounter(); + Assert.Equal(32963, total); + } +} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Counter.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Counter.cs deleted file mode 100644 index 4a35f8782c3..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Counter.cs +++ /dev/null @@ -1,210 +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 Microsoft.Extensions.Telemetry.Testing.Metering; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public partial class MetricCollectorTests -{ - [Fact] - public void Counter_BasicTest() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - CounterBasicTest(meter, metricCollector, 2, 5, 10, 17); - CounterBasicTest(meter, metricCollector, 20, 50, 100, 170); - CounterBasicTest(meter, metricCollector, 200, 500, 1000, 1700); - CounterBasicTest(meter, metricCollector, 2000L, 5000L, 10000L, 17000L); - CounterBasicTest(meter, metricCollector, 1.22f, 3.44f, 0, 1.22f + 3.44f); - CounterBasicTest(meter, metricCollector, 5.22, 6.44, 10, 5.22 + 6.44 + 10); - CounterBasicTest(meter, metricCollector, 0.99m, 15, 25.99m, 41.98m); - } - - [Fact] - public void Counter_StringDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - CounterWithStringDimensionsTest(meter, metricCollector, byte.MinValue); - CounterWithStringDimensionsTest(meter, metricCollector, short.MaxValue); - CounterWithStringDimensionsTest(meter, metricCollector, int.MaxValue); - CounterWithStringDimensionsTest(meter, metricCollector, long.MaxValue); - CounterWithStringDimensionsTest(meter, metricCollector, float.MaxValue); - CounterWithStringDimensionsTest(meter, metricCollector, double.MaxValue); - CounterWithStringDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void Counter_NumericDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - CounterWithNumericDimensionsTest(meter, metricCollector, byte.MinValue); - CounterWithNumericDimensionsTest(meter, metricCollector, short.MaxValue); - CounterWithNumericDimensionsTest(meter, metricCollector, int.MaxValue); - CounterWithNumericDimensionsTest(meter, metricCollector, long.MaxValue); - CounterWithNumericDimensionsTest(meter, metricCollector, float.MaxValue); - CounterWithNumericDimensionsTest(meter, metricCollector, double.MaxValue); - CounterWithNumericDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void Counter_ArrayDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - CounterWithArrayDimensionsTest(meter, metricCollector, byte.MinValue); - CounterWithArrayDimensionsTest(meter, metricCollector, short.MaxValue); - CounterWithArrayDimensionsTest(meter, metricCollector, int.MaxValue); - CounterWithArrayDimensionsTest(meter, metricCollector, long.MaxValue); - CounterWithArrayDimensionsTest(meter, metricCollector, float.MaxValue); - CounterWithArrayDimensionsTest(meter, metricCollector, double.MaxValue); - CounterWithArrayDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - private static void CounterBasicTest(Meter meter, MetricCollector metricCollector, T value, T valueToAdd, T valueToAdd1, T totalSum) - where T : struct - { - var counter1 = meter.CreateCounter(Guid.NewGuid().ToString()); - var holder1 = metricCollector.GetCounterValues(counter1.Name); - - Assert.NotNull(holder1); - - counter1.Add(value); - - var recordedValue1 = metricCollector.GetCounterValue(counter1.Name); - - Assert.NotNull(recordedValue1); - Assert.Equal(value, recordedValue1.Value); - - counter1.Add(valueToAdd); - counter1.Add(valueToAdd1); - - var recordedValue2 = metricCollector.GetCounterValue(counter1.Name); - - Assert.Equal(totalSum, recordedValue2!.Value); - - var counter2 = meter.CreateCounter(Guid.NewGuid().ToString()); - var holder2 = metricCollector.GetCounterValues(counter2.Name); - - Assert.NotNull(holder2); - - counter2.Add(value); - - Assert.Equal(value, metricCollector.GetCounterValue(counter2.Name)!.Value); - - counter2.Add(valueToAdd); - counter2.Add(valueToAdd1); - - Assert.Equal(totalSum, metricCollector.GetCounterValue(counter2.Name)!.Value); - } - - private static void CounterWithStringDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - - // No dimensions - counter.Add(value); - - Assert.Equal(value, metricCollector.GetCounterValue(counter.Name)!.Value); - - // One dimension - var dimension1 = Guid.NewGuid().ToString(); - var dimension1Val = Guid.NewGuid().ToString(); - counter.Add(value, new KeyValuePair(dimension1, dimension1Val)); - - Assert.Equal(value, metricCollector.GetCounterValue(counter.Name, new KeyValuePair(dimension1, dimension1Val))!.Value); - - // Two dimensions - var dimension2 = Guid.NewGuid().ToString(); - var dimension2Val = Guid.NewGuid().ToString(); - counter.Add(value, new KeyValuePair(dimension1, dimension1Val), new KeyValuePair(dimension2, dimension2Val)); - - Assert.Equal(value, - metricCollector.GetCounterValue(counter.Name, new KeyValuePair(dimension2, dimension2Val), new KeyValuePair(dimension1, dimension1Val))!.Value); - } - - private static void CounterWithNumericDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - - // One dimension - var intDimension = Guid.NewGuid().ToString(); - const int IntVal = 15555; - counter.Add(value, new KeyValuePair(intDimension, IntVal)); - - Assert.Equal(value, metricCollector.GetCounterValue(counter.Name, new KeyValuePair(intDimension, IntVal))!.Value); - - // Two dimensions - var doubleDimension = Guid.NewGuid().ToString(); - const double DoubleVal = 1111.9999d; - counter.Add(value, new KeyValuePair(intDimension, IntVal), new KeyValuePair(doubleDimension, DoubleVal)); - - Assert.Equal(value, - metricCollector.GetCounterValue(counter.Name, new KeyValuePair(intDimension, IntVal), new KeyValuePair(doubleDimension, DoubleVal))!.Value); - - // Three dimensions - var longDimension = Guid.NewGuid().ToString(); - const long LongVal = 1_999_988_887_777_111L; - - counter.Add(value, new KeyValuePair(intDimension, IntVal), new KeyValuePair(longDimension, LongVal), - new KeyValuePair(doubleDimension, DoubleVal)); - - var actualValue = metricCollector.GetCounterValue(counter.Name, - new KeyValuePair(longDimension, LongVal), - new KeyValuePair(intDimension, IntVal), - new KeyValuePair(doubleDimension, DoubleVal)); - - Assert.Equal(value, actualValue!.Value); - } - - private static void CounterWithArrayDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - - // One dimension - var intArrayDimension = Guid.NewGuid().ToString(); - int[] intArrVal = new[] { 12, 55, 2023 }; - counter.Add(value, new KeyValuePair(intArrayDimension, intArrVal)); - - Assert.Equal(value, metricCollector.GetCounterValue(counter.Name, new KeyValuePair(intArrayDimension, intArrVal))!.Value); - - // Two dimensions - var doubleArrayDimension = Guid.NewGuid().ToString(); - double[] doubleArrVal = new[] { 1111.9999d, 0, 3.1415 }; - counter.Add(value, new KeyValuePair(intArrayDimension, intArrVal), new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - var actualValue = metricCollector.GetCounterValue(counter.Name, - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - var longArrayDimension = Guid.NewGuid().ToString(); - long[] longArrVal = new[] { 1_999_988_887_777_111L, 1_111_222_333_444_555L, 1_999_988_887_777_111L, 0 }; - - counter.Add(value, new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal), - new KeyValuePair(longArrayDimension, longArrVal)); - - actualValue = metricCollector.GetCounterValue(counter.Name, - new KeyValuePair(longArrayDimension, longArrVal), - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Histogram.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Histogram.cs deleted file mode 100644 index e840d18651c..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.Histogram.cs +++ /dev/null @@ -1,209 +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 Microsoft.Extensions.Telemetry.Testing.Metering; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public partial class MetricCollectorTests -{ - [Fact] - public void Histogram_BasicTest() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - HistogramBasicTest(meter, metricCollector, 2, 5); - HistogramBasicTest(meter, metricCollector, 20, 50); - HistogramBasicTest(meter, metricCollector, 200, 500); - HistogramBasicTest(meter, metricCollector, 2000L, 5000L); - HistogramBasicTest(meter, metricCollector, 1.22f, 3.44f); - HistogramBasicTest(meter, metricCollector, 5.22, 6.44); - HistogramBasicTest(meter, metricCollector, 0.99m, 15); - } - - [Fact] - public void Histogram_StringDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - HistogramWithStringDimensionsTest(meter, metricCollector, byte.MinValue); - HistogramWithStringDimensionsTest(meter, metricCollector, short.MinValue); - HistogramWithStringDimensionsTest(meter, metricCollector, int.MinValue); - HistogramWithStringDimensionsTest(meter, metricCollector, long.MinValue); - HistogramWithStringDimensionsTest(meter, metricCollector, float.MinValue); - HistogramWithStringDimensionsTest(meter, metricCollector, double.MinValue); - HistogramWithStringDimensionsTest(meter, metricCollector, decimal.MinValue); - } - - [Fact] - public void Histogram_NumericDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - HistogramWithNumericDimensionsTest(meter, metricCollector, byte.MinValue); - HistogramWithNumericDimensionsTest(meter, metricCollector, short.MaxValue); - HistogramWithNumericDimensionsTest(meter, metricCollector, int.MaxValue); - HistogramWithNumericDimensionsTest(meter, metricCollector, long.MaxValue); - HistogramWithNumericDimensionsTest(meter, metricCollector, float.MaxValue); - HistogramWithNumericDimensionsTest(meter, metricCollector, double.MaxValue); - HistogramWithNumericDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void Histogram_ArrayDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - HistogramWithArrayDimensionsTest(meter, metricCollector, byte.MinValue); - HistogramWithArrayDimensionsTest(meter, metricCollector, short.MaxValue); - HistogramWithArrayDimensionsTest(meter, metricCollector, int.MaxValue); - HistogramWithArrayDimensionsTest(meter, metricCollector, long.MaxValue); - HistogramWithArrayDimensionsTest(meter, metricCollector, float.MaxValue); - HistogramWithArrayDimensionsTest(meter, metricCollector, double.MaxValue); - HistogramWithArrayDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - private static void HistogramBasicTest(Meter meter, MetricCollector metricCollector, T value, T secondValue) - where T : struct - { - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - var holder1 = metricCollector.GetHistogramValues(histogram.Name); - - Assert.NotNull(holder1); - - histogram.Record(value); - - var recordedValue1 = metricCollector.GetHistogramValue(histogram.Name); - - Assert.NotNull(recordedValue1); - Assert.Equal(value, recordedValue1.Value); - Assert.Equal(1, holder1.AllValues.Count); - - histogram.Record(secondValue); - - var recordedValue2 = metricCollector.GetHistogramValue(histogram.Name); - - Assert.Equal(secondValue, recordedValue2!.Value); - Assert.Equal(2, holder1.AllValues.Count); - - var histogram2 = meter.CreateHistogram(Guid.NewGuid().ToString()); - var holder2 = metricCollector.GetHistogramValues(histogram2.Name); - - Assert.NotNull(holder2); - - histogram2.Record(value); - - Assert.Equal(value, metricCollector.GetHistogramValue(histogram2.Name)!.Value); - - histogram2.Record(secondValue); - - Assert.Equal(secondValue, metricCollector.GetHistogramValue(histogram2.Name)!.Value); - } - - private static void HistogramWithStringDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - - // No dimensions - histogram.Record(value); - - Assert.Equal(value, metricCollector.GetHistogramValue(histogram.Name)!.Value); - - // One dimension - var dimension1 = Guid.NewGuid().ToString(); - var dimension1Val = Guid.NewGuid().ToString(); - histogram.Record(value, new KeyValuePair(dimension1, dimension1Val)); - - Assert.Equal(value, metricCollector.GetHistogramValue(histogram.Name, new KeyValuePair(dimension1, dimension1Val))!.Value); - - // Two dimensions - var dimension2 = Guid.NewGuid().ToString(); - var dimension2Val = Guid.NewGuid().ToString(); - histogram.Record(value, new KeyValuePair(dimension1, dimension1Val), new KeyValuePair(dimension2, dimension2Val)); - - Assert.Equal(value, - metricCollector.GetHistogramValue(histogram.Name, new KeyValuePair(dimension2, dimension2Val), new KeyValuePair(dimension1, dimension1Val))!.Value); - } - - private static void HistogramWithNumericDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - - // One dimension - var intDimension = Guid.NewGuid().ToString(); - int intVal = 15555; - histogram.Record(value, new KeyValuePair(intDimension, intVal)); - - Assert.Equal(value, metricCollector.GetHistogramValue(histogram.Name, new KeyValuePair(intDimension, intVal))!.Value); - - // Two dimensions - var doubleDimension = Guid.NewGuid().ToString(); - double doubleVal = 1111.9999d; - histogram.Record(value, new KeyValuePair(intDimension, intVal), new KeyValuePair(doubleDimension, doubleVal)); - - Assert.Equal(value, - metricCollector.GetHistogramValue(histogram.Name, new KeyValuePair(intDimension, intVal), new KeyValuePair(doubleDimension, doubleVal))!.Value); - - // Three dimensions - var longDimension = Guid.NewGuid().ToString(); - long longVal = 1_999_988_887_777_111L; - - histogram.Record(value, new KeyValuePair(intDimension, intVal), new KeyValuePair(longDimension, longVal), - new KeyValuePair(doubleDimension, doubleVal)); - - var actualValue = metricCollector.GetHistogramValue(histogram.Name, - new KeyValuePair(longDimension, longVal), - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - } - - private static void HistogramWithArrayDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - - // One dimension - var intArrayDimension = Guid.NewGuid().ToString(); - int[] intArrVal = new[] { 12, 55, 2023 }; - histogram.Record(value, new KeyValuePair(intArrayDimension, intArrVal)); - - Assert.Equal(value, metricCollector.GetHistogramValue(histogram.Name, new KeyValuePair(intArrayDimension, intArrVal))!.Value); - - // Two dimensions - var doubleArrayDimension = Guid.NewGuid().ToString(); - double[] doubleArrVal = new[] { 1111.9999d, 0, 3.1415 }; - histogram.Record(value, new KeyValuePair(intArrayDimension, intArrVal), new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - var actualValue = metricCollector.GetHistogramValue(histogram.Name, - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - var longArrayDimension = Guid.NewGuid().ToString(); - long[] longArrVal = new[] { 1_999_988_887_777_111L, 1_111_222_333_444_555L, 1_999_988_887_777_111L, 0 }; - - histogram.Record(value, new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal), - new KeyValuePair(longArrayDimension, longArrVal)); - - actualValue = metricCollector.GetHistogramValue(histogram.Name, - new KeyValuePair(longArrayDimension, longArrVal), - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableCounter.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableCounter.cs deleted file mode 100644 index dca620fdce4..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableCounter.cs +++ /dev/null @@ -1,298 +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 Microsoft.Extensions.Telemetry.Testing.Metering; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public partial class MetricCollectorTests -{ - [Fact] - public void ObservableCounter_BasicTest() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableCounterBasicTest(meter, metricCollector, 2, 5, 10, 17); - ObservableCounterBasicTest(meter, metricCollector, 20, 50, 100, 170); - ObservableCounterBasicTest(meter, metricCollector, 200, 500, 1000, 1700); - ObservableCounterBasicTest(meter, metricCollector, 2000L, 5000L, 10000L, 17000L); - ObservableCounterBasicTest(meter, metricCollector, 1.22f, 3.44f, 0, 1.22f + 3.44f); - ObservableCounterBasicTest(meter, metricCollector, 5.22, 6.44, 10, 5.22 + 6.44 + 10); - ObservableCounterBasicTest(meter, metricCollector, 0.99m, 15, 25.99m, 41.98m); - } - - [Fact] - public void ObservableCounter_StringDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableCounterWithStringDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableCounterWithStringDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableCounterWithStringDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableCounterWithStringDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableCounterWithStringDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableCounterWithStringDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableCounterWithStringDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void ObservableCounter_NumericDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableCounterWithNumericDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableCounterWithNumericDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableCounterWithNumericDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableCounterWithNumericDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableCounterWithNumericDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableCounterWithNumericDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableCounterWithNumericDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void ObservableCounter_ArrayDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableCounterWithArrayDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableCounterWithArrayDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableCounterWithArrayDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableCounterWithArrayDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableCounterWithArrayDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableCounterWithArrayDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableCounterWithArrayDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - private static void ObservableCounterBasicTest(Meter meter, MetricCollector metricCollector, T value1, T value2, T value3, T value4) - where T : struct - { - var states = new[] { value1, value2, value3, value4 }; - - int index = 0; - var observableFunc = () => - { - if (index >= states.Length) - { - index = 0; - } - - return states[index++]; - }; - - var observableCounter1 = meter.CreateObservableCounter(Guid.NewGuid().ToString(), observableFunc); - var holder1 = metricCollector.GetObservableCounterValues(observableCounter1.Name); - - Assert.NotNull(holder1); - - metricCollector.CollectObservableInstruments(); - var recordedValue1 = metricCollector.GetObservableCounterValue(observableCounter1.Name); - - Assert.NotNull(recordedValue1); - Assert.Equal(value1, recordedValue1.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value2, metricCollector.GetObservableCounterValue(observableCounter1.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value3, metricCollector.GetObservableCounterValue(observableCounter1.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value4, metricCollector.GetObservableCounterValue(observableCounter1.Name)!.Value); - - int index2 = 0; - var observableFunc2 = () => - { - if (index2 >= states.Length) - { - index2 = 0; - } - - return states[index2++]; - }; - - var observableCounter2 = meter.CreateObservableCounter(Guid.NewGuid().ToString(), observableFunc2); - var holder2 = metricCollector.GetObservableCounterValues(observableCounter2.Name); - - Assert.NotNull(holder2); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value1, metricCollector.GetObservableCounterValue(observableCounter2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value2, metricCollector.GetObservableCounterValue(observableCounter2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value3, metricCollector.GetObservableCounterValue(observableCounter2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value4, metricCollector.GetObservableCounterValue(observableCounter2.Name)!.Value); - } - - private static void ObservableCounterWithStringDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var dimension1 = Guid.NewGuid().ToString(); - var dimension1Val = Guid.NewGuid().ToString(); - var dimension2 = Guid.NewGuid().ToString(); - var dimension2Val = Guid.NewGuid().ToString(); - - var measurements = new[] - { - new Measurement(value), - new Measurement(value, new KeyValuePair(dimension1, dimension1Val)), - new Measurement(value, new KeyValuePair(dimension1, dimension1Val), new KeyValuePair(dimension2, dimension2Val)), - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableCounter = meter.CreateObservableCounter(Guid.NewGuid().ToString(), observableFunc); - - // No dimensions - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableCounterValue(observableCounter.Name)!.Value); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableCounterValue(observableCounter.Name, new KeyValuePair(dimension1, dimension1Val))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableCounterValue( - observableCounter.Name, - new KeyValuePair(dimension2, dimension2Val), - new KeyValuePair(dimension1, dimension1Val)); - Assert.Equal(value, actualValue!.Value); - } - - private static void ObservableCounterWithNumericDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var intDimension = Guid.NewGuid().ToString(); - int intVal = 15555; - var doubleDimension = Guid.NewGuid().ToString(); - double doubleVal = 1111.9999d; - var longDimension = Guid.NewGuid().ToString(); - long longVal = 1_999_988_887_777_111L; - - var measurements = new[] - { - new Measurement(value, new KeyValuePair(intDimension, intVal)), - new Measurement(value, new KeyValuePair(intDimension, intVal), new KeyValuePair(doubleDimension, doubleVal)), - new Measurement(value, new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal), - new KeyValuePair(longDimension, longVal)) - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableCounter = meter.CreateObservableCounter(Guid.NewGuid().ToString(), observableFunc); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableCounterValue(observableCounter.Name, new KeyValuePair(intDimension, intVal))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableCounterValue( - observableCounter.Name, - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - metricCollector.CollectObservableInstruments(); - actualValue = metricCollector.GetObservableCounterValue( - observableCounter.Name, - new KeyValuePair(longDimension, longVal), - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - } - - private static void ObservableCounterWithArrayDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var intArrayDimension = Guid.NewGuid().ToString(); - int[] intArrVal = new[] { 12, 55, 2023 }; - var doubleArrayDimension = Guid.NewGuid().ToString(); - double[] doubleArrVal = new[] { 1111.9999d, 0, 3.1415 }; - var longArrayDimension = Guid.NewGuid().ToString(); - long[] longArrVal = new[] { 1_999_988_887_777_111L, 1_111_222_333_444_555L, 1_999_988_887_777_111L, 0 }; - - var measurements = new[] - { - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal)), - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal), new KeyValuePair(doubleArrayDimension, doubleArrVal)), - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal), - new KeyValuePair(longArrayDimension, longArrVal)) - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableCounter = meter.CreateObservableCounter(Guid.NewGuid().ToString(), observableFunc); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableCounterValue(observableCounter.Name, new KeyValuePair(intArrayDimension, intArrVal))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableCounterValue(observableCounter.Name, - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - metricCollector.CollectObservableInstruments(); - actualValue = metricCollector.GetObservableCounterValue(observableCounter.Name, - new KeyValuePair(longArrayDimension, longArrVal), - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableGauge.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableGauge.cs deleted file mode 100644 index e1365eb773d..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableGauge.cs +++ /dev/null @@ -1,297 +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 Microsoft.Extensions.Telemetry.Testing.Metering; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public partial class MetricCollectorTests -{ - [Fact] - public void ObservableGauge_BasicTest() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableGaugeBasicTest(meter, metricCollector, 2, 5, 10, 17); - ObservableGaugeBasicTest(meter, metricCollector, 20, 50, 100, 170); - ObservableGaugeBasicTest(meter, metricCollector, 200, 500, 1000, 1700); - ObservableGaugeBasicTest(meter, metricCollector, 2000L, 5000L, 10000L, 17000L); - ObservableGaugeBasicTest(meter, metricCollector, 1.22f, 3.44f, 0, 1.22f + 3.44f); - ObservableGaugeBasicTest(meter, metricCollector, 5.22, 6.44, 10, 5.22 + 6.44 + 10); - ObservableGaugeBasicTest(meter, metricCollector, 0.99m, 15, 25.99m, 41.98m); - } - - [Fact] - public void ObservableGauge_StringDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableGaugeWithStringDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableGaugeWithStringDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableGaugeWithStringDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableGaugeWithStringDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableGaugeWithStringDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableGaugeWithStringDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableGaugeWithStringDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void ObservableGauge_NumericDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableGaugeWithNumericDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableGaugeWithNumericDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableGaugeWithNumericDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableGaugeWithNumericDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableGaugeWithNumericDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableGaugeWithNumericDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableGaugeWithNumericDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void ObservableGauge_ArrayDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableGaugeWithArrayDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableGaugeWithArrayDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableGaugeWithArrayDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableGaugeWithArrayDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableGaugeWithArrayDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableGaugeWithArrayDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableGaugeWithArrayDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - private static void ObservableGaugeBasicTest(Meter meter, MetricCollector metricCollector, T value1, T value2, T value3, T value4) - where T : struct - { - var states = new[] { value1, value2, value3, value4 }; - - int index = 0; - var observableFunc = () => - { - if (index >= states.Length) - { - index = 0; - } - - return states[index++]; - }; - - var observableGauge1 = meter.CreateObservableGauge(Guid.NewGuid().ToString(), observableFunc); - var holder1 = metricCollector.GetObservableGaugeValues(observableGauge1.Name); - - Assert.NotNull(holder1); - - metricCollector.CollectObservableInstruments(); - var recordedValue1 = metricCollector.GetObservableGaugeValue(observableGauge1.Name); - - Assert.NotNull(recordedValue1); - Assert.Equal(value1, recordedValue1.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value2, metricCollector.GetObservableGaugeValue(observableGauge1.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value3, metricCollector.GetObservableGaugeValue(observableGauge1.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value4, metricCollector.GetObservableGaugeValue(observableGauge1.Name)!.Value); - - int index2 = 0; - var observableFunc2 = () => - { - if (index2 >= states.Length) - { - index2 = 0; - } - - return states[index2++]; - }; - - var observableGauge2 = meter.CreateObservableGauge(Guid.NewGuid().ToString(), observableFunc2); - var holder2 = metricCollector.GetObservableGaugeValues(observableGauge2.Name); - - Assert.NotNull(holder2); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value1, metricCollector.GetObservableGaugeValue(observableGauge2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value2, metricCollector.GetObservableGaugeValue(observableGauge2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value3, metricCollector.GetObservableGaugeValue(observableGauge2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value4, metricCollector.GetObservableGaugeValue(observableGauge2.Name)!.Value); - } - - private static void ObservableGaugeWithStringDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var dimension1 = Guid.NewGuid().ToString(); - var dimension1Val = Guid.NewGuid().ToString(); - var dimension2 = Guid.NewGuid().ToString(); - var dimension2Val = Guid.NewGuid().ToString(); - - var measurements = new[] - { - new Measurement(value), - new Measurement(value, new KeyValuePair(dimension1, dimension1Val)), - new Measurement(value, new KeyValuePair(dimension1, dimension1Val), new KeyValuePair(dimension2, dimension2Val)), - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableGauge = meter.CreateObservableGauge(Guid.NewGuid().ToString(), observableFunc); - - // No dimensions - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableGaugeValue(observableGauge.Name)!.Value); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableGaugeValue(observableGauge.Name, new KeyValuePair(dimension1, dimension1Val))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualvalue = metricCollector.GetObservableGaugeValue( - observableGauge.Name, - new KeyValuePair(dimension2, dimension2Val), - new KeyValuePair(dimension1, dimension1Val)); - Assert.Equal(value, actualvalue!.Value); - } - - private static void ObservableGaugeWithNumericDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var intDimension = Guid.NewGuid().ToString(); - int intVal = 15555; - var doubleDimension = Guid.NewGuid().ToString(); - double doubleVal = 1111.9999d; - var longDimension = Guid.NewGuid().ToString(); - long longVal = 1_999_988_887_777_111L; - - var measurements = new[] - { - new Measurement(value, new KeyValuePair(intDimension, intVal)), - new Measurement(value, new KeyValuePair(intDimension, intVal), new KeyValuePair(doubleDimension, doubleVal)), - new Measurement(value, new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal), - new KeyValuePair(longDimension, longVal)) - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableGauge = meter.CreateObservableGauge(Guid.NewGuid().ToString(), observableFunc); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableGaugeValue(observableGauge.Name, new KeyValuePair(intDimension, intVal))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableGaugeValue( - observableGauge.Name, - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - metricCollector.CollectObservableInstruments(); - actualValue = metricCollector.GetObservableGaugeValue(observableGauge.Name, - new KeyValuePair(longDimension, longVal), - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - } - - private static void ObservableGaugeWithArrayDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var intArrayDimension = Guid.NewGuid().ToString(); - int[] intArrVal = new[] { 12, 55, 2023 }; - var doubleArrayDimension = Guid.NewGuid().ToString(); - double[] doubleArrVal = new[] { 1111.9999d, 0, 3.1415 }; - var longArrayDimension = Guid.NewGuid().ToString(); - long[] longArrVal = new[] { 1_999_988_887_777_111L, 1_111_222_333_444_555L, 1_999_988_887_777_111L, 0 }; - - var measurements = new[] - { - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal)), - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal), new KeyValuePair(doubleArrayDimension, doubleArrVal)), - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal), - new KeyValuePair(longArrayDimension, longArrVal)) - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableGauge = meter.CreateObservableGauge(Guid.NewGuid().ToString(), observableFunc); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableGaugeValue(observableGauge.Name, new KeyValuePair(intArrayDimension, intArrVal))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableGaugeValue(observableGauge.Name, - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - metricCollector.CollectObservableInstruments(); - actualValue = metricCollector.GetObservableGaugeValue(observableGauge.Name, - new KeyValuePair(longArrayDimension, longArrVal), - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableUpdownCounter.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableUpdownCounter.cs deleted file mode 100644 index 8accf92f289..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.ObservableUpdownCounter.cs +++ /dev/null @@ -1,297 +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 Microsoft.Extensions.Telemetry.Testing.Metering; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public partial class MetricCollectorTests -{ - [Fact] - public void ObservableUpDownCounter_BasicTest() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableUpDownCounterBasicTest(meter, metricCollector, 2, 5, 10, 17); - ObservableUpDownCounterBasicTest(meter, metricCollector, 20, 50, 100, 170); - ObservableUpDownCounterBasicTest(meter, metricCollector, 200, 500, 1000, 1700); - ObservableUpDownCounterBasicTest(meter, metricCollector, 2000L, 5000L, 10000L, 17000L); - ObservableUpDownCounterBasicTest(meter, metricCollector, 1.22f, 3.44f, 0, 1.22f + 3.44f); - ObservableUpDownCounterBasicTest(meter, metricCollector, 5.22, 6.44, 10, 5.22 + 6.44 + 10); - ObservableUpDownCounterBasicTest(meter, metricCollector, 0.99m, 15, 25.99m, 41.98m); - } - - [Fact] - public void ObservableUpDownCounter_StringDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableUpDownCounterWithStringDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableUpDownCounterWithStringDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableUpDownCounterWithStringDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableUpDownCounterWithStringDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableUpDownCounterWithStringDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableUpDownCounterWithStringDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableUpDownCounterWithStringDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void ObservableUpDownCounter_NumericDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableUpDownCounterWithNumericDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableUpDownCounterWithNumericDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableUpDownCounterWithNumericDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableUpDownCounterWithNumericDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableUpDownCounterWithNumericDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableUpDownCounterWithNumericDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableUpDownCounterWithNumericDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void ObservableUpDownCounter_ArrayDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - ObservableUpDownCounterWithArrayDimensionsTest(meter, metricCollector, byte.MinValue); - ObservableUpDownCounterWithArrayDimensionsTest(meter, metricCollector, short.MaxValue); - ObservableUpDownCounterWithArrayDimensionsTest(meter, metricCollector, int.MaxValue); - ObservableUpDownCounterWithArrayDimensionsTest(meter, metricCollector, long.MaxValue); - ObservableUpDownCounterWithArrayDimensionsTest(meter, metricCollector, float.MaxValue); - ObservableUpDownCounterWithArrayDimensionsTest(meter, metricCollector, double.MaxValue); - ObservableUpDownCounterWithArrayDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - private static void ObservableUpDownCounterBasicTest(Meter meter, MetricCollector metricCollector, T value1, T value2, T value3, T value4) - where T : struct - { - var states = new[] { value1, value2, value3, value4 }; - - int index = 0; - var observableFunc = () => - { - if (index >= states.Length) - { - index = 0; - } - - return states[index++]; - }; - - var observableUpDownCounter1 = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), observableFunc); - var holder1 = metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter1.Name); - - Assert.NotNull(holder1); - - metricCollector.CollectObservableInstruments(); - var recordedValue1 = metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter1.Name); - - Assert.NotNull(recordedValue1); - Assert.Equal(value1, recordedValue1.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value2, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter1.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value3, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter1.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value4, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter1.Name)!.Value); - - int index2 = 0; - var observableFunc2 = () => - { - if (index2 >= states.Length) - { - index2 = 0; - } - - return states[index2++]; - }; - - var observableUpDownCounter2 = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), observableFunc2); - var holder2 = metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter2.Name); - - Assert.NotNull(holder2); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value1, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value2, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value3, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter2.Name)!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(value4, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter2.Name)!.Value); - } - - private static void ObservableUpDownCounterWithStringDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var dimension1 = Guid.NewGuid().ToString(); - var dimension1Val = Guid.NewGuid().ToString(); - var dimension2 = Guid.NewGuid().ToString(); - var dimension2Val = Guid.NewGuid().ToString(); - - var measurements = new[] - { - new Measurement(value), - new Measurement(value, new KeyValuePair(dimension1, dimension1Val)), - new Measurement(value, new KeyValuePair(dimension1, dimension1Val), new KeyValuePair(dimension2, dimension2Val)), - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableUpDownCounter = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), observableFunc); - - // No dimensions - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)!.Value); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name, new KeyValuePair(dimension1, dimension1Val))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableUpDownCounterValue( - observableUpDownCounter.Name, - new KeyValuePair(dimension2, dimension2Val), - new KeyValuePair(dimension1, dimension1Val)); - Assert.Equal(value, actualValue!.Value); - } - - private static void ObservableUpDownCounterWithNumericDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var intDimension = Guid.NewGuid().ToString(); - int intVal = 15555; - var doubleDimension = Guid.NewGuid().ToString(); - double doubleVal = 1111.9999d; - var longDimension = Guid.NewGuid().ToString(); - long longVal = 1_999_988_887_777_111L; - - var measurements = new[] - { - new Measurement(value, new KeyValuePair(intDimension, intVal)), - new Measurement(value, new KeyValuePair(intDimension, intVal), new KeyValuePair(doubleDimension, doubleVal)), - new Measurement(value, new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal), - new KeyValuePair(longDimension, longVal)) - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableUpDownCounter = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), observableFunc); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name, new KeyValuePair(intDimension, intVal))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableUpDownCounterValue( - observableUpDownCounter.Name, - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - metricCollector.CollectObservableInstruments(); - actualValue = metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name, - new KeyValuePair(longDimension, longVal), - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - } - - private static void ObservableUpDownCounterWithArrayDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var intArrayDimension = Guid.NewGuid().ToString(); - int[] intArrVal = new[] { 12, 55, 2023 }; - var doubleArrayDimension = Guid.NewGuid().ToString(); - double[] doubleArrVal = new[] { 1111.9999d, 0, 3.1415 }; - var longArrayDimension = Guid.NewGuid().ToString(); - long[] longArrVal = new[] { 1_999_988_887_777_111L, 1_111_222_333_444_555L, 1_999_988_887_777_111L, 0 }; - - var measurements = new[] - { - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal)), - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal), new KeyValuePair(doubleArrayDimension, doubleArrVal)), - new Measurement(value, new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal), - new KeyValuePair(longArrayDimension, longArrVal)) - }; - - int index = 0; - var observableFunc = () => - { - if (index >= measurements.Length) - { - index = 0; - } - - return measurements[index++]; - }; - - var observableUpDownCounter = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), observableFunc); - - // One dimension - metricCollector.CollectObservableInstruments(); - Assert.Equal(value, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name, new KeyValuePair(intArrayDimension, intArrVal))!.Value); - - // Two dimensions - metricCollector.CollectObservableInstruments(); - var actualValue = metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name, - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - metricCollector.CollectObservableInstruments(); - actualValue = metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name, - new KeyValuePair(longArrayDimension, longArrVal), - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.UpDownCounter.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.UpDownCounter.cs deleted file mode 100644 index f4b256025b5..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.UpDownCounter.cs +++ /dev/null @@ -1,215 +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 Microsoft.Extensions.Telemetry.Testing.Metering; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public partial class MetricCollectorTests -{ - [Fact] - public void UpDownCounter_BasicTest() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - UpDownCounterBasicTest(meter, metricCollector, 2, 5, 10, 17); - UpDownCounterBasicTest(meter, metricCollector, 20, 50, 100, 170); - UpDownCounterBasicTest(meter, metricCollector, 200, 500, 1000, 1700); - UpDownCounterBasicTest(meter, metricCollector, 2000L, 5000L, 10000L, 17000L); - UpDownCounterBasicTest(meter, metricCollector, 1.22f, 3.44f, 0, 1.22f + 3.44f); - UpDownCounterBasicTest(meter, metricCollector, 5.22, 6.44, 10, 5.22 + 6.44 + 10); - UpDownCounterBasicTest(meter, metricCollector, 0.99m, 15, 25.99m, 41.98m); - } - - [Fact] - public void UpDownCounter_StringDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - UpDownCounterWithStringDimensionsTest(meter, metricCollector, byte.MinValue); - UpDownCounterWithStringDimensionsTest(meter, metricCollector, short.MaxValue); - UpDownCounterWithStringDimensionsTest(meter, metricCollector, int.MaxValue); - UpDownCounterWithStringDimensionsTest(meter, metricCollector, long.MaxValue); - UpDownCounterWithStringDimensionsTest(meter, metricCollector, float.MaxValue); - UpDownCounterWithStringDimensionsTest(meter, metricCollector, double.MaxValue); - UpDownCounterWithStringDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void UpDownCounter_NumericDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - UpDownCounterWithNumericDimensionsTest(meter, metricCollector, byte.MinValue); - UpDownCounterWithNumericDimensionsTest(meter, metricCollector, short.MaxValue); - UpDownCounterWithNumericDimensionsTest(meter, metricCollector, int.MaxValue); - UpDownCounterWithNumericDimensionsTest(meter, metricCollector, long.MaxValue); - UpDownCounterWithNumericDimensionsTest(meter, metricCollector, float.MaxValue); - UpDownCounterWithNumericDimensionsTest(meter, metricCollector, double.MaxValue); - UpDownCounterWithNumericDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - [Fact] - public void UpDownCounter_ArrayDimensionsAreHandled() - { - using var metricCollector = new MetricCollector(); - using var meter = new Meter(string.Empty); - - UpDownCounterWithArrayDimensionsTest(meter, metricCollector, byte.MinValue); - UpDownCounterWithArrayDimensionsTest(meter, metricCollector, short.MaxValue); - UpDownCounterWithArrayDimensionsTest(meter, metricCollector, int.MaxValue); - UpDownCounterWithArrayDimensionsTest(meter, metricCollector, long.MaxValue); - UpDownCounterWithArrayDimensionsTest(meter, metricCollector, float.MaxValue); - UpDownCounterWithArrayDimensionsTest(meter, metricCollector, double.MaxValue); - UpDownCounterWithArrayDimensionsTest(meter, metricCollector, decimal.MaxValue); - } - - private static void UpDownCounterBasicTest(Meter meter, MetricCollector metricCollector, T value, T valueToAdd, T valueToAdd1, T totalSum) - where T : struct - { - var upDownCounter1 = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - var holder1 = metricCollector.GetUpDownCounterValues(upDownCounter1.Name); - - Assert.NotNull(holder1); - - upDownCounter1.Add(value); - - var recordedValue1 = metricCollector.GetUpDownCounterValue(upDownCounter1.Name); - - Assert.NotNull(recordedValue1); - Assert.Equal(value, recordedValue1.Value); - - upDownCounter1.Add(valueToAdd); - upDownCounter1.Add(valueToAdd1); - - var recordedValue2 = metricCollector.GetUpDownCounterValue(upDownCounter1.Name); - - Assert.Equal(totalSum, recordedValue2!.Value); - - var upDownCounter2 = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - var holder2 = metricCollector.GetUpDownCounterValues(upDownCounter2.Name); - - Assert.NotNull(holder2); - - upDownCounter2.Add(value); - - Assert.Equal(value, metricCollector.GetUpDownCounterValue(upDownCounter2.Name)!.Value); - - upDownCounter2.Add(valueToAdd); - upDownCounter2.Add(valueToAdd1); - - Assert.Equal(totalSum, metricCollector.GetUpDownCounterValue(upDownCounter2.Name)!.Value); - } - - private static void UpDownCounterWithStringDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var upDownCounter = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - - // No dimensions - upDownCounter.Add(value); - - Assert.Equal(value, metricCollector.GetUpDownCounterValue(upDownCounter.Name)!.Value); - - // One dimension - var dimension1 = Guid.NewGuid().ToString(); - var dimension1Val = Guid.NewGuid().ToString(); - upDownCounter.Add(value, new KeyValuePair(dimension1, dimension1Val)); - - Assert.Equal(value, metricCollector.GetUpDownCounterValue(upDownCounter.Name, new KeyValuePair(dimension1, dimension1Val))!.Value); - - // Two dimensions - var dimension2 = Guid.NewGuid().ToString(); - var dimension2Val = Guid.NewGuid().ToString(); - upDownCounter.Add(value, new KeyValuePair(dimension1, dimension1Val), new KeyValuePair(dimension2, dimension2Val)); - - var actualValue = metricCollector.GetUpDownCounterValue( - upDownCounter.Name, - new KeyValuePair(dimension2, dimension2Val), - new KeyValuePair(dimension1, dimension1Val)); - Assert.Equal(value, actualValue!.Value); - } - - private static void UpDownCounterWithNumericDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var upDownCounter = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - - // One dimension - var intDimension = Guid.NewGuid().ToString(); - int intVal = 15555; - upDownCounter.Add(value, new KeyValuePair(intDimension, intVal)); - - Assert.Equal(value, metricCollector.GetUpDownCounterValue(upDownCounter.Name, new KeyValuePair(intDimension, intVal))!.Value); - - // Two dimensions - var doubleDimension = Guid.NewGuid().ToString(); - double doubleVal = 1111.9999d; - upDownCounter.Add(value, new KeyValuePair(intDimension, intVal), new KeyValuePair(doubleDimension, doubleVal)); - - var actualValue = metricCollector.GetUpDownCounterValue( - upDownCounter.Name, - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - var longDimension = Guid.NewGuid().ToString(); - long longVal = 1_999_988_887_777_111L; - - upDownCounter.Add(value, new KeyValuePair(intDimension, intVal), new KeyValuePair(longDimension, longVal), - new KeyValuePair(doubleDimension, doubleVal)); - - actualValue = metricCollector.GetUpDownCounterValue(upDownCounter.Name, - new KeyValuePair(longDimension, longVal), - new KeyValuePair(intDimension, intVal), - new KeyValuePair(doubleDimension, doubleVal)); - Assert.Equal(value, actualValue!.Value); - } - - private static void UpDownCounterWithArrayDimensionsTest(Meter meter, MetricCollector metricCollector, T value) - where T : struct - { - var upDownCounter = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - - // One dimension - var intArrayDimension = Guid.NewGuid().ToString(); - int[] intArrVal = new[] { 12, 55, 2023 }; - upDownCounter.Add(value, new KeyValuePair(intArrayDimension, intArrVal)); - - Assert.Equal(value, metricCollector.GetUpDownCounterValue(upDownCounter.Name, new KeyValuePair(intArrayDimension, intArrVal))!.Value); - - // Two dimensions - var doubleArrayDimension = Guid.NewGuid().ToString(); - double[] doubleArrVal = new[] { 1111.9999d, 0, 3.1415 }; - upDownCounter.Add(value, new KeyValuePair(intArrayDimension, intArrVal), new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - var actualValue = metricCollector.GetUpDownCounterValue(upDownCounter.Name, - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - - // Three dimensions - var longArrayDimension = Guid.NewGuid().ToString(); - long[] longArrVal = new[] { 1_999_988_887_777_111L, 1_111_222_333_444_555L, 1_999_988_887_777_111L, 0 }; - - upDownCounter.Add(value, new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal), - new KeyValuePair(longArrayDimension, longArrVal)); - - actualValue = metricCollector.GetUpDownCounterValue(upDownCounter.Name, - new KeyValuePair(longArrayDimension, longArrVal), - new KeyValuePair(intArrayDimension, intArrVal), - new KeyValuePair(doubleArrayDimension, doubleArrVal)); - - Assert.Equal(value, actualValue!.Value); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.cs index 1b507a4ec50..f76f9171cfa 100644 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.cs +++ b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricCollectorTests.cs @@ -2,614 +2,371 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.Metrics; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Time.Testing; using Xunit; namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; -public partial class MetricCollectorTests +public static class MetricCollectorTests { [Fact] - public void Ctor_Throws_WhenInvalidArguments() + public static void Constructor_NullAndEmptyChecks() { - Assert.Throws(() => new MetricCollector((Meter)null!)); - Assert.Throws(() => new MetricCollector((IEnumerable)null!)); - Assert.Throws(() => new MetricCollector((Instrument)null!)); + Assert.Throws(() => new MetricCollector((Instrument)null!)); + Assert.Throws(() => new MetricCollector((ObservableInstrument)null!)); + Assert.Throws(() => new MetricCollector(new Meter(Guid.NewGuid().ToString()), null!)); + Assert.Throws(() => new MetricCollector(null!, "Hello")); + Assert.Throws(() => new MetricCollector(null, null!, "Hello")); + + Assert.Throws(() => new MetricCollector(new Meter(Guid.NewGuid().ToString()), string.Empty)); + Assert.Throws(() => new MetricCollector(null, string.Empty, "Hello")); + Assert.Throws(() => new MetricCollector(null, "Hello", string.Empty)); } [Fact] - public void Measurements_AreFilteredOut_WithMeterNameFilter() + public static void Constructor_TypeChecks() { - using var meter1 = new Meter(Guid.NewGuid().ToString()); - using var meter2 = new Meter(Guid.NewGuid().ToString()); - using var meterToIgnore = new Meter(Guid.NewGuid().ToString()); - - using var metricCollector = new MetricCollector(new[] { meter1.Name, meter2.Name }); - - const int IntValue1 = 999; - meter1.CreateCounter("int_counter1").Add(IntValue1); - - // Measurement is captured - Assert.Equal(IntValue1, metricCollector.GetCounterValue("int_counter1")); - - const double DoubleValue1 = 999; - meter2.CreateHistogram("double_histogram1").Record(DoubleValue1); - - // Measurement is captured - Assert.Equal(DoubleValue1, metricCollector.GetHistogramValue("double_histogram1")); - - const int IntValue2 = 999999; - meterToIgnore.CreateCounter("int_counter2").Add(IntValue2); - - // Measurement is filtered out - Assert.Null(metricCollector.GetCounterValue("int_counter2")); - - const double DoubleValue2 = 111.2222; - meter2.CreateHistogram("double_histogram2").Record(DoubleValue2); - - // Measurement is filtered out - Assert.Null(metricCollector.GetCounterValue("double_histogram2")); - } - - [Fact] - public void Measurements_SingleInstrument() - { - var meterName = Guid.NewGuid().ToString(); - using var meter1 = new Meter(meterName); - using var meter2 = new Meter(meterName); - - const int IntValue1 = 123459; - const int IntValue2 = 987654; - - var ctr1 = meter1.CreateCounter("int_counter1"); - var ctr2 = meter2.CreateCounter("int_counter2"); - - using var metricCollector = new MetricCollector(ctr2); - - ctr1.Add(IntValue1); - ctr2.Add(IntValue2); - - var x = metricCollector.GetAllCounters(); - - // Single measurement is captured - Assert.Equal(1, metricCollector.Count); - Assert.Equal(IntValue2, metricCollector.GetCounterValue("int_counter2")); - } - - [Fact] - public void Measurements_AreFilteredOut_WithMeterFilter() - { - var meterName = Guid.NewGuid().ToString(); - using var meter1 = new Meter(meterName); - using var meter2 = new Meter(meterName); - - using var metricCollector = new MetricCollector(meter1); - - const int IntValue1 = 123459; - meter1.CreateCounter("int_counter1").Add(IntValue1); - - // Measurement is captured - Assert.Equal(IntValue1, metricCollector.GetCounterValue("int_counter1")); - - const double DoubleValue1 = 987; - meter1.CreateHistogram("double_histogram1").Record(DoubleValue1); - - // Measurement is captured - Assert.Equal(DoubleValue1, metricCollector.GetHistogramValue("double_histogram1")); - - const int IntValue2 = 91111; - meter2.CreateCounter("int_counter2").Add(IntValue2); - - // Measurement is filtered out - Assert.Null(metricCollector.GetCounterValue("int_counter2")); - - const double DoubleValue2 = 333.7777; - meter2.CreateHistogram("double_histogram2").Record(DoubleValue2); - - // Measurement is filtered out - Assert.Null(metricCollector.GetCounterValue("double_histogram2")); - } - - [Fact] - public void GetCounterValues_ReturnsMeteringValuesHolder() - { - const long TestValue = 111; using var meter = new Meter(Guid.NewGuid().ToString()); + var counter = meter.CreateCounter("Counter"); - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - - using var metricCollector = new MetricCollector(meter); - - counter.Add(TestValue); - - var meteringHolder = metricCollector.GetCounterValues(counter.Name); - - Assert.NotNull(meteringHolder); - Assert.Equal(TestValue, meteringHolder.GetValue()); - Assert.Null(metricCollector.GetCounterValues(counter.Name)); - Assert.Null(metricCollector.GetCounterValues(counter.Name)); - Assert.Null(metricCollector.GetCounterValues(counter.Name)); - Assert.Null(metricCollector.GetCounterValues(counter.Name)); - Assert.Null(metricCollector.GetCounterValues(counter.Name)); - Assert.Null(metricCollector.GetCounterValues(counter.Name)); + Assert.Throws(() => new MetricCollector(meter, "Counter")); + Assert.Throws(() => new MetricCollector(null, meter.Name, "Counter")); } [Fact] - public void GetHistogramValues_ReturnsMeteringValuesHolder() + public static void Constructor_Meter() { - const int TestValue = 271; - using var meter = new Meter(Guid.NewGuid().ToString()); - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); + const string CounterName = "MyCounter"; - histogram.Record(TestValue); + var now = DateTimeOffset.Now; - var meteringHolder = metricCollector.GetHistogramValues(histogram.Name); - - Assert.NotNull(meteringHolder); - Assert.Equal(TestValue, meteringHolder.GetValue()); - Assert.Null(metricCollector.GetHistogramValues(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValues(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValues(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValues(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValues(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValues(histogram.Name)); - } - - [Fact] - public void GetObservableCounterValues_ReturnsMeteringValuesHolder() - { - const byte TestValue = 255; + var timeProvider = new FakeTimeProvider(now); using var meter = new Meter(Guid.NewGuid().ToString()); - var observableCounter = meter.CreateObservableCounter(Guid.NewGuid().ToString(), () => TestValue); - using var metricCollector = new MetricCollector(meter); - - metricCollector.CollectObservableInstruments(); - var meteringHolder = metricCollector.GetObservableCounterValues(observableCounter.Name); - - Assert.NotNull(meteringHolder); - Assert.Equal(TestValue, meteringHolder.GetValue()); - Assert.Null(metricCollector.GetObservableCounterValues(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValues(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValues(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValues(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValues(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValues(observableCounter.Name)); + using var collector = new MetricCollector(meter, CounterName, timeProvider); + + Assert.Null(collector.Instrument); + Assert.Empty(collector.GetMeasurementSnapshot()); + Assert.Null(collector.LastMeasurement); + + var counter = meter.CreateCounter(CounterName); + counter.Add(3); + + // verify the update was recorded + Assert.Equal(counter, collector.Instrument); + Assert.NotNull(collector.LastMeasurement); + + // verify measurement info is correct + Assert.Single(collector.GetMeasurementSnapshot()); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(3, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(now, collector.LastMeasurement.Timestamp); + + timeProvider.Advance(TimeSpan.FromSeconds(1)); + counter.Add(2); + + // verify measurement info is correct + Assert.Equal(2, collector.GetMeasurementSnapshot().Count); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(2, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(timeProvider.GetUtcNow(), collector.LastMeasurement.Timestamp); + + collector.Clear(); + Assert.Equal(counter, collector.Instrument); + Assert.Empty(collector.GetMeasurementSnapshot()); + Assert.Null(collector.LastMeasurement); + Assert.Null(collector.LastMeasurement); } [Fact] - public void GetUpDownCounterValues_ReturnsMeteringValuesHolder() + public static void Constructor_Instrument() { - const short TestValue = 19999; - using var meter = new Meter(Guid.NewGuid().ToString()); - var upDownCounter = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - upDownCounter.Add(TestValue); - var meteringHolder = metricCollector.GetUpDownCounterValues(upDownCounter.Name); - - Assert.NotNull(meteringHolder); - Assert.Equal(TestValue, meteringHolder.GetValue()); - Assert.Null(metricCollector.GetUpDownCounterValues(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValues(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValues(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValues(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValues(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValues(upDownCounter.Name)); - } + const string CounterName = "MyCounter"; - [Fact] - public void GetObservableGaugeValues_ReturnsMeteringValuesHolder() - { - const long TestValue = 11_225_599L; - using var meter = new Meter(Guid.NewGuid().ToString()); - var observableGauge = meter.CreateObservableGauge(Guid.NewGuid().ToString(), () => TestValue); - using var metricCollector = new MetricCollector(meter); - - metricCollector.CollectObservableInstruments(); - var meteringHolder = metricCollector.GetObservableGaugeValues(observableGauge.Name); - - Assert.NotNull(meteringHolder); - Assert.Equal(TestValue, meteringHolder.GetValue()); - Assert.Null(metricCollector.GetObservableGaugeValues(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValues(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValues(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValues(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValues(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValues(observableGauge.Name)); - } + var now = DateTimeOffset.Now; - [Fact] - public void GetObservableUpDownCounterValues_ReturnsMeteringValuesHolder() - { - const int TestValue = int.MaxValue; + var timeProvider = new FakeTimeProvider(now); using var meter = new Meter(Guid.NewGuid().ToString()); - var observableUpDownCounter = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), () => TestValue); - using var metricCollector = new MetricCollector(meter); - - metricCollector.CollectObservableInstruments(); - var meteringHolder = metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter.Name); - - Assert.NotNull(meteringHolder); - Assert.Equal(TestValue, meteringHolder.GetValue()); - Assert.Null(metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValues(observableUpDownCounter.Name)); + var counter = meter.CreateCounter(CounterName); + using var collector = new MetricCollector(counter, timeProvider); + + Assert.Empty(collector.GetMeasurementSnapshot()); + Assert.Null(collector.LastMeasurement); + + counter.Add(3); + + // verify the update was recorded + Assert.Equal(counter, collector.Instrument); + Assert.NotNull(collector.LastMeasurement); + + // verify measurement info is correct + Assert.Single(collector.GetMeasurementSnapshot()); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(3, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(now, collector.LastMeasurement.Timestamp); + + timeProvider.Advance(TimeSpan.FromSeconds(1)); + counter.Add(2); + + // verify measurement info is correct + Assert.Equal(2, collector.GetMeasurementSnapshot().Count); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(2, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(timeProvider.GetUtcNow(), collector.LastMeasurement.Timestamp); + + collector.Clear(); + Assert.Equal(counter, collector.Instrument); + Assert.Empty(collector.GetMeasurementSnapshot()); + Assert.Null(collector.LastMeasurement); + Assert.Null(collector.LastMeasurement); } [Fact] - public void GetCounterValue_ReturnsValue() + public static void Constructor_Scope() { - const long TestValue = 111; - using var meter = new Meter(Guid.NewGuid().ToString()); - - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - - using var metricCollector = new MetricCollector(meter); - - counter.Add(TestValue); - - Assert.Equal(TestValue, metricCollector.GetCounterValue(counter.Name)); - Assert.Null(metricCollector.GetCounterValue(counter.Name)); - Assert.Null(metricCollector.GetCounterValue(counter.Name)); - Assert.Null(metricCollector.GetCounterValue(counter.Name)); - Assert.Null(metricCollector.GetCounterValue(counter.Name)); - Assert.Null(metricCollector.GetCounterValue(counter.Name)); - Assert.Null(metricCollector.GetCounterValue(counter.Name)); + const string CounterName = "MyCounter"; + + var now = DateTimeOffset.Now; + + var timeProvider = new FakeTimeProvider(now); + var scope = new object(); + using var meter = new Meter(Guid.NewGuid().ToString(), null, null, scope); + var counter = meter.CreateCounter(CounterName); + using var collector = new MetricCollector(scope, meter.Name, counter.Name, timeProvider); + using var collector2 = new MetricCollector(new object(), meter.Name, counter.Name, timeProvider); + + Assert.Empty(collector.GetMeasurementSnapshot()); + Assert.Null(collector.LastMeasurement); + + counter.Add(3); + + // verify the update was recorded + Assert.Equal(counter, collector.Instrument); + Assert.NotNull(collector.LastMeasurement); + + // verify measurement info is correct + Assert.Single(collector.GetMeasurementSnapshot()); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(3, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(now, collector.LastMeasurement.Timestamp); + + timeProvider.Advance(TimeSpan.FromSeconds(1)); + counter.Add(2); + + // verify measurement info is correct + Assert.Equal(2, collector.GetMeasurementSnapshot().Count); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(2, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(timeProvider.GetUtcNow(), collector.LastMeasurement.Timestamp); + + collector.Clear(); + Assert.Equal(counter, collector.Instrument); + Assert.Empty(collector.GetMeasurementSnapshot()); + Assert.Null(collector.LastMeasurement); + Assert.Null(collector.LastMeasurement); + + Assert.Null(collector2.LastMeasurement); } [Fact] - public void GetHistogramValue_ReturnsValue() + public static void Constructor_ObservableInstrument() { - const int TestValue = 271; - using var meter = new Meter(Guid.NewGuid().ToString()); - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - histogram.Record(TestValue); - - Assert.Equal(TestValue, metricCollector.GetHistogramValue(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValue(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValue(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValue(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValue(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValue(histogram.Name)); - Assert.Null(metricCollector.GetHistogramValue(histogram.Name)); - } + const string CounterName = "MyCounter"; - [Fact] - public void GetObservableCounterValue_ReturnsValue() - { - const byte TestValue = 255; - using var meter = new Meter(Guid.NewGuid().ToString()); - var observableCounter = meter.CreateObservableCounter(Guid.NewGuid().ToString(), () => TestValue); - using var metricCollector = new MetricCollector(meter); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(TestValue, metricCollector.GetObservableCounterValue(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValue(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValue(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValue(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValue(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValue(observableCounter.Name)); - Assert.Null(metricCollector.GetObservableCounterValue(observableCounter.Name)); - } + var now = DateTimeOffset.Now; - [Fact] - public void GetUpDownCounterValue_ReturnsValue() - { - const short TestValue = 19999; + var timeProvider = new FakeTimeProvider(now); using var meter = new Meter(Guid.NewGuid().ToString()); - var upDownCounter = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - upDownCounter.Add(TestValue); - - Assert.Equal(TestValue, metricCollector.GetUpDownCounterValue(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValue(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValue(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValue(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValue(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValue(upDownCounter.Name)); - Assert.Null(metricCollector.GetUpDownCounterValue(upDownCounter.Name)); + int observationCount = 0; + + var counter = meter.CreateObservableCounter(CounterName, () => + { + if (observationCount == 0) + { + observationCount++; + return 3; + } + else + { + return 2; + } + }); + + using var collector = new MetricCollector(counter, timeProvider); + + Assert.Empty(collector.GetMeasurementSnapshot()); + Assert.Null(collector.LastMeasurement); + + collector.RecordObservableInstruments(); + + // verify the update was recorded + Assert.Equal(counter, collector.Instrument); + Assert.NotNull(collector.LastMeasurement); + + // verify measurement info is correct + Assert.Single(collector.GetMeasurementSnapshot()); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(3, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(now, collector.LastMeasurement.Timestamp); + + timeProvider.Advance(TimeSpan.FromSeconds(1)); + collector.RecordObservableInstruments(); + + // verify measurement info is correct + Assert.Equal(2, collector.GetMeasurementSnapshot().Count); + Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement); + Assert.Equal(2, collector.LastMeasurement.Value); + Assert.Empty(collector.LastMeasurement.Tags); + Assert.Equal(timeProvider.GetUtcNow(), collector.LastMeasurement.Timestamp); } [Fact] - public void GetObservableGaugeValue_ReturnsValue() + public static async Task Wait() { - const long TestValue = 11_225_599L; - using var meter = new Meter(Guid.NewGuid().ToString()); - var observableGauge = meter.CreateObservableGauge(Guid.NewGuid().ToString(), () => TestValue); - using var metricCollector = new MetricCollector(meter); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(TestValue, metricCollector.GetObservableGaugeValue(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValue(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValue(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValue(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValue(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValue(observableGauge.Name)); - Assert.Null(metricCollector.GetObservableGaugeValue(observableGauge.Name)); - } + const string CounterName = "MyCounter"; - [Fact] - public void GetObservableUpDownCounterValue_ReturnsValue() - { - const int TestValue = int.MaxValue; - using var meter = new Meter(Guid.NewGuid().ToString()); - var observableUpDownCounter = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), () => TestValue); - using var metricCollector = new MetricCollector(meter); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(TestValue, metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)); - Assert.Null(metricCollector.GetObservableUpDownCounterValue(observableUpDownCounter.Name)); - } - - [Fact] - public void Clear_RemovesAllMeasurements() - { - var meterName = Guid.NewGuid().ToString(); - - using var meter = new Meter(meterName); - using var metricCollector = new MetricCollector(new[] { meterName }); - - const int CounterValue = 1; - meter.CreateCounter("int_counter").Add(CounterValue); - - const int HistogramValue = 2; - meter.CreateHistogram("int_histogram").Record(HistogramValue); - - const long UpDownCounterValue = -999L; - meter.CreateUpDownCounter("long_updownCounter").Add(UpDownCounterValue); - - const short ObservableCounterValue = short.MaxValue; - meter.CreateObservableCounter("short_observable_counter", () => ObservableCounterValue); - - const decimal ObservableGaugeValue = decimal.MinValue; - meter.CreateObservableGauge("decimal_observable_gauge", () => ObservableGaugeValue); - - const double ObservableUpdownCouterValue = double.MaxValue; - meter.CreateObservableUpDownCounter("double_observable_updownCounter", () => ObservableUpdownCouterValue); - - Assert.Equal(CounterValue, metricCollector.GetCounterValue("int_counter")!.Value); - Assert.Equal(HistogramValue, metricCollector.GetHistogramValue("int_histogram")!.Value); - Assert.Equal(UpDownCounterValue, metricCollector.GetUpDownCounterValue("long_updownCounter")!.Value); - - metricCollector.CollectObservableInstruments(); - - Assert.Equal(ObservableCounterValue, metricCollector.GetObservableCounterValue("short_observable_counter")!.Value); - Assert.Equal(ObservableGaugeValue, metricCollector.GetObservableGaugeValue("decimal_observable_gauge")!.Value); - Assert.Equal(ObservableUpdownCouterValue, metricCollector.GetObservableUpDownCounterValue("double_observable_updownCounter")!.Value); - - metricCollector.Clear(); - - Assert.Null(metricCollector.GetCounterValue("int_counter")); - Assert.Null(metricCollector.GetHistogramValue("int_histogram")); - Assert.Null(metricCollector.GetUpDownCounterValue("long_updownCounter")); - Assert.Null(metricCollector.GetObservableCounterValue("short_observable_counter")); - Assert.Null(metricCollector.GetObservableGaugeValue("decimal_observable_gauge")); - Assert.Null(metricCollector.GetObservableUpDownCounterValue("double_observable_updownCounter")); - } + var now = DateTimeOffset.Now; - [Fact] - public void CollectObservableInstruments_RecordsObservableMetrics() - { + var timeProvider = new FakeTimeProvider(now); using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - const int CounterValue = 47_382_492; - const float UpDownCounterValue = 921.342f; - const decimal GaugeValue = 12340m; - - meter.CreateObservableCounter("ObservableCounter", () => CounterValue); - meter.CreateObservableGauge("ObservableGauge", () => GaugeValue); - meter.CreateObservableUpDownCounter("ObservableUpDownCounter", () => UpDownCounterValue); - - // Observable instruments are not recorded - Assert.Null(metricCollector.GetObservableCounterValue("ObservableCounter")); - Assert.Null(metricCollector.GetObservableGaugeValue("ObservableGauge")); - Assert.Null(metricCollector.GetObservableUpDownCounterValue("ObservableUpDownCounter")); - - // Force recording of observable instruments - metricCollector.CollectObservableInstruments(); - - Assert.Equal(CounterValue, metricCollector.GetObservableCounterValue("ObservableCounter")); - Assert.Equal(GaugeValue, metricCollector.GetObservableGaugeValue("ObservableGauge")); - Assert.Equal(UpDownCounterValue, metricCollector.GetObservableUpDownCounterValue("ObservableUpDownCounter")); - } - - [Fact] - public void GetXxxValue_ThrowsWhenInvalidValueTypeIsUsed() - { - using var metricCollector = new MetricCollector(); - - var ex = Assert.Throws(() => metricCollector.GetCounterValue(string.Empty)); - Assert.Equal($"The type {typeof(ushort).FullName} is not supported as a type for a metric measurement value", ex.Message); - - var ex1 = Assert.Throws(() => metricCollector.GetHistogramValue(string.Empty)); - Assert.Equal($"The type {typeof(ulong).FullName} is not supported as a type for a metric measurement value", ex1.Message); - - var ex2 = Assert.Throws(() => metricCollector.GetUpDownCounterValue(string.Empty)); - Assert.Equal($"The type {typeof(sbyte).FullName} is not supported as a type for a metric measurement value", ex2.Message); - - var ex3 = Assert.Throws(() => metricCollector.GetObservableCounterValue(string.Empty)); - Assert.Equal($"The type {typeof(uint).FullName} is not supported as a type for a metric measurement value", ex3.Message); - - var ex4 = Assert.Throws(() => metricCollector.GetObservableGaugeValue(string.Empty)); - Assert.Equal($"The type {typeof(uint).FullName} is not supported as a type for a metric measurement value", ex4.Message); - - var ex5 = Assert.Throws(() => metricCollector.GetObservableUpDownCounterValue(string.Empty)); - Assert.Equal($"The type {typeof(uint).FullName} is not supported as a type for a metric measurement value", ex5.Message); - } - - [Fact] - public void GetXxxValues_ThrowsWhenInvalidValueTypeIsUsed() - { - using var metricCollector = new MetricCollector(); - - var ex = Assert.Throws(() => metricCollector.GetCounterValues(string.Empty)); - Assert.Equal($"The type {typeof(ushort).FullName} is not supported as a type for a metric measurement value", ex.Message); + using var collector = new MetricCollector(meter, CounterName, timeProvider); + var counter = meter.CreateCounter(CounterName); - var ex1 = Assert.Throws(() => metricCollector.GetHistogramValues(string.Empty)); - Assert.Equal($"The type {typeof(ulong).FullName} is not supported as a type for a metric measurement value", ex1.Message); + await Assert.ThrowsAsync(async () => await collector.WaitForMeasurementsAsync(-1)); + await Assert.ThrowsAsync(async () => await collector.WaitForMeasurementsAsync(0)); - var ex2 = Assert.Throws(() => metricCollector.GetUpDownCounterValues(string.Empty)); - Assert.Equal($"The type {typeof(sbyte).FullName} is not supported as a type for a metric measurement value", ex2.Message); + var wait = collector.WaitForMeasurementsAsync(2); + Assert.False(wait.IsCompleted); - var ex3 = Assert.Throws(() => metricCollector.GetObservableCounterValues(string.Empty)); - Assert.Equal($"The type {typeof(uint).FullName} is not supported as a type for a metric measurement value", ex3.Message); + counter.Add(1); + Assert.False(wait.IsCompleted); - var ex4 = Assert.Throws(() => metricCollector.GetObservableGaugeValues(string.Empty)); - Assert.Equal($"The type {typeof(ulong).FullName} is not supported as a type for a metric measurement value", ex4.Message); + counter.Add(1); + Assert.True(wait.IsCompleted); + Assert.False(wait.IsFaulted); - var ex5 = Assert.Throws(() => metricCollector.GetObservableUpDownCounterValues(string.Empty)); - Assert.Equal($"The type {typeof(ushort).FullName} is not supported as a type for a metric measurement value", ex5.Message); + collector.Clear(); + counter.Add(1); + wait = collector.WaitForMeasurementsAsync(1); + Assert.True(wait.IsCompleted); + Assert.False(wait.IsFaulted); } [Fact] - public void GenericMetricCollector_CapturesFilteredMetering() + public static async Task WaitWithTimeout() { - const int TestValue = 10; - using var metricCollector = new MetricCollector(); - using var meter = new Meter(typeof(MetricCollectorTests).FullName!); - using var meterToIgnore = new Meter(Guid.NewGuid().ToString()); + const string CounterName = "MyCounter"; - var counter1 = meter.CreateCounter(Guid.NewGuid().ToString()); - var counter2 = meterToIgnore.CreateCounter(Guid.NewGuid().ToString()); - - Assert.NotNull(metricCollector.GetCounterValues(counter1.Name)); - Assert.Null(metricCollector.GetCounterValues(counter2.Name)); - - counter1.Add(TestValue); - counter2.Add(TestValue); - - Assert.NotNull(metricCollector.GetCounterValue(counter1.Name)); - Assert.Null(metricCollector.GetCounterValues(counter2.Name)); - } + var now = DateTimeOffset.Now; - [Fact] - public void GetAllCounters_ReturnsAllCounters() - { - const long TestValue = 111; + var timeProvider = new FakeTimeProvider(now); using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - var counter1 = meter.CreateCounter(Guid.NewGuid().ToString()); - var counter2 = meter.CreateCounter(Guid.NewGuid().ToString()); - var counter3 = meter.CreateCounter(Guid.NewGuid().ToString()); - - counter1.Add(TestValue); - counter2.Add(TestValue); - counter3.Add(TestValue); - - Assert.Equal(3, metricCollector.GetAllCounters()!.Count); + using var collector = new MetricCollector(meter, CounterName, timeProvider); + var counter = meter.CreateCounter(CounterName); + + await Assert.ThrowsAsync(async () => await collector.WaitForMeasurementsAsync(-1)); + await Assert.ThrowsAsync(async () => await collector.WaitForMeasurementsAsync(0)); + + var wait = collector.WaitForMeasurementsAsync(2, TimeSpan.FromSeconds(1)); + Assert.False(wait.IsCompleted); + + counter.Add(1); + Assert.False(wait.IsCompleted); + +#if false +// TODO: This is broken for netcoreapp3.1 and net6.0, but works for net462 and net8.0. + timeProvider.Advance(TimeSpan.FromSeconds(1)); + Assert.True(wait.IsCompleted); + Assert.True(wait.IsFaulted); +#endif + + collector.Clear(); + counter.Add(1); + wait = collector.WaitForMeasurementsAsync(1, TimeSpan.FromSeconds(1)); + Assert.True(wait.IsCompleted); + Assert.False(wait.IsFaulted); } [Fact] - public void GetAllUpDownCounters_ReturnsAllCounters() + public static async Task Dispose() { - const long TestValue = 111; - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - var counter1 = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - var counter2 = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - var counter3 = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); + const string CounterName = "MyCounter"; - counter1.Add(TestValue); - counter2.Add(TestValue); - counter3.Add(TestValue); - - Assert.Equal(3, metricCollector.GetAllUpDownCounters()!.Count); - } + var now = DateTimeOffset.Now; - [Fact] - public void GetAllHistograms_ReturnsAllHistograms() - { - const long TestValue = 111; + var timeProvider = new FakeTimeProvider(now); using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); + var collector = new MetricCollector(meter, CounterName, timeProvider); + var counter = meter.CreateCounter(CounterName); - var counter1 = meter.CreateHistogram(Guid.NewGuid().ToString()); - var counter2 = meter.CreateHistogram(Guid.NewGuid().ToString()); - var counter3 = meter.CreateHistogram(Guid.NewGuid().ToString()); + var wait = collector.WaitForMeasurementsAsync(2, TimeSpan.FromSeconds(1)); - counter1.Record(TestValue); - counter2.Record(TestValue); - counter3.Record(TestValue); + collector.Dispose(); + collector.Dispose(); // second call is a nop - Assert.Equal(3, metricCollector.GetAllHistograms()!.Count); - } + Assert.Throws(() => collector.GetMeasurementSnapshot()); + Assert.Throws(() => collector.Clear()); + Assert.Throws(() => collector.LastMeasurement); + Assert.Throws(() => collector.RecordObservableInstruments()); - [Fact] - public void GetAllGauges_ReturnsAllGauges() - { - const long TestValue = 111; - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - _ = meter.CreateObservableGauge(Guid.NewGuid().ToString(), () => TestValue); - _ = meter.CreateObservableGauge(Guid.NewGuid().ToString(), () => TestValue); - _ = meter.CreateObservableGauge(Guid.NewGuid().ToString(), () => TestValue); + await Assert.ThrowsAsync(async () => await collector.WaitForMeasurementsAsync(1)); + await Assert.ThrowsAsync(async () => await collector.WaitForMeasurementsAsync(1, TimeSpan.FromSeconds(1))); - // Force recording of observable instruments - metricCollector.CollectObservableInstruments(); +#if false +// TODO: This is broken for netcoreapp3.1 and net6.0, but works for net462 and net8.0. + Assert.True(wait.IsCompleted); + Assert.True(wait.IsFaulted); +#endif - Assert.Equal(3, metricCollector.GetAllObservableGauges()!.Count); + collector = new MetricCollector(meter, CounterName, timeProvider); + collector.Dispose(); + counter.Add(1); + Assert.Throws(() => collector.Clear()); } [Fact] - public void GetAllObservableCounters_ReturnsAllObservableCounters() + public static void Snapshot() { - const long TestValue = 111; - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); + const string CounterName = "MyCounter"; - _ = meter.CreateObservableCounter(Guid.NewGuid().ToString(), () => TestValue); - _ = meter.CreateObservableCounter(Guid.NewGuid().ToString(), () => TestValue); - _ = meter.CreateObservableCounter(Guid.NewGuid().ToString(), () => TestValue); + var now = DateTimeOffset.Now; - // Force recording of observable instruments - metricCollector.CollectObservableInstruments(); - - Assert.Equal(3, metricCollector.GetAllObservableCounters()!.Count); - } - - [Fact] - public void GetAllObservableUpDownCounters_ReturnsAllObservableUpDownCounters() - { - const long TestValue = 111; + var timeProvider = new FakeTimeProvider(now); using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); + using var collector = new MetricCollector(meter, CounterName, timeProvider); + var counter = meter.CreateCounter(CounterName); - _ = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), () => TestValue); - _ = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), () => TestValue); - _ = meter.CreateObservableUpDownCounter(Guid.NewGuid().ToString(), () => TestValue); + counter.Add(1); + counter.Add(2); + counter.Add(3); - // Force recording of observable instruments - metricCollector.CollectObservableInstruments(); + var snap = collector.GetMeasurementSnapshot(); + Assert.Equal(3, snap.Count); + Assert.Equal(3, collector.GetMeasurementSnapshot().Count); - Assert.Equal(3, metricCollector.GetAllObservableUpDownCounters()!.Count); - } + Assert.Equal(1, snap[0].Value); + Assert.Empty(snap[0].Tags); + Assert.Equal(now, snap[0].Timestamp); - [Fact] - public void GetAllCounters_WithoutUnsupportedT_Throws() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); + Assert.Equal(2, snap[1].Value); + Assert.Empty(snap[1].Tags); + Assert.Equal(now, snap[1].Timestamp); + + Assert.Equal(3, snap[2].Value); + Assert.Empty(snap[2].Tags); + Assert.Equal(now, snap[2].Timestamp); - Assert.Throws(() => metricCollector.GetAllCounters()); + snap = collector.GetMeasurementSnapshot(true); + Assert.Equal(3, snap.Count); + Assert.Equal(0, collector.GetMeasurementSnapshot().Count); } } diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValueTests.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValueTests.cs deleted file mode 100644 index 9a8c9733877..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValueTests.cs +++ /dev/null @@ -1,20 +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 Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public class MetricValueTests -{ - [Fact] - public void Add_ThrowsWhenWrongValueTypeIsUsed() - { - var metricValue = new MetricValue('1', Array.Empty>(), DateTimeOffset.Now); - - var ex = Assert.Throws(() => metricValue.Add('d')); - Assert.Equal($"The type {typeof(char).FullName} is not supported as a metering measurement value type.", ex.Message); - } -} diff --git a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValuesHolderTests.cs b/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValuesHolderTests.cs deleted file mode 100644 index b43e05ab946..00000000000 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Testing.Tests/Metering/MetricValuesHolderTests.cs +++ /dev/null @@ -1,247 +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 Microsoft.Extensions.Telemetry.Testing.Metering.Internal; -using Microsoft.Extensions.Time.Testing; -using Xunit; - -namespace Microsoft.Extensions.Telemetry.Testing.Metering.Test; - -public class MetricValuesHolderTests -{ - [Fact] - public void MetricName_MatchesInstrumentName() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - var counterValuesHolder = metricCollector.GetCounterValues(counter.Name); - - Assert.NotNull(counterValuesHolder); - Assert.Equal(counterValuesHolder.Instrument.Name, counter.Name); - - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - var histgramValuesHolder = metricCollector.GetHistogramValues(histogram.Name); - - Assert.NotNull(histgramValuesHolder); - Assert.Equal(histgramValuesHolder.Instrument.Name, histogram.Name); - - var updownCounter = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - var updownCounterValuesHolder = metricCollector.GetUpDownCounterValues(updownCounter.Name); - - Assert.NotNull(updownCounterValuesHolder); - Assert.Equal(updownCounterValuesHolder.Instrument.Name, updownCounter.Name); - } - - [Fact] - public void Count() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - var counterValuesHolder = metricCollector.GetCounterValues(counter.Name); - counter.Add(1); - Assert.Equal(1, counterValuesHolder!.Count); - - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - var histgramValuesHolder = metricCollector.GetHistogramValues(histogram.Name); - histogram.Record(1); - histogram.Record(1); - Assert.Equal(2, histgramValuesHolder!.Count); - - var updownCounter = meter.CreateUpDownCounter(Guid.NewGuid().ToString()); - var updownCounterValuesHolder = metricCollector.GetUpDownCounterValues(updownCounter.Name); - Assert.Equal(0, updownCounterValuesHolder!.Count); - } - - [Fact] - public void LastWrittenValue_ReturnsTheLatestMeasurementValue() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - var counterValuesHolder = metricCollector.GetCounterValues(counter.Name)!; - - Assert.Null(counterValuesHolder.LatestWrittenValue); - - const int CounterValue = 1; - counter.Add(CounterValue); - - Assert.Equal(CounterValue, counterValuesHolder.LatestWrittenValue!.Value); - - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - var histogramValuesHolder = metricCollector.GetHistogramValues(histogram.Name)!; - - Assert.Null(histogramValuesHolder.LatestWrittenValue); - - var testValues = new[] { 20, 40, 60, 100, 200, 1000, 5000 }; - - foreach (var testValue in testValues) - { - histogram.Record(testValue); - - Assert.Equal(testValue, histogramValuesHolder.LatestWrittenValue!.Value); - } - } - - [Fact] - public void LastWritten_ReturnsTheMetricValue() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - using var metricCollector = new MetricCollector(meter); - - var counter = meter.CreateCounter(Guid.NewGuid().ToString()); - var counterValuesHolder = metricCollector.GetCounterValues(counter.Name)!; - - Assert.Null(counterValuesHolder.LatestWritten); - - const int CounterValue = 1; - counter.Add(CounterValue); - - Assert.Equal(CounterValue, counterValuesHolder.LatestWritten!.Value); - - var histogram = meter.CreateHistogram(Guid.NewGuid().ToString()); - var histogramValuesHolder = metricCollector.GetHistogramValues(histogram.Name)!; - - Assert.Null(histogramValuesHolder.LatestWritten); - - const int Value1 = 111; - histogram.Record(Value1); - - var metricValue1 = histogramValuesHolder.LatestWritten; - - Assert.NotNull(metricValue1); - Assert.Equal(Value1, metricValue1.Value); - Assert.Empty(metricValue1.Tags); - - const int Value2 = 2222; - var dimension21 = new KeyValuePair(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); - histogram.Record(Value2, dimension21); - - var metricValue2 = histogramValuesHolder.LatestWritten; - - Assert.NotNull(metricValue2); - Assert.Equal(Value2, metricValue2.Value); - Assert.Equal(new[] { new KeyValuePair(dimension21.Key, dimension21.Value) }, metricValue2.Tags); - - const int Value3 = 9991; - var dimension31 = new KeyValuePair(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); - var dimension32 = new KeyValuePair(Guid.NewGuid().ToString(), 1); - var dimension33 = new KeyValuePair(Guid.NewGuid().ToString(), 'c'); - var dimension34 = new KeyValuePair(Guid.NewGuid().ToString(), null); - histogram.Record(Value3, dimension31, dimension32, dimension33, dimension34); - - var metricValue3 = histogramValuesHolder.LatestWritten; - - var expectedTags = new[] - { - new KeyValuePair(dimension31.Key, dimension31.Value), - new KeyValuePair(dimension32.Key, dimension32.Value), - new KeyValuePair(dimension33.Key, dimension33.Value), - new KeyValuePair(dimension34.Key, dimension34.Value), - }; - Array.Sort(expectedTags, (x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.Key, y.Key)); - - Assert.NotNull(metricValue3); - Assert.Equal(Value3, metricValue3.Value); - Assert.Equal(expectedTags, metricValue3.Tags); - } - - [Fact] - public void ReceiveValue_ThrowsWhenInvalidDimensionValue() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - var instrument = meter.CreateCounter(Guid.NewGuid().ToString()); - var metricValuesHolder = new MetricValuesHolder(TimeProvider.System, AggregationType.Save, instrument); - - var ex = Assert.Throws(() => metricValuesHolder.ReceiveValue(int.MaxValue, new[] { new KeyValuePair("Dimension1", new object()) })); - Assert.Equal($"The type {typeof(object).FullName} is not supported as a dimension value type.", ex.Message); - - var ex1 = Assert.Throws(() => metricValuesHolder.ReceiveValue(int.MinValue, new[] { new KeyValuePair("Dimension2", new[] { new object() }) })); - Assert.Equal($"The type {typeof(object[]).FullName} is not supported as a dimension value type.", ex1.Message); - } - - [Fact] - public void GetValue_ReturnsCapturedMeasurementValue() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - var instrument = meter.CreateCounter(Guid.NewGuid().ToString()); - var metricValuesHolder = new MetricValuesHolder(System.TimeProvider.System, AggregationType.Save, instrument); - - const int Value1 = 1; - metricValuesHolder.ReceiveValue(Value1, null); - - Assert.Equal(Value1, metricValuesHolder.GetValue()); - - const int Value2 = 20; - metricValuesHolder.ReceiveValue(Value2, null); - - Assert.Equal(Value2, metricValuesHolder.GetValue()); - } - - [Fact] - public void ReceiveValue_TimestampIsRecorded() - { - var recordTime = DateTimeOffset.UtcNow.AddDays(-1); - var timeProvider = new FakeTimeProvider(recordTime); - using var meter = new Meter(Guid.NewGuid().ToString()); - var instrument = meter.CreateCounter(Guid.NewGuid().ToString()); - var metricValuesHolder = new MetricValuesHolder(timeProvider, AggregationType.Save, instrument); - - metricValuesHolder.ReceiveValue(50, null); - - Assert.NotNull(metricValuesHolder.LatestWrittenValue); - Assert.Equal(recordTime, metricValuesHolder.LatestWritten!.Timestamp); - } - - [Fact] - public void GetDimension_RetursDimensionValue() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - var instrument = meter.CreateCounter(Guid.NewGuid().ToString()); - var metricValuesHolder = new MetricValuesHolder(TimeProvider.System, AggregationType.Save, instrument); - - var intDimension = "int_dimension"; - int intVal = 11111; - var stringDimension = "string_dimension"; - var stringValue = Guid.NewGuid().ToString(); - var nullDimension = "null_dimension"; - var doubleDimension = "double_dimension"; - var doubleVal = 78.78d; - - var dimensions = new[] - { - new KeyValuePair(intDimension, intVal), - new KeyValuePair(stringDimension, stringValue), - new KeyValuePair(nullDimension, null), - new KeyValuePair(doubleDimension, doubleVal) - }; - - metricValuesHolder.ReceiveValue(50, dimensions); - - Assert.NotNull(metricValuesHolder.LatestWritten); - Assert.Equal(intVal, metricValuesHolder.LatestWritten.GetDimension(intDimension)); - Assert.Equal(stringValue, metricValuesHolder.LatestWritten.GetDimension(stringDimension)); - Assert.Equal(doubleVal, metricValuesHolder.LatestWritten.GetDimension(doubleDimension)); - Assert.Null(metricValuesHolder.LatestWritten.GetDimension(nullDimension)); - Assert.Null(metricValuesHolder.LatestWritten.GetDimension("invalid_dimension_name")); - } - - [Fact] - public void ReceiveValue_ThrowsWhenInvalidAggregationTypeIsUsed() - { - using var meter = new Meter(Guid.NewGuid().ToString()); - var instrument = meter.CreateCounter(Guid.NewGuid().ToString()); - AggregationType invalidAggregationType = (AggregationType)111; - var metricValuesHolder = new MetricValuesHolder(System.TimeProvider.System, invalidAggregationType, instrument); - - var ex = Assert.Throws(() => metricValuesHolder.ReceiveValue(50, new KeyValuePair[0].AsSpan())); - Assert.Equal($"Aggregation type {invalidAggregationType} is not supported.", ex.Message); - } -} 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 index a351335ca10..db518de05b7 100644 --- a/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersListenerTest.cs +++ b/test/Libraries/Microsoft.Extensions.Telemetry.Tests/Metering.Collectors.EventCounters/EventCountersListenerTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Metrics; using System.Diagnostics.Tracing; #if !NETFRAMEWORK using System.Threading; @@ -45,11 +46,35 @@ public void EventCountersListener_WhenNullOptions_Throws() 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 metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter, _logger); @@ -62,18 +87,14 @@ public void EventCountersListener_Ignores_NonStructuredEvents() Increment = 1 }); - var counters = metricCollector.GetAllCounters(); - var histograms = metricCollector.GetAllCounters(); - - Assert.Empty(counters!); - Assert.Empty(histograms!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_UnknownCounterTypes() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -84,18 +105,13 @@ public void EventCountersListener_Ignores_UnknownCounterTypes() payload = new { CounterType = _fixture.Create(), Name = _counterName, Increment = 1 } }); - var counters = metricCollector.GetAllCounters(); - var histograms = metricCollector.GetAllCounters(); - - Assert.Empty(counters!); - Assert.Empty(histograms!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_ValidateEventCounterInterval_SetCorrectly() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); var intervalInSeconds = 2; @@ -124,7 +140,7 @@ public void EventCountersListener_ValidateEventCounterInterval_SetCorrectly() public void EventCountersListener_OnEventWritten_Ignores_WithNullCounter() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) { @@ -146,15 +162,14 @@ public void EventCountersListener_OnEventWritten_Ignores_WithNullCounter() payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_OnEventWritten_Ignores_WithEmptyCounterName() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -167,15 +182,14 @@ public void EventCountersListener_OnEventWritten_Ignores_WithEmptyCounterName() payload = new { CounterType = "Sum", Name = emptyCounterName, Increment = 1 } }); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_OnEventWritten_Ignores_WithoutEventName() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new TestEventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -187,15 +201,14 @@ public void EventCountersListener_OnEventWritten_Ignores_WithoutEventName() payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_OnEventWritten_Ignores_WithoutPayload() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new TestEventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -203,30 +216,28 @@ public void EventCountersListener_OnEventWritten_Ignores_WithoutPayload() eventSource.Write(_eventSourceName, new EventSourceOptions { Level = EventLevel.LogAlways }); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_OnEventWritten_Ignores_WithoutEventData() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new TestEventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); eventSource.Write(_eventSourceName); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_UnknownEventSources() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All, @@ -234,15 +245,14 @@ public void EventCountersListener_Ignores_UnknownEventSources() counterName: _counterName, eventName: EventName); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_UnknownEvents() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All, @@ -250,64 +260,60 @@ public void EventCountersListener_Ignores_UnknownEvents() counterName: _counterName, eventName: _fixture.Create()); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_UnknownCounters() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); + _options.Value.Counters.Clear(); SendMeanEvent(meter, MeanEventProperties.All); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_EventsWithoutCounterType() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All ^ MeanEventProperties.WithType); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_EventsWithoutName() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All ^ MeanEventProperties.WithName); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_MeanEventsWithoutMean() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All ^ MeanEventProperties.WithMean); - Assert.Empty(metricCollector.GetAllHistograms()!); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_Empty_Counters_Maps() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); var eventSourceName = Guid.NewGuid().ToString(); var options = new EventCountersCollectorOptions(); options.Counters.Add(eventSourceName, new HashSet()); @@ -321,14 +327,14 @@ public void EventCountersListener_Ignores_Empty_Counters_Maps() payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Emits_WhenSumCounterMatches() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -339,7 +345,7 @@ public void EventCountersListener_Emits_WhenSumCounterMatches() payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - var latest = metricCollector.GetCounterValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); } @@ -348,7 +354,7 @@ public void EventCountersListener_Emits_WhenSumCounterMatches() public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoublePositiveInfinity() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -359,14 +365,14 @@ public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoublePos payload = new { CounterType = "Sum", Name = _counterName, Increment = double.PositiveInfinity } }); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoubleNegativeInfinity() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -377,14 +383,14 @@ public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoubleNeg payload = new { CounterType = "Sum", Name = _counterName, Increment = double.NegativeInfinity } }); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoubleNan() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -395,14 +401,14 @@ public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIsDoubleNan payload = new { CounterType = "Sum", Name = _counterName, Increment = double.NaN } }); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIncorrectlyFormatted() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -413,14 +419,14 @@ public void EventCountersListener_Ignores_WhenSumCounterMatches_ValueIncorrectly payload = new { CounterType = "Sum", Name = _counterName, Increment = "?/str." } }); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Emits_WhenSourceIsCreatedAfterListener() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); using var listener = new EventCountersListener(_options, meter); using var eventSource = new EventSource(_eventSourceName); @@ -431,7 +437,7 @@ public void EventCountersListener_Emits_WhenSourceIsCreatedAfterListener() payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - var latest = metricCollector.GetCounterValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); } @@ -440,7 +446,7 @@ public void EventCountersListener_Emits_WhenSourceIsCreatedAfterListener() public void EventCountersListener_Ignores_SumCounterWithoutIncrement() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -451,18 +457,18 @@ public void EventCountersListener_Ignores_SumCounterWithoutIncrement() payload = new { CounterType = "Sum", Name = _counterName } }); - Assert.Empty(metricCollector.GetAllCounters()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Emits_WhenMeanCounterMatches() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); SendMeanEvent(meter, MeanEventProperties.All); - var latest = metricCollector.GetHistogramValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); } @@ -471,7 +477,7 @@ public void EventCountersListener_Emits_WhenMeanCounterMatches() public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedHistogram() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -483,7 +489,7 @@ public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedHist payload = new { CounterType = "Mean", Name = _counterName, Mean = 1 } }); - var latest = metricCollector.GetHistogramValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); Assert.Single(listener.HistogramInstruments); @@ -495,7 +501,7 @@ public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedHist payload = new { CounterType = "Mean", Name = _counterName, Mean = 1 } }); - latest = metricCollector.GetHistogramValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Single(listener.HistogramInstruments); } @@ -504,7 +510,7 @@ public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedHist public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedCounter() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(_options, meter); @@ -516,7 +522,7 @@ public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedCoun payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - var latest = metricCollector.GetCounterValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); Assert.Single(listener.CounterInstruments); @@ -528,7 +534,7 @@ public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedCoun payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - latest = metricCollector.GetCounterValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.Single(listener.CounterInstruments); } @@ -537,30 +543,30 @@ public void EventCountersListener_MultipleEventsForSameMetricShouldUseCachedCoun public void EventCountersListener_Ignores_WhenMeanCounterMatches_MeanValueDoubleNan() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All, _eventSourceName, _counterName, EventName, meanValue: double.NaN); - Assert.Empty(metricCollector.GetAllHistograms()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_WhenMeanCounterMatches_MeanValuePositiveInfinity() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All, _eventSourceName, _counterName, EventName, meanValue: double.PositiveInfinity); - Assert.Empty(metricCollector.GetAllHistograms()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_Ignores_WhenMeanCounterMatches_MeanValueNegativeInfinity() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); SendMeanEvent(meter, MeanEventProperties.All, _eventSourceName, _counterName, EventName, meanValue: double.NegativeInfinity); - Assert.Empty(metricCollector.GetAllHistograms()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] @@ -570,7 +576,7 @@ public void EventCountersListener_Ignores_WhenCounterNameIsNotRegistered() options.Value.Counters.Add(_eventSourceName, new HashSet { _counterName }); using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(options, meter); @@ -582,15 +588,13 @@ public void EventCountersListener_Ignores_WhenCounterNameIsNotRegistered() payload = new { CounterType = "Sum", Name = "randomCounterName", Increment = 1 } }); - Assert.Empty(metricCollector.GetAllCounters()!); - Assert.Empty(metricCollector.GetAllHistograms()!); + Assert.Equal(0, instrumentCounter.Count); } [Fact] public void EventCountersListener_IncludeRecommendedDefault_AddsDefaultCountersToCounterList() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); using var listener = new EventCountersListener(Create(new EventCountersCollectorOptions { IncludeRecommendedDefault = true }), meter); Assert.NotEmpty(listener.Counters); @@ -629,7 +633,6 @@ public void EventCountersListener_IncludeRecommendedDefault_AddsDefaultCountersT public void EventCountersListener_DuplicateEntriesAreIgnored() { using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) { { _eventSourceName, new HashSet { _counterName } }, @@ -677,8 +680,12 @@ public void EventCountersListener_DuplicateEntriesAreIgnored() [Fact] public void EventCountersListener_UsingWildcard_EnablesAllCountersForSource() { + var firstCounterName = "randomCounterName"; + var secondCounterName = "randomCounterName2"; + using var meter = new Meter(); - using var metricCollector = new MetricCollector(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 { "*" } } @@ -687,9 +694,6 @@ public void EventCountersListener_UsingWildcard_EnablesAllCountersForSource() using var eventSource = new EventSource(_eventSourceName); using var listener = new EventCountersListener(Create(new EventCountersCollectorOptions { Counters = counters }), meter); - var firstCounterName = "randomCounterName"; - var secondCounterName = "randomCounterName2"; - eventSource.Write(EventName, new EventSourceOptions { Level = EventLevel.LogAlways }, new @@ -697,7 +701,7 @@ public void EventCountersListener_UsingWildcard_EnablesAllCountersForSource() payload = new { CounterType = "Mean", Name = firstCounterName, Mean = 1 } }); - var latest = metricCollector.GetHistogramValues($"{_eventSourceName}|{firstCounterName}")!.LatestWritten!; + var latest = metricCollector1.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); Assert.Single(listener.HistogramInstruments); @@ -709,7 +713,7 @@ public void EventCountersListener_UsingWildcard_EnablesAllCountersForSource() payload = new { CounterType = "Mean", Name = secondCounterName, Mean = 1 } }); - latest = metricCollector.GetHistogramValues($"{_eventSourceName}|{secondCounterName}")!.LatestWritten!; + latest = metricCollector2.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); Assert.Equal(2, listener.HistogramInstruments.Count); @@ -794,10 +798,10 @@ public void MeanCounter() using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, metricName); RunListener(options, meter, eventWaitHandle); - var latest = metricCollector.GetHistogramValues(metricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.True(latest.Value >= 0); } @@ -812,10 +816,10 @@ public void SumCounter() using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector = new MetricCollector(meter, metricName); RunListener(options, meter, eventWaitHandle); - var latest = metricCollector.GetCounterValues(metricName)!.LatestWritten!; + var latest = metricCollector.LastMeasurement; Assert.NotNull(latest); Assert.True(latest.Value >= 0); } @@ -827,7 +831,8 @@ public void EventCountersListener_IncludeRecommendedDefault_AddsDefaultCounters( string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var metricCollector1 = new MetricCollector(meter, metricName); + using var metricCollector2 = new MetricCollector(meter, $"{_eventSourceName}|{_counterName}"); IDictionary> counters = new Dictionary>(StringComparer.OrdinalIgnoreCase) { @@ -844,7 +849,7 @@ public void EventCountersListener_IncludeRecommendedDefault_AddsDefaultCounters( using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); RunListener(options, meter, eventWaitHandle); - var latest = metricCollector.GetCounterValues(metricName)!.LatestWritten!; + var latest = metricCollector1.LastMeasurement; Assert.NotNull(latest); Assert.True(latest.Value >= 0); @@ -858,7 +863,7 @@ public void EventCountersListener_IncludeRecommendedDefault_AddsDefaultCounters( payload = new { CounterType = "Sum", Name = _counterName, Increment = 1 } }); - latest = metricCollector.GetCounterValues($"{_eventSourceName}|{_counterName}")!.LatestWritten!; + latest = metricCollector2.LastMeasurement; Assert.NotNull(latest); Assert.Equal(1, latest.Value); } @@ -870,7 +875,8 @@ public void EventCountersListener_IncludeRecommendedDefault_AndDuplicateEntryInP string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(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) { @@ -887,11 +893,11 @@ public void EventCountersListener_IncludeRecommendedDefault_AndDuplicateEntryInP using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); RunListener(options, meter, eventWaitHandle); - var latest = metricCollector.GetCounterValues(metricName)!.LatestWritten!; + var latest = metricCollector1.LastMeasurement; Assert.NotNull(latest); Assert.True(latest.Value >= 0); - latest = metricCollector.GetHistogramValues($"{TestUtils.SystemRuntime}|active-timer-count")!.LatestWritten!; + latest = metricCollector2.LastMeasurement; Assert.NotNull(latest); Assert.True(latest.Value >= 0); } @@ -903,15 +909,14 @@ public void EventCountersListener_IncludeRecommendedDefault_SetToFalse_DoesNot_I string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(meter); + using var instrumentCounter = new InstrumentCounter(meter); IOptions options = Create(new EventCountersCollectorOptions()); using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); RunListener(options, meter, eventWaitHandle); - var counterRecords = metricCollector.GetCounterValues(metricName); - Assert.Null(counterRecords); + Assert.Equal(0, instrumentCounter.Count); } [Fact] @@ -921,7 +926,8 @@ public void EventCountersListener_UsingWildcard_IncludesAllCountersFromSource() string metricName = $"{TestUtils.SystemRuntime}|{counterName}"; using var meter = new Meter(); - using var metricCollector = new MetricCollector(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) { @@ -937,11 +943,11 @@ public void EventCountersListener_UsingWildcard_IncludesAllCountersFromSource() using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); RunListener(options, meter, eventWaitHandle); - var latest = metricCollector.GetCounterValues(metricName)!.LatestWritten!; + var latest = metricCollector1.LastMeasurement; Assert.NotNull(latest); Assert.True(latest.Value >= 0); - latest = metricCollector.GetHistogramValues($"{TestUtils.SystemRuntime}|active-timer-count")!.LatestWritten!; + latest = metricCollector2.LastMeasurement; Assert.NotNull(latest); Assert.True(latest.Value >= 0); }