From 919033d8c5c3cdfbd9e598a63f69d57c1b013042 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 17 Jul 2023 11:47:24 -0700 Subject: [PATCH 01/50] Packaging --- ...ft.Extensions.Diagnostics.Abstractions.sln | 76 +++++++++++ .../README.md | 23 ++++ ...oft.Extensions.Diagnostics.Abstractions.cs | 25 ++++ ...Extensions.Diagnostics.Abstractions.csproj | 13 ++ ...Extensions.Diagnostics.Abstractions.csproj | 37 ++++++ .../src/Resources/Strings.resx | 123 ++++++++++++++++++ .../Microsoft.Extensions.Diagnostics.sln | 14 ++ .../Microsoft.Extensions.Diagnostics.csproj | 2 +- 8 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln new file mode 100644 index 00000000000000..fe6f8c75be9dca --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.33711.374 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{79CE8C7E-A4AF-413C-A54D-86F17073559C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\ref\Microsoft.Bcl.AsyncInterfaces.csproj", "{9052AC86-4B89-4311-BEF2-7C49FB72DC0F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\src\Microsoft.Bcl.AsyncInterfaces.csproj", "{FA353FC1-2D03-426A-8973-0CDA8DF5E5DD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Abstractions", "..\Microsoft.Extensions.DependencyInjection.Abstractions\ref\Microsoft.Extensions.DependencyInjection.Abstractions.csproj", "{D6778DF4-DA03-43E7-BD9D-2E2C35DCCE7F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Abstractions", "..\Microsoft.Extensions.DependencyInjection.Abstractions\src\Microsoft.Extensions.DependencyInjection.Abstractions.csproj", "{527CCF66-AC37-487C-871E-A4F6B94E1731}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4DE63935-DCA9-4D63-9C1F-AAE79C89CA8B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{7631380A-FB73-4241-9987-0891A21E9769}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{548DF5F7-790C-4A1C-89EB-BD904CA1BA86}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "src\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{0588387D-FB65-4BA9-A8B2-DA6790027CC2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "ref\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {79CE8C7E-A4AF-413C-A54D-86F17073559C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79CE8C7E-A4AF-413C-A54D-86F17073559C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79CE8C7E-A4AF-413C-A54D-86F17073559C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79CE8C7E-A4AF-413C-A54D-86F17073559C}.Release|Any CPU.Build.0 = Release|Any CPU + {9052AC86-4B89-4311-BEF2-7C49FB72DC0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9052AC86-4B89-4311-BEF2-7C49FB72DC0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9052AC86-4B89-4311-BEF2-7C49FB72DC0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9052AC86-4B89-4311-BEF2-7C49FB72DC0F}.Release|Any CPU.Build.0 = Release|Any CPU + {FA353FC1-2D03-426A-8973-0CDA8DF5E5DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA353FC1-2D03-426A-8973-0CDA8DF5E5DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA353FC1-2D03-426A-8973-0CDA8DF5E5DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA353FC1-2D03-426A-8973-0CDA8DF5E5DD}.Release|Any CPU.Build.0 = Release|Any CPU + {D6778DF4-DA03-43E7-BD9D-2E2C35DCCE7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6778DF4-DA03-43E7-BD9D-2E2C35DCCE7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6778DF4-DA03-43E7-BD9D-2E2C35DCCE7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6778DF4-DA03-43E7-BD9D-2E2C35DCCE7F}.Release|Any CPU.Build.0 = Release|Any CPU + {527CCF66-AC37-487C-871E-A4F6B94E1731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {527CCF66-AC37-487C-871E-A4F6B94E1731}.Debug|Any CPU.Build.0 = Debug|Any CPU + {527CCF66-AC37-487C-871E-A4F6B94E1731}.Release|Any CPU.ActiveCfg = Release|Any CPU + {527CCF66-AC37-487C-871E-A4F6B94E1731}.Release|Any CPU.Build.0 = Release|Any CPU + {0588387D-FB65-4BA9-A8B2-DA6790027CC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0588387D-FB65-4BA9-A8B2-DA6790027CC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0588387D-FB65-4BA9-A8B2-DA6790027CC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0588387D-FB65-4BA9-A8B2-DA6790027CC2}.Release|Any CPU.Build.0 = Release|Any CPU + {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {79CE8C7E-A4AF-413C-A54D-86F17073559C} = {4DE63935-DCA9-4D63-9C1F-AAE79C89CA8B} + {9052AC86-4B89-4311-BEF2-7C49FB72DC0F} = {7631380A-FB73-4241-9987-0891A21E9769} + {FA353FC1-2D03-426A-8973-0CDA8DF5E5DD} = {548DF5F7-790C-4A1C-89EB-BD904CA1BA86} + {D6778DF4-DA03-43E7-BD9D-2E2C35DCCE7F} = {7631380A-FB73-4241-9987-0891A21E9769} + {527CCF66-AC37-487C-871E-A4F6B94E1731} = {548DF5F7-790C-4A1C-89EB-BD904CA1BA86} + {0588387D-FB65-4BA9-A8B2-DA6790027CC2} = {548DF5F7-790C-4A1C-89EB-BD904CA1BA86} + {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C} = {7631380A-FB73-4241-9987-0891A21E9769} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {450DA749-CBDC-4BDC-950F-8A491CF59D49} + EndGlobalSection +EndGlobal diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md new file mode 100644 index 00000000000000..e0b0a1710d498e --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md @@ -0,0 +1,23 @@ +# Microsoft.Extensions.Logging.Abstractions + +`Microsoft.Extensions.Logging.Abstractions` provides abstractions of logging. Interfaces defined in this package are implemented by classes in [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging/) and other logging packages. + +Commonly Used Types: +- `Microsoft.Extensions.Logging.ILogger` +- `Microsoft.Extensions.Logging.ILoggerFactory` +- `Microsoft.Extensions.Logging.ILogger` +- `Microsoft.Extensions.Logging.LogLevel` +- `Microsoft.Extensions.Logging.Logger` +- `Microsoft.Extensions.Logging.LoggerMessage` +- `Microsoft.Extensions.Logging.Abstractions.NullLogger` + +Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. + +## Contribution Bar +- [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) +- [x] [We consider PRs that target this library for improvements to the logging source generator](../../libraries/README.md#secondary-bars) + +The APIs and functionality are mature, but do get extended occasionally. + +## Deployment +[Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions) is included in the ASP.NET Core shared framework. The package is deployed as out-of-band (OOB) too and can be referenced into projects directly. \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs new file mode 100644 index 00000000000000..6b5a4081e11eec --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace Microsoft.Extensions.Diagnostics +{ + /* Format example: + public readonly partial struct EventId : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public EventId(int id, string? name = null) { throw null; } + public int Id { get { throw null; } } + public string? Name { get { throw null; } } + public bool Equals(Microsoft.Extensions.Logging.EventId other) { throw null; } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } + public override int GetHashCode() { throw null; } + public static bool operator ==(Microsoft.Extensions.Logging.EventId left, Microsoft.Extensions.Logging.EventId right) { throw null; } + public static implicit operator Microsoft.Extensions.Logging.EventId (int i) { throw null; } + public static bool operator !=(Microsoft.Extensions.Logging.EventId left, Microsoft.Extensions.Logging.EventId right) { throw null; } + public override string ToString() { throw null; } + }*/ +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj new file mode 100644 index 00000000000000..b86e9326873055 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -0,0 +1,13 @@ + + + $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj new file mode 100644 index 00000000000000..d291ca16939780 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -0,0 +1,37 @@ + + + + $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) + true + true + + true + Diagnostic abstractions for Microsoft.Extensions.Diagnostics. + +Commonly Used Types: +Microsoft.Extensions.Logging.ILogger +Microsoft.Extensions.Logging.ILoggerFactory +Microsoft.Extensions.Logging.ILogger<TCategoryName> +Microsoft.Extensions.Logging.LogLevel +Microsoft.Extensions.Logging.Logger<T> +Microsoft.Extensions.Logging.LoggerMessage +Microsoft.Extensions.Logging.Abstractions.NullLogger + + + + + + + + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx new file mode 100644 index 00000000000000..5112bed03e659f --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The format string '{0}' does not have the expected number of named parameters. Expected {1} parameter(s) but found {2} parameter(s). + + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln index d0ceba54c79984..a0e1e68d675d33 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln +++ b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln @@ -43,6 +43,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A447D0CB-601 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{66953A8A-9E31-486F-AF8E-7310F6707E4F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\ref\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{AA790584-200C-4301-8545-8B2854B2F6CC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\src\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{40525D17-4553-405E-8B21-4603B07D126A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +117,14 @@ Global {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Release|Any CPU.Build.0 = Release|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Release|Any CPU.Build.0 = Release|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -134,6 +146,8 @@ Global {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} {3FEA305D-0B5F-46A6-8E18-587387FCBFBF} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} {3D040E9F-C39B-49C6-8C87-68D427AECA8F} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {AA790584-200C-4301-8545-8B2854B2F6CC} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {40525D17-4553-405E-8B21-4603B07D126A} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7D279EE5-E38F-4125-AE82-6ADE52D72F26} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index 7a879044946db4..5a9f2769cdc6d6 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -17,7 +17,7 @@ - + From 775c9dd15a755e41e1f1fe4f0167a6df1317f40c Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 17 Jul 2023 12:58:19 -0700 Subject: [PATCH 02/50] Bulk add types --- ...oft.Extensions.Diagnostics.Abstractions.cs | 92 +++++++++++++++---- ...Extensions.Diagnostics.Abstractions.csproj | 2 + .../src/Metrics/IMetricsBuilder.cs | 12 +++ .../src/Metrics/IMetricsListener.cs | 15 +++ .../src/Metrics/IMetricsSource.cs | 10 ++ .../src/Metrics/InstrumentEnableRule.cs | 18 ++++ .../src/Metrics/MeterScope.cs | 14 +++ .../Metrics/MetricsBuilderEnableExtensions.cs | 41 +++++++++ .../src/Metrics/MetricsBuilderExtensions.cs | 11 +++ .../src/Metrics/MetricsEnableOptions.cs | 12 +++ ...Extensions.Diagnostics.Abstractions.csproj | 4 + .../Microsoft.Extensions.Diagnostics.csproj | 3 +- .../Microsoft.Extensions.Diagnostics.csproj | 1 - 13 files changed, 215 insertions(+), 20 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSource.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 6b5a4081e11eec..37ddafe14a79c6 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -4,22 +4,80 @@ // Changes to this file must follow the https://aka.ms/api-review process. // ------------------------------------------------------------------------------ -namespace Microsoft.Extensions.Diagnostics +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.Metrics { - /* Format example: - public readonly partial struct EventId : System.IEquatable - { - private readonly object _dummy; - private readonly int _dummyPrimitive; - public EventId(int id, string? name = null) { throw null; } - public int Id { get { throw null; } } - public string? Name { get { throw null; } } - public bool Equals(Microsoft.Extensions.Logging.EventId other) { throw null; } - public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } - public override int GetHashCode() { throw null; } - public static bool operator ==(Microsoft.Extensions.Logging.EventId left, Microsoft.Extensions.Logging.EventId right) { throw null; } - public static implicit operator Microsoft.Extensions.Logging.EventId (int i) { throw null; } - public static bool operator !=(Microsoft.Extensions.Logging.EventId left, Microsoft.Extensions.Logging.EventId right) { throw null; } - public override string ToString() { throw null; } - }*/ + public interface IMetricsBuilder + { + Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; } + } + public interface IMetricsListener + { + public void SetSource(IMetricsSource source); + public object? InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument); + public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState); + public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct; + } + public interface IMetricsSource + { + public void RecordObservableInstruments(); + } + public class InstrumentEnableRule + { + public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Action filter) { } + public string? ListenerName { get; } + public string? MeterName { get; } + public MeterScope Scopes { get; } + public string? InstrumentName { get; } + public Func? Filter { get; } + } + [Flags] + public enum MeterScope + { + Global, + Local + } + public static class MetricsBuilderEnableExtensions + { + // common overloads + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener => throw null!; + + // less common overloads + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; + + // all the same extension methods on MetricsEnableOptions + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; + } + public static class MetricsBuilderExtensions + { + public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } + public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } + } + public class MetricsEnableOptions + { + public IList Rules { get; } = null!; + } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj index b86e9326873055..0725cc36abad0e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -1,6 +1,7 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) + false @@ -8,6 +9,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs new file mode 100644 index 00000000000000..8e9919aa6f317a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public interface IMetricsBuilder + { + IServiceCollection Services { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs new file mode 100644 index 00000000000000..d1bca89f5269a2 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.Metrics; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public interface IMetricsListener + { + public void SetSource(IMetricsSource source); + public object? InstrumentPublished(Instrument instrument); + public void MeasurementsCompleted(Instrument instrument, object? userState); + public MeasurementCallback GetMeasurementHandler() where T : struct; + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSource.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSource.cs new file mode 100644 index 00000000000000..b10365c58987a4 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSource.cs @@ -0,0 +1,10 @@ +// 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.Diagnostics.Metrics +{ + public interface IMetricsSource + { + public void RecordObservableInstruments(); + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs new file mode 100644 index 00000000000000..9d601d4bde9135 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs @@ -0,0 +1,18 @@ +// 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.Metrics; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public class InstrumentEnableRule + { + public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Action filter) { } + public string? ListenerName { get; } + public string? MeterName { get; } + public MeterScope Scopes { get; } + public string? InstrumentName { get; } + public Func? Filter { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs new file mode 100644 index 00000000000000..fd36fcfce8c6ef --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + [Flags] + public enum MeterScope + { + Global, + Local + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs new file mode 100644 index 00000000000000..55dbd5e93fa96c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs @@ -0,0 +1,41 @@ +// 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.Metrics; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public static class MetricsBuilderEnableExtensions + { + // common overloads + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener => throw null!; + + // less common overloads + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; + + // all the same extension methods on MetricsEnableOptions + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs new file mode 100644 index 00000000000000..c0739de1634a99 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs @@ -0,0 +1,11 @@ +// 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.Diagnostics.Metrics +{ + public static class MetricsBuilderExtensions + { + public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } + public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs new file mode 100644 index 00000000000000..d281116d4e4fed --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public class MetricsEnableOptions + { + public IList Rules { get; } = null!; + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj index d291ca16939780..c6bd6bc54df5eb 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -4,6 +4,9 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true true + false + + CS1591 @@ -31,6 +34,7 @@ Microsoft.Extensions.Logging.Abstractions.NullLogger + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj index 2dbcc3c5ac7b62..98f6966eb8e54e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj @@ -9,8 +9,7 @@ - - + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index 5a9f2769cdc6d6..0f054980127722 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -16,7 +16,6 @@ - From a537d9eac97faafb363e400822807a1e5dd1ca2c Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 18 Jul 2023 11:18:38 -0700 Subject: [PATCH 03/50] Configuration packaging, API ref --- ...t.Extensions.Diagnostics.Configuration.sln | 204 ++++++++++++++++++ .../README.md | 15 ++ ...ft.Extensions.Diagnostics.Configuration.cs | 21 ++ ...xtensions.Diagnostics.Configuration.csproj | 17 ++ .../IMetricListenerConfiguration.cs | 12 ++ .../IMetricListenerConfigurationFactory.cs | 13 ++ .../MetricsBuilderConfigurationExtensions.cs | 12 ++ ...xtensions.Diagnostics.Configuration.csproj | 22 ++ .../src/Resources/Strings.resx | 63 ++++++ ...ons.Diagnostics.Configuration.Tests.csproj | 17 ++ .../ref/Microsoft.Extensions.Diagnostics.cs | 3 +- .../src/Metrics/MetricsServiceExtensions.cs | 18 ++ 12 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfiguration.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln new file mode 100644 index 00000000000000..4627a2575af392 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln @@ -0,0 +1,204 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33801.468 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\ref\Microsoft.Bcl.AsyncInterfaces.csproj", "{E7A3B914-598D-4ABC-B973-6CC444DAFE52}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\src\Microsoft.Bcl.AsyncInterfaces.csproj", "{280FDDEA-50B1-4BD3-83B1-475B15829538}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Abstractions", "..\Microsoft.Extensions.DependencyInjection.Abstractions\ref\Microsoft.Extensions.DependencyInjection.Abstractions.csproj", "{CE53C256-EE31-4E4A-8A05-70350840448F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Abstractions", "..\Microsoft.Extensions.DependencyInjection.Abstractions\src\Microsoft.Extensions.DependencyInjection.Abstractions.csproj", "{3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection", "..\Microsoft.Extensions.DependencyInjection\ref\Microsoft.Extensions.DependencyInjection.csproj", "{C175A982-E0E0-4E22-8A3B-0A9C00EE7730}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection", "..\Microsoft.Extensions.DependencyInjection\src\Microsoft.Extensions.DependencyInjection.csproj", "{1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "ref\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{EF75497C-6CB7-4471-980A-619EA1AB8CF6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "src\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{09E28D94-B771-48EB-800C-5A80C2C0055C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.DiagnosticSource", "..\System.Diagnostics.DiagnosticSource\ref\System.Diagnostics.DiagnosticSource.csproj", "{F452AA57-7BEC-4E64-BAB5-166078865EC4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.DiagnosticSource", "..\System.Diagnostics.DiagnosticSource\src\System.Diagnostics.DiagnosticSource.csproj", "{AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComInterfaceGenerator", "..\System.Runtime.InteropServices\gen\ComInterfaceGenerator\ComInterfaceGenerator.csproj", "{ED105ED3-0060-4035-AD5E-1F857F94C2DF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{3FEA305D-0B5F-46A6-8E18-587387FCBFBF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime", "..\System.Runtime\ref\System.Runtime.csproj", "{3D040E9F-C39B-49C6-8C87-68D427AECA8F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9BF048D0-411D-4C2A-8C32-3A3255501D27}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A447D0CB-601B-479E-A2B2-76E48F5D4D61}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{66953A8A-9E31-486F-AF8E-7310F6707E4F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\ref\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{AA790584-200C-4301-8545-8B2854B2F6CC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\src\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{40525D17-4553-405E-8B21-4603B07D126A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration.Tests", "tests\Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj", "{85133A0C-2427-4085-AA71-02881F46AEAE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics", "..\Microsoft.Extensions.Diagnostics\ref\Microsoft.Extensions.Diagnostics.csproj", "{BCB6C67A-A765-41A0-80EA-7807720714D0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics", "..\Microsoft.Extensions.Diagnostics\src\Microsoft.Extensions.Diagnostics.csproj", "{CB8A4E34-76F8-408A-98BE-5881D12BA02C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Tests", "..\Microsoft.Extensions.Diagnostics\tests\Microsoft.Extensions.Diagnostics.Tests.csproj", "{F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "..\Microsoft.Extensions.Configuration.Abstractions\ref\Microsoft.Extensions.Configuration.Abstractions.csproj", "{0D43A981-ED8F-434A-B349-3227DDD38D2E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "..\Microsoft.Extensions.Configuration.Abstractions\src\Microsoft.Extensions.Configuration.Abstractions.csproj", "{9AC40671-E708-4FE1-B5CB-ADBBC425F793}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Primitives", "..\Microsoft.Extensions.Primitives\ref\Microsoft.Extensions.Primitives.csproj", "{CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Primitives", "..\Microsoft.Extensions.Primitives\src\Microsoft.Extensions.Primitives.csproj", "{DFCDD214-BAED-468D-BC61-B1D536FF0AFF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Release|Any CPU.Build.0 = Release|Any CPU + {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Release|Any CPU.Build.0 = Release|Any CPU + {280FDDEA-50B1-4BD3-83B1-475B15829538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {280FDDEA-50B1-4BD3-83B1-475B15829538}.Debug|Any CPU.Build.0 = Debug|Any CPU + {280FDDEA-50B1-4BD3-83B1-475B15829538}.Release|Any CPU.ActiveCfg = Release|Any CPU + {280FDDEA-50B1-4BD3-83B1-475B15829538}.Release|Any CPU.Build.0 = Release|Any CPU + {CE53C256-EE31-4E4A-8A05-70350840448F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE53C256-EE31-4E4A-8A05-70350840448F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE53C256-EE31-4E4A-8A05-70350840448F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE53C256-EE31-4E4A-8A05-70350840448F}.Release|Any CPU.Build.0 = Release|Any CPU + {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Release|Any CPU.Build.0 = Release|Any CPU + {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Release|Any CPU.Build.0 = Release|Any CPU + {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Release|Any CPU.Build.0 = Release|Any CPU + {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Release|Any CPU.Build.0 = Release|Any CPU + {09E28D94-B771-48EB-800C-5A80C2C0055C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09E28D94-B771-48EB-800C-5A80C2C0055C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09E28D94-B771-48EB-800C-5A80C2C0055C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09E28D94-B771-48EB-800C-5A80C2C0055C}.Release|Any CPU.Build.0 = Release|Any CPU + {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Release|Any CPU.Build.0 = Release|Any CPU + {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Release|Any CPU.Build.0 = Release|Any CPU + {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Release|Any CPU.Build.0 = Release|Any CPU + {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Release|Any CPU.Build.0 = Release|Any CPU + {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Release|Any CPU.Build.0 = Release|Any CPU + {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Release|Any CPU.Build.0 = Release|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA790584-200C-4301-8545-8B2854B2F6CC}.Release|Any CPU.Build.0 = Release|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.Build.0 = Release|Any CPU + {85133A0C-2427-4085-AA71-02881F46AEAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85133A0C-2427-4085-AA71-02881F46AEAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85133A0C-2427-4085-AA71-02881F46AEAE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85133A0C-2427-4085-AA71-02881F46AEAE}.Release|Any CPU.Build.0 = Release|Any CPU + {BCB6C67A-A765-41A0-80EA-7807720714D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCB6C67A-A765-41A0-80EA-7807720714D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCB6C67A-A765-41A0-80EA-7807720714D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCB6C67A-A765-41A0-80EA-7807720714D0}.Release|Any CPU.Build.0 = Release|Any CPU + {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Release|Any CPU.Build.0 = Release|Any CPU + {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Release|Any CPU.Build.0 = Release|Any CPU + {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Release|Any CPU.Build.0 = Release|Any CPU + {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Release|Any CPU.Build.0 = Release|Any CPU + {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Release|Any CPU.Build.0 = Release|Any CPU + {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} + {E7A3B914-598D-4ABC-B973-6CC444DAFE52} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {280FDDEA-50B1-4BD3-83B1-475B15829538} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {CE53C256-EE31-4E4A-8A05-70350840448F} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {C175A982-E0E0-4E22-8A3B-0A9C00EE7730} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {EF75497C-6CB7-4471-980A-619EA1AB8CF6} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {09E28D94-B771-48EB-800C-5A80C2C0055C} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {F452AA57-7BEC-4E64-BAB5-166078865EC4} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {ED105ED3-0060-4035-AD5E-1F857F94C2DF} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} + {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} + {3FEA305D-0B5F-46A6-8E18-587387FCBFBF} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} + {3D040E9F-C39B-49C6-8C87-68D427AECA8F} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {AA790584-200C-4301-8545-8B2854B2F6CC} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {40525D17-4553-405E-8B21-4603B07D126A} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {85133A0C-2427-4085-AA71-02881F46AEAE} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} + {BCB6C67A-A765-41A0-80EA-7807720714D0} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {CB8A4E34-76F8-408A-98BE-5881D12BA02C} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} + {0D43A981-ED8F-434A-B349-3227DDD38D2E} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {9AC40671-E708-4FE1-B5CB-ADBBC425F793} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {DFCDD214-BAED-468D-BC61-B1D536FF0AFF} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7D279EE5-E38F-4125-AE82-6ADE52D72F26} + EndGlobalSection +EndGlobal diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md new file mode 100644 index 00000000000000..776b53340b7833 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md @@ -0,0 +1,15 @@ +# Microsoft.Extensions.Diagnostics + +`Microsoft.Extensions.Diagnostics` contains the default implementation of meter factory and extension methods for registering this default meter factory to the DI. + +Commonly Used APIS: +- MetricsServiceExtensions.AddMetrics(this IServiceCollection services) +- MeterFactoryExtensions.Create(this IMeterFactory, string name, string? version = null, IEnumerable> tags = null, object? scope = null) + +## Contribution Bar +- [x] [We consider new features, new APIs, bug fixes, and performance changes](https://github.com/dotnet/runtime/tree/main/src/libraries#contribution-bar) + +The APIs and functionality are mature, but do get extended occasionally. + +## Deployment +[Microsoft.Extensions.Diagnostics](https://www.nuget.org/packages/Microsoft.Extensions.Diagnostics) is included in the ASP.NET Core shared framework. The package is deployed as out-of-band (OOB) too and can be referenced into projects directly. \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs new file mode 100644 index 00000000000000..9d92d4f27ec330 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs @@ -0,0 +1,21 @@ +// 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.Diagnostics.Metrics +{ + public static class MetricsBuilderConfigurationExtensions + { + public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) => throw null!; + } +} +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + public interface IMetricListenerConfiguration + { + Microsoft.Extensions.Configuration.IConfiguration Configuration { get; } + } + public interface IMetricListenerConfigurationFactory + { + Microsoft.Extensions.Configuration.IConfiguration GetConfiguration(System.Type listenerType); + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj new file mode 100644 index 00000000000000..38941813e02b0e --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj @@ -0,0 +1,17 @@ + + + + $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) + false + + + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfiguration.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfiguration.cs new file mode 100644 index 00000000000000..802ba4224880ca --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfiguration.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Configuration; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + public interface IMetricListenerConfiguration + { + IConfiguration Configuration { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs new file mode 100644 index 00000000000000..3fa961597e6928 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs @@ -0,0 +1,13 @@ +// 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 Microsoft.Extensions.Configuration; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + public interface IMetricListenerConfigurationFactory + { + IConfiguration GetConfiguration(Type listenerType); + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs new file mode 100644 index 00000000000000..e0d6b85d5ebe08 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Configuration; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public static class MetricsBuilderConfigurationExtensions + { + public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) => throw null!; + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj new file mode 100644 index 00000000000000..3b938f6c60f466 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj @@ -0,0 +1,22 @@ + + + + $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) + true + false + + CS1591 + + true + true + This package includes the default implementation of IMeterFactory and additional extension methods to easily register it with the Dependency Injection framework. + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx new file mode 100644 index 00000000000000..ab0d5ca5a461b3 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The meter factory does not allow a custom scope value when creating a meter. + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj new file mode 100644 index 00000000000000..0296919ebc510c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj @@ -0,0 +1,17 @@ + + + + $(NetCoreAppCurrent);$(NetFrameworkMinimum) + true + true + false + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 9c1afafdd2fb53..dfc39c7b54ba80 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -6,5 +6,6 @@ namespace Microsoft.Extensions.DependencyInjection public static class MetricsServiceExtensions { public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure) { throw null; } } -} \ No newline at end of file +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs index 6bf2228e72345a..bded174408a97a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs @@ -29,5 +29,23 @@ public static IServiceCollection AddMetrics(this IServiceCollection services) return services; } + + /// + /// Adds metrics services to the specified . + /// + /// The to add services to. + /// A callback to configure the . + /// The so that additional calls can be chained. + public static IServiceCollection AddMetrics(this IServiceCollection services, Action configure) + { + if (configure is null) + { + throw new ArgumentNullException(nameof(configure)); + } + + services.AddMetrics(); + + throw new NotImplementedException(); + } } } From 7453b2a5987acb668a574ca06c36c911f37da187 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 18 Jul 2023 16:36:29 -0700 Subject: [PATCH 04/50] Hosting API dump, dependencies --- ...crosoft.Extensions.Hosting.Abstractions.cs | 1 + ...oft.Extensions.Hosting.Abstractions.csproj | 2 + .../src/IHostApplicationBuilder.cs | 6 + ...oft.Extensions.Hosting.Abstractions.csproj | 2 + .../Microsoft.Extensions.Hosting.sln | 128 ++++++++++++------ .../ref/Microsoft.Extensions.Hosting.cs | 3 + .../ref/Microsoft.Extensions.Hosting.csproj | 1 + .../src/HostApplicationBuilder.cs | 3 + .../src/HostingHostBuilderExtensions.cs | 4 + .../src/Microsoft.Extensions.Hosting.csproj | 2 + 10 files changed, 111 insertions(+), 41 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs index cc663649830ad8..dc256ff6466f30 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.cs @@ -102,6 +102,7 @@ public partial interface IHostApplicationBuilder Microsoft.Extensions.Configuration.IConfigurationManager Configuration { get; } Microsoft.Extensions.Hosting.IHostEnvironment Environment { get; } Microsoft.Extensions.Logging.ILoggingBuilder Logging { get; } + Microsoft.Extensions.Diagnostics.Metrics.IMetricsBuilder Metrics { get; } System.Collections.Generic.IDictionary Properties { get; } Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; } void ConfigureContainer(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory factory, System.Action? configure = null) where TContainerBuilder : notnull; diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj index e69771980113fc..460d22f7a0c1ae 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) $(NoWarn);CS0618 + false @@ -16,6 +17,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs index 5ae02d03a7b59d..ddad9f58d2538f 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.Hosting; @@ -37,6 +38,11 @@ public interface IHostApplicationBuilder /// ILoggingBuilder Logging { get; } + /// + /// Allows enabling metrics and directing their output. + /// + IMetricsBuilder Metrics { get; } + /// /// Gets a collection of services for the application to compose. This is useful for adding user provided or framework provided services. /// diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj index f7881ef99062f4..55cbc03238b809 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj @@ -5,6 +5,7 @@ Microsoft.Extensions.Hosting true true + false Hosting and startup abstractions for applications. @@ -21,6 +22,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln b/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln index f07ea3b0139d2e..d761fb5ddc15aa 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln +++ b/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.33711.374 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{BCAE2699-A994-48FE-B9B0-5580D267BD2E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\ref\Microsoft.Bcl.AsyncInterfaces.csproj", "{47ACDB6F-34CB-478D-9E43-F3662EE5838D}" @@ -165,6 +169,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{59A29BF0-B76 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{A9A8D649-4C09-4FD1-9837-EE7B9D902253}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics", "..\Microsoft.Extensions.Diagnostics\src\Microsoft.Extensions.Diagnostics.csproj", "{5B630ECF-5C4D-4F66-9AB9-59B014520A5C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\src\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{FB173557-75BD-4943-B813-0AD13E0337A3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "..\Microsoft.Extensions.Diagnostics.Configuration\src\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{96FC685A-DEC6-4CB1-B6AF-597060B3D478}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "..\Microsoft.Extensions.Diagnostics.Configuration\ref\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{68401297-58B6-4A4C-BCC0-419EA594DA74}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\ref\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{982C5C02-DD1E-452C-B5BC-13207D2CB14F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics", "..\Microsoft.Extensions.Diagnostics\ref\Microsoft.Extensions.Diagnostics.csproj", "{A9181F84-BD47-4B46-AD71-908E289A695E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -487,90 +503,120 @@ Global {0813853E-8C78-429A-B01A-3FB2EF1898F8}.Debug|Any CPU.Build.0 = Debug|Any CPU {0813853E-8C78-429A-B01A-3FB2EF1898F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {0813853E-8C78-429A-B01A-3FB2EF1898F8}.Release|Any CPU.Build.0 = Release|Any CPU + {5B630ECF-5C4D-4F66-9AB9-59B014520A5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B630ECF-5C4D-4F66-9AB9-59B014520A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B630ECF-5C4D-4F66-9AB9-59B014520A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B630ECF-5C4D-4F66-9AB9-59B014520A5C}.Release|Any CPU.Build.0 = Release|Any CPU + {FB173557-75BD-4943-B813-0AD13E0337A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB173557-75BD-4943-B813-0AD13E0337A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB173557-75BD-4943-B813-0AD13E0337A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB173557-75BD-4943-B813-0AD13E0337A3}.Release|Any CPU.Build.0 = Release|Any CPU + {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Release|Any CPU.Build.0 = Release|Any CPU + {68401297-58B6-4A4C-BCC0-419EA594DA74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68401297-58B6-4A4C-BCC0-419EA594DA74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68401297-58B6-4A4C-BCC0-419EA594DA74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68401297-58B6-4A4C-BCC0-419EA594DA74}.Release|Any CPU.Build.0 = Release|Any CPU + {982C5C02-DD1E-452C-B5BC-13207D2CB14F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {982C5C02-DD1E-452C-B5BC-13207D2CB14F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {982C5C02-DD1E-452C-B5BC-13207D2CB14F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {982C5C02-DD1E-452C-B5BC-13207D2CB14F}.Release|Any CPU.Build.0 = Release|Any CPU + {A9181F84-BD47-4B46-AD71-908E289A695E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9181F84-BD47-4B46-AD71-908E289A695E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9181F84-BD47-4B46-AD71-908E289A695E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9181F84-BD47-4B46-AD71-908E289A695E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {BCAE2699-A994-48FE-B9B0-5580D267BD2E} = {06E9ED4F-D3CF-4216-A8C7-E8A0AB16E33D} - {2A882DCC-96C1-4EDF-A7F0-B526EC81533F} = {06E9ED4F-D3CF-4216-A8C7-E8A0AB16E33D} - {495208B7-31BB-4802-A769-CEE4917BDF75} = {06E9ED4F-D3CF-4216-A8C7-E8A0AB16E33D} - {33C3D8F0-297F-4471-92B0-F4E8717F10E3} = {06E9ED4F-D3CF-4216-A8C7-E8A0AB16E33D} {47ACDB6F-34CB-478D-9E43-F3662EE5838D} = {E041754F-1A93-443A-9294-87DC1C30B471} + {C24E4188-27CB-4E00-A5F0-62AE23D536EE} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {EDAC3418-1D4E-4216-9371-0A36EA1E13FE} = {E041754F-1A93-443A-9294-87DC1C30B471} + {020874FC-11A2-4FC7-8929-527462F8819A} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} + {53A78CBA-7270-4F79-84EF-09F669729079} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} {670AB88D-85C9-4674-A652-C27488ED73F9} = {E041754F-1A93-443A-9294-87DC1C30B471} + {3411D565-223A-44B5-864C-E30F826001B4} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {4AB3E652-6709-4011-AC2F-C379A0415BAC} = {E041754F-1A93-443A-9294-87DC1C30B471} + {ECA6E734-3908-45B4-9DFA-FDDA49AD620D} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {3D657A5A-C7DF-4817-864F-944755DCE6DF} = {E041754F-1A93-443A-9294-87DC1C30B471} + {0C041643-1217-466B-AF2E-1E44C7B117B1} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {BD938E1D-6FC8-4D46-B103-B4D35761CEA2} = {E041754F-1A93-443A-9294-87DC1C30B471} + {6DB8DE55-5419-48EA-B4CD-2880E00409FB} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {1C07ECD0-F69E-4E35-9C68-E4063B5D97EC} = {E041754F-1A93-443A-9294-87DC1C30B471} + {0A738527-821F-4089-B64E-3C0F4714DC78} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {37A36947-2652-4AFD-BCF8-AAFD4D4D2827} = {E041754F-1A93-443A-9294-87DC1C30B471} + {EA9E6747-867B-4312-94B3-624EEB1C7A7A} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {13BC7AAA-7831-4500-9D28-A93FA4CA3C74} = {E041754F-1A93-443A-9294-87DC1C30B471} + {EB889E78-AE59-4D41-AC29-8BC4D58BCE16} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {135F551E-ACB8-4073-ABB5-A1FA558455DE} = {E041754F-1A93-443A-9294-87DC1C30B471} + {0EEA7382-25A8-4FB0-AE9A-4ECDF2FF6FB7} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {5532E155-E423-4FFD-B009-80B4281D36BA} = {E041754F-1A93-443A-9294-87DC1C30B471} + {6C6DDBF6-AAF3-4A2A-8CB1-C7A630B7C49F} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {24220AD7-03ED-427A-BFC8-114C475EAD0F} = {E041754F-1A93-443A-9294-87DC1C30B471} + {0A021166-613C-430C-8460-50F1E0DF7AD8} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {376BB9D1-6C3E-4BB1-B13A-F0750D2BE01F} = {E041754F-1A93-443A-9294-87DC1C30B471} + {1E3D564C-A79E-4E28-8E13-626EE7780831} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {E4157F2E-F11D-48C6-A146-B4D12D9211F7} = {E041754F-1A93-443A-9294-87DC1C30B471} + {9C06E60B-2D45-4284-B7C8-7AE6D8194CE0} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {2F25C0DB-E010-4802-8030-C88E2D09D3B0} = {E041754F-1A93-443A-9294-87DC1C30B471} + {973CE6DA-B55D-4E55-88D5-53BE69D44410} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {0D7771CB-B3D1-4FF4-A523-40BFD3B12A84} = {E041754F-1A93-443A-9294-87DC1C30B471} + {F5CF1FC4-8F56-49BD-BFC2-5AD42AE6302D} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} + {2A882DCC-96C1-4EDF-A7F0-B526EC81533F} = {06E9ED4F-D3CF-4216-A8C7-E8A0AB16E33D} + {495208B7-31BB-4802-A769-CEE4917BDF75} = {06E9ED4F-D3CF-4216-A8C7-E8A0AB16E33D} + {33C3D8F0-297F-4471-92B0-F4E8717F10E3} = {06E9ED4F-D3CF-4216-A8C7-E8A0AB16E33D} + {1B235247-6666-4B62-95A4-AC043626FDEA} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} + {5F6EF6F2-A742-445B-9418-682188F61130} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} + {3F732A69-1E48-4EA7-A40E-9C6C9332388E} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} {42E1BF94-6FE0-4017-9702-55913BD95EDE} = {E041754F-1A93-443A-9294-87DC1C30B471} + {8845E6FF-94B2-4994-A8F4-DF30844A2168} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {15AD68F8-6C58-41A3-99B3-558C42A6E7A5} = {E041754F-1A93-443A-9294-87DC1C30B471} + {99C8FB58-8718-4E76-AEFA-3C42F2F729B1} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {28C9D427-83BA-46A6-BEF5-6994A07F2258} = {E041754F-1A93-443A-9294-87DC1C30B471} + {F3230087-28E2-4ADF-A7D1-D48C5D9CFFE9} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {098CEEE6-91D3-4A7C-A5D5-6BB329BB2B7B} = {E041754F-1A93-443A-9294-87DC1C30B471} + {B729474D-0E96-4296-B317-450EE24F6B48} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {9ADBD2EE-D390-490C-BBEA-F844FE6F371E} = {E041754F-1A93-443A-9294-87DC1C30B471} + {2D4DBF5A-3BF4-4846-89F2-6FCDB80BF295} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {465AE1C4-84DD-4864-916A-74D89DC3BBBC} = {E041754F-1A93-443A-9294-87DC1C30B471} + {D466E363-F930-4D26-AE55-0256182961DD} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {D69326FC-CD05-4690-91C0-1537A80ACBFF} = {E041754F-1A93-443A-9294-87DC1C30B471} + {518D4AE0-FBFF-493A-A2DF-8ACBA842AE19} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {014EE6B4-BE08-4E50-9EBD-0D7A0CB7A76E} = {E041754F-1A93-443A-9294-87DC1C30B471} + {0AEAD15B-CD38-4462-A36C-655ED8D0CBD1} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {DD121F9F-3548-4247-8E10-FB584FC0827C} = {E041754F-1A93-443A-9294-87DC1C30B471} + {25C5119A-AAFB-4C74-9E12-F747CA4D80E7} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {45235656-E284-4682-BE70-9A284FD73243} = {E041754F-1A93-443A-9294-87DC1C30B471} + {D975AE29-0CA2-43FC-90A0-B266DF7D4C2A} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {FA490103-0140-473C-A572-72D529249234} = {E041754F-1A93-443A-9294-87DC1C30B471} {F90D2ED9-A38D-4E0B-AB9C-651FD24F23E2} = {E041754F-1A93-443A-9294-87DC1C30B471} {28726AEF-2732-4832-8930-074ACD9DC732} = {E041754F-1A93-443A-9294-87DC1C30B471} - {34ECA788-A105-411B-AE8B-17B7CC1D703D} = {E041754F-1A93-443A-9294-87DC1C30B471} - {9DB83E87-F09F-4DE0-8127-8C9509F0649B} = {E041754F-1A93-443A-9294-87DC1C30B471} - {000F87DC-0D56-4B37-8A0E-F7BCB3C3B61A} = {E041754F-1A93-443A-9294-87DC1C30B471} - {C595A27A-FA05-4BC8-9048-402338D7AF76} = {E041754F-1A93-443A-9294-87DC1C30B471} - {1E3D79D4-51D6-46C6-BF0F-DF51A47C5952} = {E041754F-1A93-443A-9294-87DC1C30B471} - {C24E4188-27CB-4E00-A5F0-62AE23D536EE} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {020874FC-11A2-4FC7-8929-527462F8819A} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {3411D565-223A-44B5-864C-E30F826001B4} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {ECA6E734-3908-45B4-9DFA-FDDA49AD620D} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {0C041643-1217-466B-AF2E-1E44C7B117B1} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {6DB8DE55-5419-48EA-B4CD-2880E00409FB} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {0A738527-821F-4089-B64E-3C0F4714DC78} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {EA9E6747-867B-4312-94B3-624EEB1C7A7A} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {EB889E78-AE59-4D41-AC29-8BC4D58BCE16} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {0EEA7382-25A8-4FB0-AE9A-4ECDF2FF6FB7} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {6C6DDBF6-AAF3-4A2A-8CB1-C7A630B7C49F} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {0A021166-613C-430C-8460-50F1E0DF7AD8} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {1E3D564C-A79E-4E28-8E13-626EE7780831} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {9C06E60B-2D45-4284-B7C8-7AE6D8194CE0} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {973CE6DA-B55D-4E55-88D5-53BE69D44410} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {F5CF1FC4-8F56-49BD-BFC2-5AD42AE6302D} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {8845E6FF-94B2-4994-A8F4-DF30844A2168} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {99C8FB58-8718-4E76-AEFA-3C42F2F729B1} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {F3230087-28E2-4ADF-A7D1-D48C5D9CFFE9} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {B729474D-0E96-4296-B317-450EE24F6B48} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {2D4DBF5A-3BF4-4846-89F2-6FCDB80BF295} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {D466E363-F930-4D26-AE55-0256182961DD} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {518D4AE0-FBFF-493A-A2DF-8ACBA842AE19} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {0AEAD15B-CD38-4462-A36C-655ED8D0CBD1} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {25C5119A-AAFB-4C74-9E12-F747CA4D80E7} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {D975AE29-0CA2-43FC-90A0-B266DF7D4C2A} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {0BC9E4F4-5C34-4B90-80AB-2933992D99A6} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} + {34ECA788-A105-411B-AE8B-17B7CC1D703D} = {E041754F-1A93-443A-9294-87DC1C30B471} {A5DD36AF-F0AD-4616-AB91-BC63E89B2744} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {4ED9C0A9-C1EF-47ED-99F8-74B7411C971B} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {B41AA17B-5129-41CC-8EA4-250B80BABF87} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {0813853E-8C78-429A-B01A-3FB2EF1898F8} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {53A78CBA-7270-4F79-84EF-09F669729079} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} - {1B235247-6666-4B62-95A4-AC043626FDEA} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} - {5F6EF6F2-A742-445B-9418-682188F61130} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} - {3F732A69-1E48-4EA7-A40E-9C6C9332388E} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} + {9DB83E87-F09F-4DE0-8127-8C9509F0649B} = {E041754F-1A93-443A-9294-87DC1C30B471} {CCF8862C-E2D7-45B0-96EF-54134178DA5A} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} {C00722C2-E56B-424F-9216-FA6A91788986} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} {90DD4D77-E3DC-456E-A27F-F13DA6194481} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} + {000F87DC-0D56-4B37-8A0E-F7BCB3C3B61A} = {E041754F-1A93-443A-9294-87DC1C30B471} + {C595A27A-FA05-4BC8-9048-402338D7AF76} = {E041754F-1A93-443A-9294-87DC1C30B471} + {B41AA17B-5129-41CC-8EA4-250B80BABF87} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {0A495D7B-44DE-4E59-8497-6CBF7C55CFA5} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} {B69B7D59-6E7D-43FC-B83F-AA481B677B06} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} {4F7612BF-0AFB-4AC5-952F-7D12D99FA357} = {A9A8D649-4C09-4FD1-9837-EE7B9D902253} + {1E3D79D4-51D6-46C6-BF0F-DF51A47C5952} = {E041754F-1A93-443A-9294-87DC1C30B471} + {0813853E-8C78-429A-B01A-3FB2EF1898F8} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} + {5B630ECF-5C4D-4F66-9AB9-59B014520A5C} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} + {FB173557-75BD-4943-B813-0AD13E0337A3} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} + {96FC685A-DEC6-4CB1-B6AF-597060B3D478} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} + {68401297-58B6-4A4C-BCC0-419EA594DA74} = {E041754F-1A93-443A-9294-87DC1C30B471} + {982C5C02-DD1E-452C-B5BC-13207D2CB14F} = {E041754F-1A93-443A-9294-87DC1C30B471} + {A9181F84-BD47-4B46-AD71-908E289A695E} = {E041754F-1A93-443A-9294-87DC1C30B471} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {971198CC-ACB9-4718-9024-42A87E02E503} diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs index c6415b41429ad5..b0ddb0e2274c47 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs @@ -35,6 +35,7 @@ public HostApplicationBuilder(string[]? args) { } public Microsoft.Extensions.Configuration.ConfigurationManager Configuration { get { throw null; } } public Microsoft.Extensions.Hosting.IHostEnvironment Environment { get { throw null; } } public Microsoft.Extensions.Logging.ILoggingBuilder Logging { get { throw null; } } + public Microsoft.Extensions.Diagnostics.Metrics.IMetricsBuilder Metrics { get { throw null; } } Microsoft.Extensions.Configuration.IConfigurationManager Microsoft.Extensions.Hosting.IHostApplicationBuilder.Configuration { get { throw null; } } System.Collections.Generic.IDictionary Microsoft.Extensions.Hosting.IHostApplicationBuilder.Properties { get { throw null; } } public Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get { throw null; } } @@ -97,6 +98,8 @@ public static partial class HostingHostBuilderExtensions public static Microsoft.Extensions.Hosting.IHostBuilder UseDefaultServiceProvider(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configure) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseDefaultServiceProvider(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configure) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseEnvironment(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, string environment) { throw null; } + public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureMetrics(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureMetrics) => throw null!; + public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureMetrics(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureMetrics) => throw null!; } public partial class HostOptions { diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj index 634b13f75c45d3..997fe5d0af1003 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) $(NoWarn);CS0618 + false diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs index 2de31175450a4a..b76d29a7ac07ee 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs @@ -7,6 +7,7 @@ using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging; @@ -203,6 +204,8 @@ private void Initialize(HostApplicationBuilderSettings settings, out HostBuilder /// public ILoggingBuilder Logging => _logging; + public IMetricsBuilder Metrics => throw null!; + /// public void ConfigureContainer(IServiceProviderFactory factory, Action? configure = null) where TContainerBuilder : notnull { diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index 5b2f56abe6a665..87f5f2d141daab 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.EventLog; @@ -391,5 +392,8 @@ public static Task RunConsoleAsync(this IHostBuilder hostBuilder, Action configureMetrics) => throw null!; + public static IHostBuilder ConfigureMetrics(this IHostBuilder hostBuilder, Action configureMetrics) => throw null!; } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index 1ea45457d06272..a464d86b6769da 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -5,6 +5,7 @@ true Hosting and startup infrastructures for applications. true + false @@ -35,6 +36,7 @@ + From 784becbba88262e1e3d657414c8a1d420a727a90 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 19 Jul 2023 14:07:32 -0700 Subject: [PATCH 05/50] AddConsole --- .../ref/Microsoft.Extensions.Diagnostics.cs | 7 +++++++ .../Metrics/MetricsBuilderConsoleExtensions.cs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index dfc39c7b54ba80..da9151fb8d53cd 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -9,3 +9,10 @@ public static class MetricsServiceExtensions public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure) { throw null; } } } +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public static class MetricsBuilderConsoleExtensions + { + public static IMetricsBuilder AddConsole(this IMetricsBuilder builder) => throw null!; + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs new file mode 100644 index 00000000000000..d3537f4db29b67 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs @@ -0,0 +1,18 @@ +// 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.Diagnostics.Metrics +{ + /// + /// IMetricsBuilder extension methods for console output. + /// + public static class MetricsBuilderConsoleExtensions + { + /// + /// Enables console output for metrics for debugging purposes. This is not recommended for production use. + /// + /// The metrics builder. + /// The original metrics builder for chaining. + public static IMetricsBuilder AddConsole(this IMetricsBuilder builder) => throw null!; + } +} From 6103d8258c808f63a9309a33ce6554883c420a51 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 19 Jul 2023 14:39:21 -0700 Subject: [PATCH 06/50] Hosting wireup --- .../src/HostApplicationBuilder.cs | 16 ++++++++--- .../src/HostBuilder.cs | 1 + .../src/HostingHostBuilderExtensions.cs | 28 +++++++++++++++++-- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs index b76d29a7ac07ee..95947c2d4b08dc 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs @@ -23,6 +23,7 @@ public sealed class HostApplicationBuilder : IHostApplicationBuilder private readonly ServiceCollection _serviceCollection = new(); private readonly IHostEnvironment _environment; private readonly LoggingBuilder _logging; + private readonly MetricsBuilder _metrics; private Func _createServiceProvider; private Action _configureContainer = _ => { }; @@ -95,7 +96,7 @@ public HostApplicationBuilder(HostApplicationBuilderSettings? settings) Configuration.AddEnvironmentVariables(prefix: "DOTNET_"); } - Initialize(settings, out _hostBuilderContext, out _environment, out _logging); + Initialize(settings, out _hostBuilderContext, out _environment, out _logging, out _metrics); ServiceProviderOptions? serviceProviderOptions = null; if (!settings.DisableDefaults) @@ -121,7 +122,7 @@ internal HostApplicationBuilder(HostApplicationBuilderSettings? settings, bool e settings ??= new HostApplicationBuilderSettings(); Configuration = settings.Configuration ?? new ConfigurationManager(); - Initialize(settings, out _hostBuilderContext, out _environment, out _logging); + Initialize(settings, out _hostBuilderContext, out _environment, out _logging, out _metrics); _createServiceProvider = () => { @@ -132,7 +133,7 @@ internal HostApplicationBuilder(HostApplicationBuilderSettings? settings, bool e }; } - private void Initialize(HostApplicationBuilderSettings settings, out HostBuilderContext hostBuilderContext, out IHostEnvironment environment, out LoggingBuilder logging) + private void Initialize(HostApplicationBuilderSettings settings, out HostBuilderContext hostBuilderContext, out IHostEnvironment environment, out LoggingBuilder logging, out MetricsBuilder metrics) { // Command line args are added even when settings.DisableDefaults == true. If the caller didn't want settings.Args applied, // they wouldn't have set them on the settings. @@ -181,6 +182,7 @@ private void Initialize(HostApplicationBuilderSettings settings, out HostBuilder () => _appServices!); logging = new LoggingBuilder(Services); + metrics = new MetricsBuilder(Services); } IDictionary IHostApplicationBuilder.Properties => _hostBuilderContext.Properties; @@ -204,7 +206,8 @@ private void Initialize(HostApplicationBuilderSettings settings, out HostBuilder /// public ILoggingBuilder Logging => _logging; - public IMetricsBuilder Metrics => throw null!; + /// + public IMetricsBuilder Metrics => _metrics; /// public void ConfigureContainer(IServiceProviderFactory factory, Action? configure = null) where TContainerBuilder : notnull @@ -397,5 +400,10 @@ public LoggingBuilder(IServiceCollection services) public IServiceCollection Services { get; } } + + private sealed class MetricsBuilder(IServiceCollection services) : IMetricsBuilder + { + public IServiceCollection Services { get; } = services; + } } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs index 596c9af102f593..7a965e0efc85f5 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs @@ -328,6 +328,7 @@ internal static void PopulateServiceCollection( }); services.AddOptions().Configure(options => { options.Initialize(hostBuilderContext.Configuration); }); services.AddLogging(); + services.AddMetrics(); } [MemberNotNull(nameof(_appServices))] diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index 87f5f2d141daab..80447a8ccae0ae 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -315,6 +315,11 @@ internal static void AddDefaultServices(HostBuilderContext hostingContext, IServ ActivityTrackingOptions.ParentId; }); }); + + services.AddMetrics(metrics => + { + metrics.AddConfiguration(hostingContext.Configuration.GetSection("Metrics")); + }); } internal static ServiceProviderOptions CreateDefaultServiceProviderOptions(HostBuilderContext context) @@ -393,7 +398,26 @@ public static Task RunConsoleAsync(this IHostBuilder hostBuilder, Action configureMetrics) => throw null!; - public static IHostBuilder ConfigureMetrics(this IHostBuilder hostBuilder, Action configureMetrics) => throw null!; + /// + /// Adds a delegate for configuring the provided . This may be called multiple times. + /// + /// The to configure. + /// The delegate that configures the . + /// The same instance of the for chaining. + public static IHostBuilder ConfigureMetrics(this IHostBuilder hostBuilder, Action configureMetrics) + { + return hostBuilder.ConfigureServices((context, collection) => collection.AddMetrics(builder => configureMetrics(builder))); + } + + /// + /// Adds a delegate for configuring the provided . This may be called multiple times. + /// + /// The to configure. + /// The delegate that configures the . + /// The same instance of the for chaining. + public static IHostBuilder ConfigureMetrics(this IHostBuilder hostBuilder, Action configureMetrics) + { + return hostBuilder.ConfigureServices((context, collection) => collection.AddMetrics(builder => configureMetrics(context, builder))); + } } } From f599cb92c2a56a74e75be7c841180bafbe2d87cc Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 19 Jul 2023 15:48:59 -0700 Subject: [PATCH 07/50] Rules --- ...oft.Extensions.Diagnostics.Abstractions.cs | 34 +------ .../src/Metrics/InstrumentEnableRule.cs | 13 ++- .../Metrics/MetricsBuilderEnableExtensions.cs | 41 -------- .../src/Metrics/MetricsEnableOptions.cs | 2 +- .../ref/Microsoft.Extensions.Diagnostics.cs | 29 ++++++ .../Metrics/MetricsBuilderEnableExtensions.cs | 97 +++++++++++++++++++ .../Microsoft.Extensions.Diagnostics.csproj | 2 + 7 files changed, 136 insertions(+), 82 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 37ddafe14a79c6..ab68420b6c949a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -26,7 +26,7 @@ public interface IMetricsSource } public class InstrumentEnableRule { - public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Action filter) { } + public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Func? filter) { } public string? ListenerName { get; } public string? MeterName { get; } public MeterScope Scopes { get; } @@ -39,38 +39,6 @@ public enum MeterScope Global, Local } - public static class MetricsBuilderEnableExtensions - { - // common overloads - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener => throw null!; - - // less common overloads - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; - - // all the same extension methods on MetricsEnableOptions - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; - } public static class MetricsBuilderExtensions { public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs index 9d601d4bde9135..ad86965c146b05 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs @@ -6,13 +6,12 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - public class InstrumentEnableRule + public class InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Func? filter) { - public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Action filter) { } - public string? ListenerName { get; } - public string? MeterName { get; } - public MeterScope Scopes { get; } - public string? InstrumentName { get; } - public Func? Filter { get; } + public string? ListenerName { get; } = listenerName; + public string? MeterName { get; } = meterName; + public MeterScope Scopes { get; } = scopes; + public string? InstrumentName { get; } = instrumentName; + public Func? Filter { get; } = filter; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs deleted file mode 100644 index 55dbd5e93fa96c..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics.Metrics; - -namespace Microsoft.Extensions.Diagnostics.Metrics -{ - public static class MetricsBuilderEnableExtensions - { - // common overloads - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener => throw null!; - - // less common overloads - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Action filter) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; - - // all the same extension methods on MetricsEnableOptions - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Action filter) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Action filter) where T : IMetricsListener => throw null!; - } -} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs index d281116d4e4fed..247e4073adc619 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs @@ -7,6 +7,6 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public class MetricsEnableOptions { - public IList Rules { get; } = null!; + public IList Rules { get; } = new List(); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index da9151fb8d53cd..077040337441e2 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; + namespace Microsoft.Extensions.DependencyInjection { public static class MetricsServiceExtensions @@ -15,4 +17,31 @@ public static class MetricsBuilderConsoleExtensions { public static IMetricsBuilder AddConsole(this IMetricsBuilder builder) => throw null!; } + public static class MetricsBuilderEnableExtensions + { + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) where T : IMetricsListener => throw null!; + } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs new file mode 100644 index 00000000000000..0ddfde78798eae --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs @@ -0,0 +1,97 @@ +// 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.Metrics; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public static class MetricsBuilderEnableExtensions + { + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) + => builder.ConfigureRule(options => options.EnableMetrics(meterName)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener + => builder.ConfigureRule(options => options.EnableMetrics(meterName)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) + => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener + => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) + => builder.ConfigureRule(options => options.EnableMetrics(filter)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener + => builder.ConfigureRule(options => options.EnableMetrics(filter)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) + => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener + => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) + => builder.ConfigureRule(options => options.EnableMetrics(meterName, filter)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) where T : IMetricsListener + => builder.ConfigureRule(options => options.EnableMetrics(meterName, filter)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) + => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes, filter)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) where T : IMetricsListener + => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes, filter)); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => options.AddRule(meterName: meterName); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener + => options.AddRule(meterName: meterName, listenerName: typeof(T).FullName); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) + => options.AddRule(meterName: meterName, instrumentName: instrumentName); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener + => options.AddRule(meterName: meterName, instrumentName: instrumentName, listenerName: typeof(T).FullName); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) + => options.AddRule(filter: (_, instrument) => filter(instrument)); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener + => options.AddRule(filter: (_, instrument) => filter(instrument), listenerName: typeof(T).FullName); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) + => options.AddRule(meterName: meterName, scopes: scopes); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener + => options.AddRule(meterName: meterName, scopes: scopes, listenerName: typeof(T).FullName); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) + => options.AddRule(meterName: meterName, filter: (_, instrument) => filter(instrument)); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) where T : IMetricsListener + => options.AddRule(meterName: meterName, filter: (_, instrument) => filter(instrument), listenerName: typeof(T).FullName); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) + => options.AddRule(meterName: meterName, scopes: scopes, filter: (_, instrument) => filter(instrument)); + + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) + where T : IMetricsListener => options.AddRule(meterName: meterName, scopes: scopes, filter: (_, instrument) => filter(instrument)); + + private static IMetricsBuilder ConfigureRule(this IMetricsBuilder builder, Action configureOptions) + { + builder.Services.Configure(configureOptions); + return builder; + } + + private static MetricsEnableOptions AddRule(this MetricsEnableOptions options, string? listenerName = null, string? meterName = null, MeterScope scopes = MeterScope.Local, + string? instrumentName = null, Func? filter = null) + { + ThrowHelper.ThrowIfNull(options); + options.Rules.Add(new InstrumentEnableRule(listenerName, meterName, scopes, instrumentName, filter)); + return options; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index 0f054980127722..35f56365072d2f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -13,10 +13,12 @@ + + From 97aee1ffda4ea2f4c021983676aef8d2cb70f564 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 24 Jul 2023 15:42:36 -0700 Subject: [PATCH 08/50] Wire up the factory --- ...oft.Extensions.Diagnostics.Abstractions.cs | 1 + .../src/Metrics/IMetricsListener.cs | 1 + .../src/Metrics/InstrumentEnableRule.cs | 4 + .../ref/Microsoft.Extensions.Diagnostics.cs | 11 + .../Microsoft.Extensions.Diagnostics.csproj | 4 + .../src/Metrics/ConsoleMetricListener.cs | 57 +++++ .../src/Metrics/DefaultMeterFactory.cs | 240 +++++++++++++++++- .../MetricsBuilderConsoleExtensions.cs | 11 +- .../src/Metrics/MetricsServiceExtensions.cs | 12 +- .../Microsoft.Extensions.Diagnostics.csproj | 4 + .../tests/ConsoleMetricListenerTests.cs | 39 +++ ...Tests.cs => DefaultMetricsFactoryTests.cs} | 10 +- 12 files changed, 373 insertions(+), 21 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs rename src/libraries/Microsoft.Extensions.Diagnostics/tests/{MetricsTests.cs => DefaultMetricsFactoryTests.cs} (97%) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index ab68420b6c949a..94fdc8d11425d0 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -15,6 +15,7 @@ public interface IMetricsBuilder } public interface IMetricsListener { + public string Name { get; } public void SetSource(IMetricsSource source); public object? InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument); public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs index d1bca89f5269a2..365d7887500b2e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs @@ -7,6 +7,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public interface IMetricsListener { + public string Name { get; } public void SetSource(IMetricsSource source); public object? InstrumentPublished(Instrument instrument); public void MeasurementsCompleted(Instrument instrument, object? userState); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs index ad86965c146b05..edcb131b2885f6 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs @@ -12,6 +12,10 @@ public class InstrumentEnableRule(string? listenerName, string? meterName, Meter public string? MeterName { get; } = meterName; public MeterScope Scopes { get; } = scopes; public string? InstrumentName { get; } = instrumentName; + + /// + /// A filter callback that takes a listener name and can be used to enable or disable an instrument. + /// public Func? Filter { get; } = filter; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 077040337441e2..019237b3530045 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.IO; namespace Microsoft.Extensions.DependencyInjection { @@ -44,4 +45,14 @@ public static class MetricsBuilderEnableExtensions public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) => throw null!; public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) where T : IMetricsListener => throw null!; } + public sealed class ConsoleMetricListener : IMetricsListener, IDisposable + { + internal TextWriter _textWriter; + public string Name { get; } + public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct => throw new NotImplementedException(); + public object? InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument) => throw new NotImplementedException(); + public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState) => throw new NotImplementedException(); + public void SetSource(IMetricsSource source) => throw new NotImplementedException(); + public void Dispose() => throw new NotImplementedException(); + } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj index 98f6966eb8e54e..96a403f6b9780a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj @@ -8,6 +8,10 @@ + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs new file mode 100644 index 00000000000000..51907a540754ad --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -0,0 +1,57 @@ +// 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.Metrics; +using System.IO; +using System.Threading; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public sealed class ConsoleMetricListener : IMetricsListener, IDisposable + { + internal TextWriter _textWriter = Console.Out; + private IMetricsSource? _source; + private Timer _timer; + private int _instrumentCount; + + public ConsoleMetricListener() + { + _timer = new Timer(OnTimer); + } + + public string Name => "Console"; + + public object? InstrumentPublished(Instrument instrument) + { + if (Interlocked.Increment(ref _instrumentCount) == 1) + { + _timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); + } + return null; + } + + public void MeasurementsCompleted(Instrument instrument, object? userState) + { + if (Interlocked.Decrement(ref _instrumentCount) == 0) + { + _timer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + + public void SetSource(IMetricsSource source) => _source = source; + public MeasurementCallback GetMeasurementHandler() where T : struct => MeasurementHandler; + + private void MeasurementHandler(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct + { + _textWriter.WriteLine($"{instrument.Meter.Name}-{instrument.Name} {measurement} {instrument.Unit}"); + } + + private void OnTimer(object? _) + { + _source?.RecordObservableInstruments(); + } + + public void Dispose() => _timer.Dispose(); + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs index dd1be2d1512d3a..183048a3cb852c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs @@ -2,18 +2,63 @@ // 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 Microsoft.Extensions.Options; namespace Microsoft.Extensions.Diagnostics.Metrics { - internal sealed class DefaultMeterFactory : IMeterFactory + internal sealed class DefaultMeterFactory : IMeterFactory, IMetricsSource { private readonly Dictionary> _cachedMeters = new(); + private readonly MeterListener _meterListener; + private readonly IMetricsListener[] _listeners; + private readonly IDisposable? _changeTokenRegistration; + private readonly ConcurrentDictionary> _connections = new(); + private IList _rules; private bool _disposed; - public DefaultMeterFactory() { } + public DefaultMeterFactory(IEnumerable listeners, IOptionsMonitor options) + { + _listeners = listeners.ToArray(); + _changeTokenRegistration = options.OnChange(UpdateRules); + UpdateRules(options.CurrentValue, name: null); + + _meterListener = new MeterListener() + { + InstrumentPublished = InstrumentPublished, + MeasurementsCompleted = MeasurementsCompleted, + }; + RegisterCallbacks(); + _meterListener.Start(); + } + + private void RegisterCallbacks() + { + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + } + + private void MeasurementCallback(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct + { + if (!_connections.TryGetValue(instrument, out ConcurrentDictionary? listeners) || listeners.IsEmpty) + { + return; + } + foreach (var pair in listeners) + { + pair.Key.GetMeasurementHandler().Invoke(instrument, measurement, tags, pair.Value); + } + } public Meter Create(MeterOptions options) { @@ -62,6 +107,166 @@ public Meter Create(MeterOptions options) } } + [MemberNotNull(nameof(_rules))] + private void UpdateRules(MetricsEnableOptions options, string? name) + { + lock (_connections) + { + _rules = options.Rules; + + if (_disposed) + { + return; + } + + foreach (var pair in _connections) + { + RefreshConnections(pair.Key, pair.Value); + } + } + } + + private void InstrumentPublished(Instrument instrument, MeterListener listener) + { + lock (_connections) + { + if (_disposed) + { + return; + } + + if (_connections.ContainsKey(instrument)) + { + Debug.Assert(false, "InstrumentPublished called twice for the same instrument"); + return; + } + + var listeners = _connections.GetOrAdd(instrument, static _ => new()); + RefreshConnections(instrument, listeners); + } + } + + // Called under _connections lock + private void RefreshConnections(Instrument instrument, ConcurrentDictionary listeners) + { + // Find any that match, pair them + var newListeners = new HashSet(); + var alreadyListening = !listeners.IsEmpty; + foreach (var rule in _rules) + { + if (Matches(rule, instrument)) + { + foreach (var listener in _listeners) + { + if (Matches(rule, instrument, listener)) + { + newListeners.Add(listener); + } + } + } + } + + // Remove any that are no longer needed + foreach (var pair in listeners) + { + if (!newListeners.Contains(pair.Key)) + { + listeners.TryRemove(pair.Key, out var _); + pair.Key.MeasurementsCompleted(instrument, pair.Value); + } + } + + // Add new ones + foreach (var listener in newListeners) + { + if (!listeners.ContainsKey(listener)) + { + var state = listener.InstrumentPublished(instrument); + listeners.GetOrAdd(listener, state); + } + } + + if (!alreadyListening && !listeners.IsEmpty) + { + _meterListener.EnableMeasurementEvents(instrument); + } + else if (alreadyListening && listeners.IsEmpty) + { + _meterListener.DisableMeasurementEvents(instrument); + } + } + + private static bool Matches(InstrumentEnableRule rule, Instrument instrument) + { + if (!string.IsNullOrEmpty(rule.InstrumentName) && + !string.Equals(rule.InstrumentName, instrument.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!string.IsNullOrEmpty(rule.MeterName) && + !string.Equals(rule.MeterName, instrument.Meter.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // TODO: Scopes? + + return true; + } + + private static bool Matches(InstrumentEnableRule rule, Instrument instrument, IMetricsListener listener) + { + if (rule.Filter != null) + { + return rule.Filter(listener.Name, instrument); + } + + if (string.IsNullOrEmpty(rule.ListenerName)) + { + return true; + } + + return string.Equals(rule.ListenerName, listener.Name, StringComparison.OrdinalIgnoreCase) + || string.Equals(rule.ListenerName, listener.GetType().FullName, StringComparison.OrdinalIgnoreCase); + } + + private void MeasurementsCompleted(Instrument instrument, object? state) + { + lock (_connections) + { + if (_disposed) + { + return; + } + + if (_connections.TryRemove(instrument, out var listeners)) + { + foreach (var pair in listeners) + { + pair.Key.MeasurementsCompleted(instrument, pair.Value); + } + } + else + { + Debug.Assert(false, "InstrumentPublished was not called for this instrument"); + } + } + } + + public void RecordObservableInstruments() + { + foreach (var pair in _connections) + { + var instrument = pair.Key; + if (instrument.IsObservable) + { + // TODO: We can't downcast because we don't know what the T is. + // var = instrument as ObservableInstrument; + } + } + } + public void Dispose() { lock (_cachedMeters) @@ -83,21 +288,32 @@ public void Dispose() _cachedMeters.Clear(); } + + lock (_connections) + { + foreach (var instrumentPair in _connections) + { + foreach (var listenerPair in instrumentPair.Value) + { + listenerPair.Key.MeasurementsCompleted(instrumentPair.Key, listenerPair.Value); + } + } + } } - } - internal sealed class FactoryMeter : Meter - { - public FactoryMeter(string name, string? version, IEnumerable>? tags, object? scope) - : base(name, version, tags, scope) + internal sealed class FactoryMeter : Meter { - } + public FactoryMeter(string name, string? version, IEnumerable>? tags, object? scope) + : base(name, version, tags, scope) + { + } - public void Release() => base.Dispose(true); // call the protected Dispose(bool) + public void Release() => base.Dispose(true); // call the protected Dispose(bool) - protected override void Dispose(bool disposing) - { - // no-op, disallow users from disposing of the meters created from the factory. + protected override void Dispose(bool disposing) + { + // no-op, disallow users from disposing of the meters created from the factory. + } } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs index d3537f4db29b67..6a1e8ce5bf86e4 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs @@ -1,6 +1,10 @@ // 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 Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + namespace Microsoft.Extensions.Diagnostics.Metrics { /// @@ -13,6 +17,11 @@ public static class MetricsBuilderConsoleExtensions /// /// The metrics builder. /// The original metrics builder for chaining. - public static IMetricsBuilder AddConsole(this IMetricsBuilder builder) => throw null!; + public static IMetricsBuilder AddConsole(this IMetricsBuilder builder) + { + ThrowHelper.ThrowIfNull(builder); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + return builder; + } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs index bded174408a97a..1dcbea921d0fcd 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs @@ -25,6 +25,8 @@ public static IServiceCollection AddMetrics(this IServiceCollection services) throw new ArgumentNullException(nameof(services)); } + services.AddOptions(); + services.TryAddSingleton(); return services; @@ -45,7 +47,15 @@ public static IServiceCollection AddMetrics(this IServiceCollection services, Ac services.AddMetrics(); - throw new NotImplementedException(); + var builder = new MetricsBuilder(services); + configure(builder); + + return services; + } + + private sealed class MetricsBuilder(IServiceCollection services) : IMetricsBuilder + { + public IServiceCollection Services { get; } = services; } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index 35f56365072d2f..253ee5d87ada76 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -11,6 +11,10 @@ This package includes the default implementation of IMeterFactory and additional extension methods to easily register it with the Dependency Injection framework. + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs new file mode 100644 index 00000000000000..0bc96dd8e3156e --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -0,0 +1,39 @@ +// 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; +using System.Diagnostics.Metrics; +using System.IO; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Tests +{ + public class ConsoleMetricListenerTests + { + [Fact] + public void ListenerCanBeRegisteredViaDi() + { + ServiceCollection services = new ServiceCollection(); + services.AddMetrics(builder => + { + builder.AddConsole(); + builder.EnableMetrics("TestMeter"); + }); + using var sp = services.BuildServiceProvider(); + + var listener = sp.GetRequiredService(); + var consoleListener = Assert.IsType(listener); + var output = new StringWriter(); + consoleListener._textWriter = output; + + var factory = sp.GetRequiredService(); + var meter = factory.Create("TestMeter", "version", new TagList() { { "key1", "value1" }, { "key2", "value2" } }); + var counter = meter.CreateCounter("counter", "blip"); + counter.Add(1); + + Assert.Equal("TestMeter-counter 1 blip" + Environment.NewLine, output.ToString()); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/MetricsTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DefaultMetricsFactoryTests.cs similarity index 97% rename from src/libraries/Microsoft.Extensions.Diagnostics/tests/MetricsTests.cs rename to src/libraries/Microsoft.Extensions.Diagnostics/tests/DefaultMetricsFactoryTests.cs index 8594e1c7692ca1..f73b197136bd7e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/MetricsTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DefaultMetricsFactoryTests.cs @@ -1,22 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.RemoteExecutor; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Diagnostics.Metrics; using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Metrics; using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Xunit; namespace Microsoft.Extensions.Diagnostics.Metrics.Tests { - public class MetricsTests + public class DefaultMetricsFactoryTests { [Fact] public void FactoryDITest() From 646fe7cea422ae8fb6440662155749350f944c86 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 25 Jul 2023 10:21:11 -0700 Subject: [PATCH 09/50] Revert non-CLS Compliance --- .../ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj | 1 - .../src/Microsoft.Extensions.Diagnostics.Abstractions.csproj | 3 +-- .../ref/Microsoft.Extensions.Diagnostics.Configuration.csproj | 1 - .../src/Microsoft.Extensions.Diagnostics.Configuration.csproj | 1 - .../src/Microsoft.Extensions.Diagnostics.csproj | 2 ++ 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj index 0725cc36abad0e..2430dd2a54f721 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -1,7 +1,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) - false diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj index c6bd6bc54df5eb..68c5605339092e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -4,9 +4,8 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true true - false - CS1591 + $(NoWarn);CS1591 diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj index 38941813e02b0e..dff8b4cce8bd7b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) - false diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj index 3b938f6c60f466..afab95956105c2 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj @@ -3,7 +3,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true - false CS1591 + $(NoWarn);CS1591 From 7b90ec29a808a26bebc70430f55998cc4ca4ebbf Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 25 Jul 2023 11:21:18 -0700 Subject: [PATCH 10/50] Feedback, scopes --- .../src/Metrics/ConsoleMetricListener.cs | 20 ++--------- .../src/Metrics/DefaultMeterFactory.cs | 33 ++++++++----------- .../MetricsBuilderConsoleExtensions.cs | 2 +- .../Metrics/MetricsBuilderEnableExtensions.cs | 6 ++-- .../tests/ConsoleMetricListenerTests.cs | 2 +- 5 files changed, 21 insertions(+), 42 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index 51907a540754ad..24e34cde3bd63d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -13,31 +13,17 @@ public sealed class ConsoleMetricListener : IMetricsListener, IDisposable internal TextWriter _textWriter = Console.Out; private IMetricsSource? _source; private Timer _timer; - private int _instrumentCount; public ConsoleMetricListener() { - _timer = new Timer(OnTimer); + _timer = new Timer(OnTimer, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); } public string Name => "Console"; - public object? InstrumentPublished(Instrument instrument) - { - if (Interlocked.Increment(ref _instrumentCount) == 1) - { - _timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); - } - return null; - } + public object? InstrumentPublished(Instrument instrument) => null; - public void MeasurementsCompleted(Instrument instrument, object? userState) - { - if (Interlocked.Decrement(ref _instrumentCount) == 0) - { - _timer.Change(Timeout.Infinite, Timeout.Infinite); - } - } + public void MeasurementsCompleted(Instrument instrument, object? userState) { } public void SetSource(IMetricsSource source) => _source = source; public MeasurementCallback GetMeasurementHandler() where T : struct => MeasurementHandler; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs index 183048a3cb852c..eb3f101e219bdb 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs @@ -154,11 +154,11 @@ private void RefreshConnections(Instrument instrument, ConcurrentDictionary /// The metrics builder. /// The original metrics builder for chaining. - public static IMetricsBuilder AddConsole(this IMetricsBuilder builder) + public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) { ThrowHelper.ThrowIfNull(builder); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs index 0ddfde78798eae..f4babba60fbd8f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs @@ -78,7 +78,7 @@ public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions optio => options.AddRule(meterName: meterName, scopes: scopes, filter: (_, instrument) => filter(instrument)); public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) - where T : IMetricsListener => options.AddRule(meterName: meterName, scopes: scopes, filter: (_, instrument) => filter(instrument)); + where T : IMetricsListener => options.AddRule(meterName: meterName, scopes: scopes, filter: (_, instrument) => filter(instrument), listenerName: typeof(T).FullName); private static IMetricsBuilder ConfigureRule(this IMetricsBuilder builder, Action configureOptions) { @@ -86,8 +86,8 @@ private static IMetricsBuilder ConfigureRule(this IMetricsBuilder builder, Actio return builder; } - private static MetricsEnableOptions AddRule(this MetricsEnableOptions options, string? listenerName = null, string? meterName = null, MeterScope scopes = MeterScope.Local, - string? instrumentName = null, Func? filter = null) + private static MetricsEnableOptions AddRule(this MetricsEnableOptions options, string? meterName = null, MeterScope scopes = MeterScope.Local | MeterScope.Global, + string? instrumentName = null, Func? filter = null, string? listenerName = null) { ThrowHelper.ThrowIfNull(options); options.Rules.Add(new InstrumentEnableRule(listenerName, meterName, scopes, instrumentName, filter)); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index 0bc96dd8e3156e..ee21356797591d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -18,7 +18,7 @@ public void ListenerCanBeRegisteredViaDi() ServiceCollection services = new ServiceCollection(); services.AddMetrics(builder => { - builder.AddConsole(); + builder.AddDebugConsole(); builder.EnableMetrics("TestMeter"); }); using var sp = services.BuildServiceProvider(); From 80dfa8639be24ce884cbf20d040cd3ff32c1b807 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 26 Jul 2023 10:30:33 -0700 Subject: [PATCH 11/50] Split DefaultMeterFactory --- ...oft.Extensions.Diagnostics.Abstractions.cs | 5 + .../Metrics/IMetricsSubscriptionManager.cs | 10 + .../ref/Microsoft.Extensions.Diagnostics.cs | 2 +- .../src/Metrics/DefaultMeterFactory.cs | 213 +--------------- .../src/Metrics/MetricsServiceExtensions.cs | 1 + .../src/Metrics/MetricsSubscriptionManager.cs | 233 ++++++++++++++++++ .../src/HostingHostBuilderExtensions.cs | 1 + .../src/Internal/MetricsHostedService.cs | 27 ++ 8 files changed, 279 insertions(+), 213 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs create mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 94fdc8d11425d0..c800695046c03f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -49,4 +49,9 @@ public class MetricsEnableOptions { public IList Rules { get; } = null!; } + + public interface IMetricsSubscriptionManager + { + public void Start(); + } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs new file mode 100644 index 00000000000000..5eaf7d6c6a3094 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs @@ -0,0 +1,10 @@ +// 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.Diagnostics.Metrics +{ + public interface IMetricsSubscriptionManager + { + public void Start(); + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 019237b3530045..f6c02aed4cb503 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -16,7 +16,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public static class MetricsBuilderConsoleExtensions { - public static IMetricsBuilder AddConsole(this IMetricsBuilder builder) => throw null!; + public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => throw null!; } public static class MetricsBuilderEnableExtensions { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs index eb3f101e219bdb..e20c459fd1a8ae 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs @@ -2,64 +2,17 @@ // 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 Microsoft.Extensions.Options; namespace Microsoft.Extensions.Diagnostics.Metrics { - internal sealed class DefaultMeterFactory : IMeterFactory, IMetricsSource + internal sealed class DefaultMeterFactory : IMeterFactory { private readonly Dictionary> _cachedMeters = new(); - private readonly MeterListener _meterListener; - private readonly IMetricsListener[] _listeners; - private readonly IDisposable? _changeTokenRegistration; - private readonly ConcurrentDictionary> _connections = new(); - private IList _rules; private bool _disposed; - public DefaultMeterFactory(IEnumerable listeners, IOptionsMonitor options) - { - _listeners = listeners.ToArray(); - _changeTokenRegistration = options.OnChange(UpdateRules); - UpdateRules(options.CurrentValue, name: null); - - _meterListener = new MeterListener() - { - InstrumentPublished = InstrumentPublished, - MeasurementsCompleted = MeasurementsCompleted, - }; - RegisterCallbacks(); - _meterListener.Start(); - } - - private void RegisterCallbacks() - { - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - } - - private void MeasurementCallback(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct - { - if (!_connections.TryGetValue(instrument, out ConcurrentDictionary? listeners) || listeners.IsEmpty) - { - return; - } - foreach (var pair in listeners) - { - pair.Key.GetMeasurementHandler().Invoke(instrument, measurement, tags, pair.Value); - } - } - public Meter Create(MeterOptions options) { if (options is null) @@ -107,168 +60,6 @@ public Meter Create(MeterOptions options) } } - [MemberNotNull(nameof(_rules))] - private void UpdateRules(MetricsEnableOptions options, string? name) - { - lock (_connections) - { - _rules = options.Rules; - - if (_disposed) - { - return; - } - - foreach (var pair in _connections) - { - RefreshConnections(pair.Key, pair.Value); - } - } - } - - private void InstrumentPublished(Instrument instrument, MeterListener listener) - { - lock (_connections) - { - if (_disposed) - { - return; - } - - if (_connections.ContainsKey(instrument)) - { - Debug.Assert(false, "InstrumentPublished called twice for the same instrument"); - return; - } - - var listeners = _connections.GetOrAdd(instrument, static _ => new()); - RefreshConnections(instrument, listeners); - } - } - - // Called under _connections lock - private void RefreshConnections(Instrument instrument, ConcurrentDictionary listeners) - { - // Find any that match, pair them - var newListeners = new HashSet(); - var alreadyListening = !listeners.IsEmpty; - foreach (var rule in _rules) - { - if (RuleMatchesInstrument(rule, instrument)) - { - foreach (var listener in _listeners) - { - if (RuleMatchesListener(rule, instrument, listener)) - { - newListeners.Add(listener); - } - } - } - } - - // Remove any that are no longer needed - foreach (var pair in listeners) - { - if (!newListeners.Contains(pair.Key)) - { - listeners.TryRemove(pair.Key, out var _); - pair.Key.MeasurementsCompleted(instrument, pair.Value); - } - } - - // Add new ones - foreach (var listener in newListeners) - { - if (!listeners.ContainsKey(listener)) - { - var state = listener.InstrumentPublished(instrument); - listeners.GetOrAdd(listener, state); - } - } - - if (!alreadyListening && !listeners.IsEmpty) - { - _meterListener.EnableMeasurementEvents(instrument); - } - else if (alreadyListening && listeners.IsEmpty) - { - _meterListener.DisableMeasurementEvents(instrument); - } - } - - private bool RuleMatchesInstrument(InstrumentEnableRule rule, Instrument instrument) - { - if (!string.IsNullOrEmpty(rule.InstrumentName) && - !string.Equals(rule.InstrumentName, instrument.Name, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (!string.IsNullOrEmpty(rule.MeterName)) - { - // TODO: StartsWith. E.g. "System.Net.Http" or "System.Net.Http.*" should match "System.Net.Http.SocketsHttpHandler" and "System.Net.Http.HttpClient". - if (!string.Equals(rule.MeterName, instrument.Meter.Name, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - return rule.Scopes.HasFlag(MeterScope.Global) && instrument.Meter.Scope == null - || rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope == this; - } - - private static bool RuleMatchesListener(InstrumentEnableRule rule, Instrument instrument, IMetricsListener listener) - { - if (rule.Filter != null) - { - return rule.Filter(listener.Name, instrument); - } - - if (string.IsNullOrEmpty(rule.ListenerName)) - { - return true; - } - - return string.Equals(rule.ListenerName, listener.Name, StringComparison.OrdinalIgnoreCase) - || string.Equals(rule.ListenerName, listener.GetType().FullName, StringComparison.OrdinalIgnoreCase); - } - - private void MeasurementsCompleted(Instrument instrument, object? state) - { - lock (_connections) - { - if (_disposed) - { - return; - } - - if (_connections.TryRemove(instrument, out var listeners)) - { - foreach (var pair in listeners) - { - pair.Key.MeasurementsCompleted(instrument, pair.Value); - } - } - else - { - Debug.Assert(false, "InstrumentPublished was not called for this instrument"); - } - } - } - - public void RecordObservableInstruments() - { - foreach (var pair in _connections) - { - var instrument = pair.Key; - if (instrument.IsObservable) - { - // TODO: We can't downcast because we don't know what the T is. - // var = instrument as ObservableInstrument; - } - } - } - public void Dispose() { lock (_cachedMeters) @@ -290,8 +81,6 @@ public void Dispose() _cachedMeters.Clear(); } - - _meterListener.Dispose(); } internal sealed class FactoryMeter : Meter diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs index 1dcbea921d0fcd..628df89240f9e4 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs @@ -28,6 +28,7 @@ public static IServiceCollection AddMetrics(this IServiceCollection services) services.AddOptions(); services.TryAddSingleton(); + services.TryAddSingleton(); return services; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs new file mode 100644 index 00000000000000..d783913c404e9f --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs @@ -0,0 +1,233 @@ +// 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 Microsoft.Extensions.Options; +using static Microsoft.Extensions.Diagnostics.Metrics.DefaultMeterFactory; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + internal class MetricsSubscriptionManager : IMetricsSubscriptionManager + { + private readonly MeterListener _meterListener; + private readonly IMetricsListener[] _listeners; + private readonly IDisposable? _changeTokenRegistration; + private readonly ConcurrentDictionary> _connections = new(); + private IList _rules; + private bool _disposed; + + public MetricsSubscriptionManager(IEnumerable listeners, IOptionsMonitor options) + { + _listeners = listeners.ToArray(); + _changeTokenRegistration = options.OnChange(UpdateRules); + UpdateRules(options.CurrentValue, name: null); + + _meterListener = new MeterListener() + { + InstrumentPublished = InstrumentPublished, + MeasurementsCompleted = MeasurementsCompleted, + }; + RegisterCallbacks(); + _meterListener.Start(); + } + + private void RegisterCallbacks() + { + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + _meterListener.SetMeasurementEventCallback(MeasurementCallback); + } + + private void MeasurementCallback(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct + { + if (!_connections.TryGetValue(instrument, out ConcurrentDictionary? listeners) || listeners.IsEmpty) + { + return; + } + foreach (var pair in listeners) + { + pair.Key.GetMeasurementHandler().Invoke(instrument, measurement, tags, pair.Value); + } + } + + + [MemberNotNull(nameof(_rules))] + private void UpdateRules(MetricsEnableOptions options, string? name) + { + lock (_connections) + { + _rules = options.Rules; + + if (_disposed) + { + return; + } + + foreach (var pair in _connections) + { + RefreshConnections(pair.Key, pair.Value); + } + } + } + + private void InstrumentPublished(Instrument instrument, MeterListener listener) + { + lock (_connections) + { + if (_disposed) + { + return; + } + + if (_connections.ContainsKey(instrument)) + { + Debug.Assert(false, "InstrumentPublished called twice for the same instrument"); + return; + } + + var listeners = _connections.GetOrAdd(instrument, static _ => new()); + RefreshConnections(instrument, listeners); + } + } + + // Called under _connections lock + private void RefreshConnections(Instrument instrument, ConcurrentDictionary listeners) + { + // Find any that match, pair them + var newListeners = new HashSet(); + var alreadyListening = !listeners.IsEmpty; + foreach (var rule in _rules) + { + if (RuleMatchesInstrument(rule, instrument)) + { + foreach (var listener in _listeners) + { + if (RuleMatchesListener(rule, instrument, listener)) + { + newListeners.Add(listener); + } + } + } + } + + // Remove any that are no longer needed + foreach (var pair in listeners) + { + if (!newListeners.Contains(pair.Key)) + { + listeners.TryRemove(pair.Key, out var _); + pair.Key.MeasurementsCompleted(instrument, pair.Value); + } + } + + // Add new ones + foreach (var listener in newListeners) + { + if (!listeners.ContainsKey(listener)) + { + var state = listener.InstrumentPublished(instrument); + listeners.GetOrAdd(listener, state); + } + } + + if (!alreadyListening && !listeners.IsEmpty) + { + _meterListener.EnableMeasurementEvents(instrument); + } + else if (alreadyListening && listeners.IsEmpty) + { + _meterListener.DisableMeasurementEvents(instrument); + } + } + + private bool RuleMatchesInstrument(InstrumentEnableRule rule, Instrument instrument) + { + if (!string.IsNullOrEmpty(rule.InstrumentName) && + !string.Equals(rule.InstrumentName, instrument.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!string.IsNullOrEmpty(rule.MeterName)) + { + // TODO: StartsWith. E.g. "System.Net.Http" or "System.Net.Http.*" should match "System.Net.Http.SocketsHttpHandler" and "System.Net.Http.HttpClient". + if (!string.Equals(rule.MeterName, instrument.Meter.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + + return rule.Scopes.HasFlag(MeterScope.Global) && instrument.Meter.Scope == null + || rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope == this; + } + + private static bool RuleMatchesListener(InstrumentEnableRule rule, Instrument instrument, IMetricsListener listener) + { + if (rule.Filter != null) + { + return rule.Filter(listener.Name, instrument); + } + + if (string.IsNullOrEmpty(rule.ListenerName)) + { + return true; + } + + return string.Equals(rule.ListenerName, listener.Name, StringComparison.OrdinalIgnoreCase) + || string.Equals(rule.ListenerName, listener.GetType().FullName, StringComparison.OrdinalIgnoreCase); + } + + private void MeasurementsCompleted(Instrument instrument, object? state) + { + lock (_connections) + { + if (_disposed) + { + return; + } + + if (_connections.TryRemove(instrument, out var listeners)) + { + foreach (var pair in listeners) + { + pair.Key.MeasurementsCompleted(instrument, pair.Value); + } + } + else + { + Debug.Assert(false, "InstrumentPublished was not called for this instrument"); + } + } + } + + public void RecordObservableInstruments() + { + foreach (var pair in _connections) + { + var instrument = pair.Key; + if (instrument.IsObservable) + { + // TODO: We can't downcast because we don't know what the T is. + // var = instrument as ObservableInstrument; + } + } + } + + public void Dispose() + { + _disposed = true; + } + + public void Start() { } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index 80447a8ccae0ae..07a3f63c6d284c 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -320,6 +320,7 @@ internal static void AddDefaultServices(HostBuilderContext hostingContext, IServ { metrics.AddConfiguration(hostingContext.Configuration.GetSection("Metrics")); }); + services.AddHostedService(); } internal static ServiceProviderOptions CreateDefaultServiceProviderOptions(HostBuilderContext context) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs new file mode 100644 index 00000000000000..5e0bd09ce0a081 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Diagnostics.Metrics; + +namespace Microsoft.Extensions.Hosting.Internal +{ + internal class MetricsHostedService : IHostedService + { + private readonly IMetricSubscriptionManager _manager; + + public MetricsHostedService(IMetricSubscriptionManager manager) + { + _manager = manager; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _manager.Start(); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } +} From 4d789be060e57fa8a6db5fa82fe0aaf3a437e7d3 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 26 Jul 2023 13:46:14 -0700 Subject: [PATCH 12/50] Remove hosting CLS compliance --- .../ref/Microsoft.Extensions.Hosting.Abstractions.csproj | 1 - .../src/Microsoft.Extensions.Hosting.Abstractions.csproj | 1 - .../ref/Microsoft.Extensions.Hosting.csproj | 1 - .../src/Microsoft.Extensions.Hosting.csproj | 1 - 4 files changed, 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj index 460d22f7a0c1ae..96046ae4010396 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/ref/Microsoft.Extensions.Hosting.Abstractions.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) $(NoWarn);CS0618 - false diff --git a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj index 55cbc03238b809..c2a3841ec4902f 100644 --- a/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/Microsoft.Extensions.Hosting.Abstractions.csproj @@ -5,7 +5,6 @@ Microsoft.Extensions.Hosting true true - false Hosting and startup abstractions for applications. diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj index 997fe5d0af1003..634b13f75c45d3 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.1;netstandard2.0;$(NetFrameworkMinimum) $(NoWarn);CS0618 - false diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index a464d86b6769da..f70223b68e86c0 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -5,7 +5,6 @@ true Hosting and startup infrastructures for applications. true - false From 3bf5e561c4cd2dda639723450c3958fb92cc58f3 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 26 Jul 2023 13:46:40 -0700 Subject: [PATCH 13/50] Split out listener subscriptions. --- .../src/Metrics/ListenerSubscription.cs | 188 ++++++++++++++++ .../src/Metrics/MetricsSubscriptionManager.cs | 212 ++---------------- .../tests/ConsoleMetricListenerTests.cs | 1 + 3 files changed, 213 insertions(+), 188 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs new file mode 100644 index 00000000000000..5c022b5fd68338 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -0,0 +1,188 @@ +// 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.Metrics; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + internal class ListenerSubscription : IMetricsSource, IDisposable + { + private readonly MeterListener _meterListener; + private readonly IMetricsListener _metricsListener; + private readonly HashSet _instruments = new(); + private readonly HashSet _listening = new(); + private IList _rules = Array.Empty(); + private bool _disposed; + + internal ListenerSubscription(IMetricsListener metricsListener) + { + _metricsListener = metricsListener; + + _meterListener = new MeterListener(); + } + + public void Start() + { + _meterListener.InstrumentPublished = InstrumentPublished; + _meterListener.MeasurementsCompleted = MeasurementsCompleted; + _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + _meterListener.Start(); + } + + private void InstrumentPublished(Instrument instrument, MeterListener _) + { + lock (_instruments) + { + if (_disposed) + { + return; + } + + if (_instruments.Contains(instrument)) + { + Debug.Assert(false, "InstrumentPublished called twice for the same instrument"); + return; + } + + _instruments.Add(instrument); + RefreshConnection(instrument); + } + } + + private void MeasurementsCompleted(Instrument instrument, object? state) + { + lock (_instruments) + { + if (_disposed) + { + return; + } + + if (_instruments.Remove(instrument)) + { + if (_listening.Remove(instrument)) + { + _meterListener.DisableMeasurementEvents(instrument); + } + } + else + { + Debug.Assert(false, "InstrumentPublished was not called for this instrument"); + } + } + } + + internal void UpdateRules(IList rules) + { + lock (_instruments) + { + if (_disposed) + { + return; + } + + _rules = rules; + + foreach (var instrument in _instruments) + { + RefreshConnection(instrument); + } + } + } + + // Called under _instrument lock + private void RefreshConnection(Instrument instrument) + { + var alreadyListening = _listening.Contains(instrument); + var listen = false; + foreach (var rule in _rules) + { + // TODO: Most specific match + if (RuleMatches(rule, instrument)) + { + listen = true; + break; + } + } + + // Remove any that are no longer needed + if (!listen && alreadyListening) + { + _listening.Remove(instrument); + _meterListener.DisableMeasurementEvents(instrument); + } + else if (listen && !alreadyListening) + { + _meterListener.EnableMeasurementEvents(instrument); + _listening.Add(instrument); + } + } + + private bool RuleMatches(InstrumentEnableRule rule, Instrument instrument) + { + if (!string.IsNullOrEmpty(rule.InstrumentName) && + !string.Equals(rule.InstrumentName, instrument.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!string.IsNullOrEmpty(rule.MeterName)) + { + // TODO: StartsWith. E.g. "System.Net.Http" or "System.Net.Http.*" should match "System.Net.Http.SocketsHttpHandler" and "System.Net.Http.HttpClient". + if (!string.Equals(rule.MeterName, instrument.Meter.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + + if (!(rule.Scopes.HasFlag(MeterScope.Global) && instrument.Meter.Scope == null) + && !(rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope != null)) // TODO: What should we be comparing Scope to, the DefaultMeterFactory / IMeterFactory? + { + return false; + } + + /* TODO: Remove filters + if (rule.Filter != null) + { + return rule.Filter(listener.Name, instrument); + } + */ + + if (string.IsNullOrEmpty(rule.ListenerName)) + { + return true; + } + + return string.Equals(rule.ListenerName, _metricsListener.Name, StringComparison.OrdinalIgnoreCase) + || string.Equals(rule.ListenerName, _metricsListener.GetType().FullName, StringComparison.OrdinalIgnoreCase); + } + + public void RecordObservableInstruments() + { + foreach (var instrument in _listening) + { + if (instrument.IsObservable) + { + // TODO: We can't downcast because we don't know what the T is. + // var = instrument as ObservableInstrument; + } + } + } + + public void Dispose() + { + _disposed = true; + _meterListener.Dispose(); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs index d783913c404e9f..94dacc41495104 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs @@ -4,230 +4,66 @@ 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 Microsoft.Extensions.Options; -using static Microsoft.Extensions.Diagnostics.Metrics.DefaultMeterFactory; namespace Microsoft.Extensions.Diagnostics.Metrics { - internal class MetricsSubscriptionManager : IMetricsSubscriptionManager + internal partial class MetricsSubscriptionManager : IMetricsSubscriptionManager { - private readonly MeterListener _meterListener; - private readonly IMetricsListener[] _listeners; + private readonly ListenerSubscription[] _listeners; private readonly IDisposable? _changeTokenRegistration; - private readonly ConcurrentDictionary> _connections = new(); - private IList _rules; private bool _disposed; public MetricsSubscriptionManager(IEnumerable listeners, IOptionsMonitor options) { - _listeners = listeners.ToArray(); - _changeTokenRegistration = options.OnChange(UpdateRules); - UpdateRules(options.CurrentValue, name: null); - - _meterListener = new MeterListener() - { - InstrumentPublished = InstrumentPublished, - MeasurementsCompleted = MeasurementsCompleted, - }; - RegisterCallbacks(); - _meterListener.Start(); - } - - private void RegisterCallbacks() - { - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - _meterListener.SetMeasurementEventCallback(MeasurementCallback); - } - - private void MeasurementCallback(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct - { - if (!_connections.TryGetValue(instrument, out ConcurrentDictionary? listeners) || listeners.IsEmpty) - { - return; - } - foreach (var pair in listeners) - { - pair.Key.GetMeasurementHandler().Invoke(instrument, measurement, tags, pair.Value); - } - } - - - [MemberNotNull(nameof(_rules))] - private void UpdateRules(MetricsEnableOptions options, string? name) - { - lock (_connections) - { - _rules = options.Rules; - - if (_disposed) - { - return; - } - - foreach (var pair in _connections) - { - RefreshConnections(pair.Key, pair.Value); - } - } - } - - private void InstrumentPublished(Instrument instrument, MeterListener listener) - { - lock (_connections) - { - if (_disposed) - { - return; - } - - if (_connections.ContainsKey(instrument)) - { - Debug.Assert(false, "InstrumentPublished called twice for the same instrument"); - return; - } - - var listeners = _connections.GetOrAdd(instrument, static _ => new()); - RefreshConnections(instrument, listeners); - } - } - - // Called under _connections lock - private void RefreshConnections(Instrument instrument, ConcurrentDictionary listeners) - { - // Find any that match, pair them - var newListeners = new HashSet(); - var alreadyListening = !listeners.IsEmpty; - foreach (var rule in _rules) - { - if (RuleMatchesInstrument(rule, instrument)) - { - foreach (var listener in _listeners) - { - if (RuleMatchesListener(rule, instrument, listener)) - { - newListeners.Add(listener); - } - } - } - } - - // Remove any that are no longer needed - foreach (var pair in listeners) - { - if (!newListeners.Contains(pair.Key)) - { - listeners.TryRemove(pair.Key, out var _); - pair.Key.MeasurementsCompleted(instrument, pair.Value); - } - } - - // Add new ones - foreach (var listener in newListeners) - { - if (!listeners.ContainsKey(listener)) - { - var state = listener.InstrumentPublished(instrument); - listeners.GetOrAdd(listener, state); - } - } - - if (!alreadyListening && !listeners.IsEmpty) - { - _meterListener.EnableMeasurementEvents(instrument); - } - else if (alreadyListening && listeners.IsEmpty) + var list = listeners.ToList(); + _listeners = new ListenerSubscription[list.Count]; + for (int i = 0; i < _listeners.Length; i++) { - _meterListener.DisableMeasurementEvents(instrument); + _listeners[i] = new ListenerSubscription(list[i]); } + _changeTokenRegistration = options.OnChange(UpdateRules); + UpdateRules(options.CurrentValue); } - private bool RuleMatchesInstrument(InstrumentEnableRule rule, Instrument instrument) + public void Start() { - if (!string.IsNullOrEmpty(rule.InstrumentName) && - !string.Equals(rule.InstrumentName, instrument.Name, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (!string.IsNullOrEmpty(rule.MeterName)) + foreach (var listener in _listeners) { - // TODO: StartsWith. E.g. "System.Net.Http" or "System.Net.Http.*" should match "System.Net.Http.SocketsHttpHandler" and "System.Net.Http.HttpClient". - if (!string.Equals(rule.MeterName, instrument.Meter.Name, StringComparison.OrdinalIgnoreCase)) - { - return false; - } + listener.Start(); } - - return rule.Scopes.HasFlag(MeterScope.Global) && instrument.Meter.Scope == null - || rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope == this; } - private static bool RuleMatchesListener(InstrumentEnableRule rule, Instrument instrument, IMetricsListener listener) + private void UpdateRules(MetricsEnableOptions options) { - if (rule.Filter != null) + if (_disposed) { - return rule.Filter(listener.Name, instrument); + return; } + var rules = options.Rules; - if (string.IsNullOrEmpty(rule.ListenerName)) + foreach (var listener in _listeners) { - return true; + listener.UpdateRules(rules); } - - return string.Equals(rule.ListenerName, listener.Name, StringComparison.OrdinalIgnoreCase) - || string.Equals(rule.ListenerName, listener.GetType().FullName, StringComparison.OrdinalIgnoreCase); } - private void MeasurementsCompleted(Instrument instrument, object? state) + public void Dispose() { - lock (_connections) + if (_disposed) { - if (_disposed) - { - return; - } - - if (_connections.TryRemove(instrument, out var listeners)) - { - foreach (var pair in listeners) - { - pair.Key.MeasurementsCompleted(instrument, pair.Value); - } - } - else - { - Debug.Assert(false, "InstrumentPublished was not called for this instrument"); - } + return; } - } - public void RecordObservableInstruments() - { - foreach (var pair in _connections) + _disposed = true; + _changeTokenRegistration?.Dispose(); + foreach (var listener in _listeners) { - var instrument = pair.Key; - if (instrument.IsObservable) - { - // TODO: We can't downcast because we don't know what the T is. - // var = instrument as ObservableInstrument; - } + listener.Dispose(); } } - - public void Dispose() - { - _disposed = true; - } - - public void Start() { } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index ee21356797591d..7336e1adfc7fa5 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -22,6 +22,7 @@ public void ListenerCanBeRegisteredViaDi() builder.EnableMetrics("TestMeter"); }); using var sp = services.BuildServiceProvider(); + sp.GetRequiredService().Start(); var listener = sp.GetRequiredService(); var consoleListener = Assert.IsType(listener); From 74da7e1d04df403de0149b20eb93b557a8ce11b2 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 26 Jul 2023 15:22:40 -0700 Subject: [PATCH 14/50] Remove filter, add enable, replace TListener with string, internal Console Listener --- ...oft.Extensions.Diagnostics.Abstractions.cs | 4 +- .../src/Metrics/InstrumentEnableRule.cs | 11 +-- .../ref/Microsoft.Extensions.Diagnostics.cs | 29 ++----- .../src/Metrics/ConsoleMetricListener.cs | 4 +- .../Metrics/MetricsBuilderEnableExtensions.cs | 79 +++++-------------- .../tests/ConsoleMetricListenerTests.cs | 2 +- 6 files changed, 36 insertions(+), 93 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index c800695046c03f..4c5dd75e5c7803 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -27,12 +27,12 @@ public interface IMetricsSource } public class InstrumentEnableRule { - public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Func? filter) { } + public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, bool enable) { } public string? ListenerName { get; } public string? MeterName { get; } public MeterScope Scopes { get; } public string? InstrumentName { get; } - public Func? Filter { get; } + public bool Enable { get; } } [Flags] public enum MeterScope diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs index edcb131b2885f6..a04cde052bec94 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs @@ -1,21 +1,14 @@ // 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.Metrics; - namespace Microsoft.Extensions.Diagnostics.Metrics { - public class InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, Func? filter) + public class InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, bool enable) { public string? ListenerName { get; } = listenerName; public string? MeterName { get; } = meterName; public MeterScope Scopes { get; } = scopes; public string? InstrumentName { get; } = instrumentName; - - /// - /// A filter callback that takes a listener name and can be used to enable or disable an instrument. - /// - public Func? Filter { get; } = filter; + public bool Enable { get; } = enable; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index f6c02aed4cb503..28f4db73f1420a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -20,32 +20,19 @@ public static class MetricsBuilderConsoleExtensions } public static class MetricsBuilderEnableExtensions { + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder) => throw null!; public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener => throw null!; public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) where T : IMetricsListener => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) where T : IMetricsListener => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options) => throw null!; public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener => throw null!; public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) where T : IMetricsListener => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) where T : IMetricsListener => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; + public static MetricsEnableOptions DisableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; } - public sealed class ConsoleMetricListener : IMetricsListener, IDisposable + internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable { internal TextWriter _textWriter; public string Name { get; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index 24e34cde3bd63d..388cb880797275 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -8,11 +8,11 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - public sealed class ConsoleMetricListener : IMetricsListener, IDisposable + internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable { + private readonly Timer _timer; internal TextWriter _textWriter = Console.Out; private IMetricsSource? _source; - private Timer _timer; public ConsoleMetricListener() { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs index f4babba60fbd8f..52be860ef0215e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs @@ -9,76 +9,39 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public static class MetricsBuilderEnableExtensions { + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder) + => builder.ConfigureRule(options => options.EnableMetrics()); + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => builder.ConfigureRule(options => options.EnableMetrics(meterName)); - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) where T : IMetricsListener - => builder.ConfigureRule(options => options.EnableMetrics(meterName)); - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName)); - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) where T : IMetricsListener - => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) - => builder.ConfigureRule(options => options.EnableMetrics(filter)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, Func filter) where T : IMetricsListener - => builder.ConfigureRule(options => options.EnableMetrics(filter)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) - => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes) where T : IMetricsListener - => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) - => builder.ConfigureRule(options => options.EnableMetrics(meterName, filter)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, Func filter) where T : IMetricsListener - => builder.ConfigureRule(options => options.EnableMetrics(meterName, filter)); + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName) + => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName)); - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) - => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes, filter)); + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) + => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName, scopes)); - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, MeterScope scopes, Func filter) where T : IMetricsListener - => builder.ConfigureRule(options => options.EnableMetrics(meterName, scopes, filter)); + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options) + => options.AddRule(); - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => options.AddRule(meterName: meterName); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) where T : IMetricsListener - => options.AddRule(meterName: meterName, listenerName: typeof(T).FullName); + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) + => options.AddRule(meterName: meterName); public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => options.AddRule(meterName: meterName, instrumentName: instrumentName); - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) where T : IMetricsListener - => options.AddRule(meterName: meterName, instrumentName: instrumentName, listenerName: typeof(T).FullName); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) - => options.AddRule(filter: (_, instrument) => filter(instrument)); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, Func filter) where T : IMetricsListener - => options.AddRule(filter: (_, instrument) => filter(instrument), listenerName: typeof(T).FullName); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) - => options.AddRule(meterName: meterName, scopes: scopes); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes) where T : IMetricsListener - => options.AddRule(meterName: meterName, scopes: scopes, listenerName: typeof(T).FullName); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) - => options.AddRule(meterName: meterName, filter: (_, instrument) => filter(instrument)); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, Func filter) where T : IMetricsListener - => options.AddRule(meterName: meterName, filter: (_, instrument) => filter(instrument), listenerName: typeof(T).FullName); + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName) + => options.AddRule(meterName, instrumentName, listenerName); - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) - => options.AddRule(meterName: meterName, scopes: scopes, filter: (_, instrument) => filter(instrument)); + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) + => options.AddRule(meterName, instrumentName, listenerName, scopes); - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, MeterScope scopes, Func filter) - where T : IMetricsListener => options.AddRule(meterName: meterName, scopes: scopes, filter: (_, instrument) => filter(instrument), listenerName: typeof(T).FullName); + // TODO: How many overloads of this do we want? + public static MetricsEnableOptions DisableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) + => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: false); private static IMetricsBuilder ConfigureRule(this IMetricsBuilder builder, Action configureOptions) { @@ -86,11 +49,11 @@ private static IMetricsBuilder ConfigureRule(this IMetricsBuilder builder, Actio return builder; } - private static MetricsEnableOptions AddRule(this MetricsEnableOptions options, string? meterName = null, MeterScope scopes = MeterScope.Local | MeterScope.Global, - string? instrumentName = null, Func? filter = null, string? listenerName = null) + private static MetricsEnableOptions AddRule(this MetricsEnableOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, + MeterScope scopes = MeterScope.Local | MeterScope.Global, bool? enable = true) { ThrowHelper.ThrowIfNull(options); - options.Rules.Add(new InstrumentEnableRule(listenerName, meterName, scopes, instrumentName, filter)); + options.Rules.Add(new InstrumentEnableRule(listenerName, meterName, scopes, instrumentName, enable ?? true)); return options; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index 7336e1adfc7fa5..d285c5d220f243 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -19,7 +19,7 @@ public void ListenerCanBeRegisteredViaDi() services.AddMetrics(builder => { builder.AddDebugConsole(); - builder.EnableMetrics("TestMeter"); + builder.EnableMetrics("TestMeter"); }); using var sp = services.BuildServiceProvider(); sp.GetRequiredService().Start(); From 0c11591660464c49977068761a95ccecb582423a Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 26 Jul 2023 15:34:03 -0700 Subject: [PATCH 15/50] IMetric_s_SubscriptionManager --- .../src/Internal/MetricsHostedService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs index 5e0bd09ce0a081..5376f278682e1b 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs @@ -9,9 +9,9 @@ namespace Microsoft.Extensions.Hosting.Internal { internal class MetricsHostedService : IHostedService { - private readonly IMetricSubscriptionManager _manager; + private readonly IMetricsSubscriptionManager _manager; - public MetricsHostedService(IMetricSubscriptionManager manager) + public MetricsHostedService(IMetricsSubscriptionManager manager) { _manager = manager; } From 2109487e25a791d8198f423cc756322e01a6780e Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 26 Jul 2023 16:36:45 -0700 Subject: [PATCH 16/50] Better matching --- .../src/Metrics/ListenerSubscription.cs | 121 +++++++++++++----- .../src/Metrics/MetricsSubscriptionManager.cs | 3 - 2 files changed, 89 insertions(+), 35 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 5c022b5fd68338..e9e1b2cfe95d17 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -2,7 +2,6 @@ // 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.Metrics; @@ -14,7 +13,7 @@ internal class ListenerSubscription : IMetricsSource, IDisposable private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; private readonly HashSet _instruments = new(); - private readonly HashSet _listening = new(); + private readonly HashSet _enabled = new(); private IList _rules = Array.Empty(); private bool _disposed; @@ -55,7 +54,7 @@ private void InstrumentPublished(Instrument instrument, MeterListener _) } _instruments.Add(instrument); - RefreshConnection(instrument); + RefreshInstrument(instrument); } } @@ -70,7 +69,7 @@ private void MeasurementsCompleted(Instrument instrument, object? state) if (_instruments.Remove(instrument)) { - if (_listening.Remove(instrument)) + if (_enabled.Remove(instrument)) { _meterListener.DisableMeasurementEvents(instrument); } @@ -95,43 +94,66 @@ internal void UpdateRules(IList rules) foreach (var instrument in _instruments) { - RefreshConnection(instrument); + RefreshInstrument(instrument); } } } // Called under _instrument lock - private void RefreshConnection(Instrument instrument) + private void RefreshInstrument(Instrument instrument) { - var alreadyListening = _listening.Contains(instrument); - var listen = false; - foreach (var rule in _rules) + var alreadyEnabled = _enabled.Contains(instrument); + var enable = false; + var rule = GetMostSpecificRule(instrument); + if (rule != null) { - // TODO: Most specific match - if (RuleMatches(rule, instrument)) - { - listen = true; - break; - } + enable = rule.Enable; } - // Remove any that are no longer needed - if (!listen && alreadyListening) + if (!enable && alreadyEnabled) { - _listening.Remove(instrument); + _enabled.Remove(instrument); _meterListener.DisableMeasurementEvents(instrument); } - else if (listen && !alreadyListening) + else if (enable && !alreadyEnabled) { _meterListener.EnableMeasurementEvents(instrument); - _listening.Add(instrument); + _enabled.Add(instrument); + } + } + + private InstrumentEnableRule? GetMostSpecificRule(Instrument instrument) + { + InstrumentEnableRule? best = null; + foreach (var rule in _rules) + { + if (RuleMatches(rule, instrument) && IsMoreSpecific(rule, best)) + { + best = rule; + } } + + return best; } private bool RuleMatches(InstrumentEnableRule rule, Instrument instrument) { - if (!string.IsNullOrEmpty(rule.InstrumentName) && - !string.Equals(rule.InstrumentName, instrument.Name, StringComparison.OrdinalIgnoreCase)) + // Exact match or empty + if (!string.IsNullOrEmpty(rule.ListenerName) + && !string.Equals(rule.ListenerName, _metricsListener.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // Exact match or empty + if (!string.IsNullOrEmpty(rule.InstrumentName) + && !string.Equals(rule.InstrumentName, instrument.Name, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!(rule.Scopes.HasFlag(MeterScope.Global) && instrument.Meter.Scope == null) + && !(rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope != null)) // TODO: What should we be comparing Scope to, the DefaultMeterFactory / IMeterFactory? { return false; } @@ -145,31 +167,66 @@ private bool RuleMatches(InstrumentEnableRule rule, Instrument instrument) } } - if (!(rule.Scopes.HasFlag(MeterScope.Global) && instrument.Meter.Scope == null) - && !(rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope != null)) // TODO: What should we be comparing Scope to, the DefaultMeterFactory / IMeterFactory? + return true; + } + + // Everything must already match the Instrument and listener, or be blank. + // Which rule has more non-blank fields? Or longer Meter name? + // + private static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) + { + if (best == null) + { + return true; + } + + // Meter name + if (!string.IsNullOrEmpty(rule.MeterName)) + { + if (string.IsNullOrEmpty(best.MeterName)) + { + return true; + } + + // Longer is more specific. + if (rule.MeterName.Length != best.MeterName.Length) + { + return rule.MeterName.Length > best.MeterName.Length; + } + } + else if (!string.IsNullOrEmpty(best.MeterName)) { return false; } - /* TODO: Remove filters - if (rule.Filter != null) + // Instrument name + if (!string.IsNullOrEmpty(rule.InstrumentName) && string.IsNullOrEmpty(best.InstrumentName)) { - return rule.Filter(listener.Name, instrument); + return true; + } + else if (string.IsNullOrEmpty(rule.InstrumentName) && !string.IsNullOrEmpty(best.InstrumentName)) + { + return false; } - */ - if (string.IsNullOrEmpty(rule.ListenerName)) + // Listener name + if (!string.IsNullOrEmpty(rule.ListenerName) && string.IsNullOrEmpty(best.ListenerName)) { return true; } + else if (string.IsNullOrEmpty(rule.ListenerName) && !string.IsNullOrEmpty(best.ListenerName)) + { + return false; + } + + // Scopes TODO:?? - return string.Equals(rule.ListenerName, _metricsListener.Name, StringComparison.OrdinalIgnoreCase) - || string.Equals(rule.ListenerName, _metricsListener.GetType().FullName, StringComparison.OrdinalIgnoreCase); + return false; } public void RecordObservableInstruments() { - foreach (var instrument in _listening) + foreach (var instrument in _enabled) { if (instrument.IsObservable) { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs index 94dacc41495104..c4d8dbb99127a7 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs @@ -2,10 +2,7 @@ // 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.CodeAnalysis; -using System.Diagnostics.Metrics; using System.Linq; using Microsoft.Extensions.Options; From a7e1884fbad598aac00ea413d43fcde354709ca7 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 10:03:36 -0700 Subject: [PATCH 17/50] sealed --- .../src/Internal/MetricsHostedService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs index 5376f278682e1b..dd12bca549923a 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Hosting.Internal { - internal class MetricsHostedService : IHostedService + internal sealed class MetricsHostedService : IHostedService { private readonly IMetricsSubscriptionManager _manager; From f0f578b6c89775739fd5aeaefd669d203e9343ac Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 12:28:06 -0700 Subject: [PATCH 18/50] Rule heirarchy, tests --- ...oft.Extensions.Diagnostics.Abstractions.cs | 2 +- .../src/Metrics/InstrumentEnableRule.cs | 2 +- .../ref/Microsoft.Extensions.Diagnostics.cs | 5 ++ .../src/Metrics/ListenerSubscription.cs | 49 +++++++++++---- .../Metrics/MetricsBuilderEnableExtensions.cs | 2 +- .../tests/ListenerSubscriptionTests.cs | 63 +++++++++++++++++++ 6 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 4c5dd75e5c7803..5dbc9d270df82a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -27,7 +27,7 @@ public interface IMetricsSource } public class InstrumentEnableRule { - public InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, bool enable) { } + public InstrumentEnableRule(string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) { } public string? ListenerName { get; } public string? MeterName { get; } public MeterScope Scopes { get; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs index a04cde052bec94..1940ba3d8fb99c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs @@ -3,7 +3,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - public class InstrumentEnableRule(string? listenerName, string? meterName, MeterScope scopes, string? instrumentName, bool enable) + public class InstrumentEnableRule(string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) { public string? ListenerName { get; } = listenerName; public string? MeterName { get; } = meterName; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 28f4db73f1420a..5793b42e7b2ff0 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -42,4 +42,9 @@ internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable public void SetSource(IMetricsSource source) => throw new NotImplementedException(); public void Dispose() => throw new NotImplementedException(); } + internal sealed class ListenerSubscription + { + internal static bool RuleMatches(InstrumentEnableRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw new NotImplementedException(); + internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) => throw new NotImplementedException(); + } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index e9e1b2cfe95d17..3d6526d19e5aeb 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -127,7 +127,7 @@ private void RefreshInstrument(Instrument instrument) InstrumentEnableRule? best = null; foreach (var rule in _rules) { - if (RuleMatches(rule, instrument) && IsMoreSpecific(rule, best)) + if (RuleMatches(rule, instrument, _metricsListener.Name) && IsMoreSpecific(rule, best)) { best = rule; } @@ -136,11 +136,12 @@ private void RefreshInstrument(Instrument instrument) return best; } - private bool RuleMatches(InstrumentEnableRule rule, Instrument instrument) + // internal for testing + internal static bool RuleMatches(InstrumentEnableRule rule, Instrument instrument, string listenerName) { // Exact match or empty if (!string.IsNullOrEmpty(rule.ListenerName) - && !string.Equals(rule.ListenerName, _metricsListener.Name, StringComparison.OrdinalIgnoreCase)) + && !string.Equals(rule.ListenerName, listenerName, StringComparison.OrdinalIgnoreCase)) { return false; } @@ -158,22 +159,46 @@ private bool RuleMatches(InstrumentEnableRule rule, Instrument instrument) return false; } - if (!string.IsNullOrEmpty(rule.MeterName)) + // Empty + var ruleMeterName = rule.MeterName.AsSpan(); + // Rule "System.Net.*" matches meter "System.Net" and "System.Net.Http" + if (ruleMeterName.EndsWith(".*".AsSpan(), StringComparison.Ordinal)) { - // TODO: StartsWith. E.g. "System.Net.Http" or "System.Net.Http.*" should match "System.Net.Http.SocketsHttpHandler" and "System.Net.Http.HttpClient". - if (!string.Equals(rule.MeterName, instrument.Meter.Name, StringComparison.OrdinalIgnoreCase)) - { - return false; - } + ruleMeterName = ruleMeterName.Slice(0, ruleMeterName.Length - 2); + } + + // Rule "" or "*" matches everything + if (ruleMeterName.IsEmpty || ruleMeterName.Equals("*".AsSpan(), StringComparison.Ordinal)) + { + return true; + } + + // Rule "System.Net.Http" doesn't match meter "System.Net" + if (ruleMeterName.Length > instrument.Meter.Name.Length) + { + return false; + } + + // Exact match +/- ".*" + if (ruleMeterName.Equals(instrument.Meter.Name.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // Rule "System.Data" doesn't match meter "System.Net" + if (!instrument.Meter.Name.AsSpan().StartsWith(ruleMeterName, StringComparison.OrdinalIgnoreCase)) + { + return false; } - return true; + // Only allow StartsWith on segment boundaries + return instrument.Meter.Name[ruleMeterName.Length] == '.'; } // Everything must already match the Instrument and listener, or be blank. // Which rule has more non-blank fields? Or longer Meter name? - // - private static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) + // internal for testing + internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) { if (best == null) { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs index 52be860ef0215e..66bcf335d8f51c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs @@ -53,7 +53,7 @@ private static MetricsEnableOptions AddRule(this MetricsEnableOptions options, s MeterScope scopes = MeterScope.Local | MeterScope.Global, bool? enable = true) { ThrowHelper.ThrowIfNull(options); - options.Rules.Add(new InstrumentEnableRule(listenerName, meterName, scopes, instrumentName, enable ?? true)); + options.Rules.Add(new InstrumentEnableRule(meterName, instrumentName, listenerName, scopes, enable ?? true)); return options; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs new file mode 100644 index 00000000000000..9ef7207b3c6c28 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -0,0 +1,63 @@ +// 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.DotNet.RemoteExecutor; +using Microsoft.Extensions.Diagnostics.Metrics; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.Tests +{ + public class ListenerSubscriptionTests + { + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData("", "", "")] + [InlineData("*", "", "")] + [InlineData("lonG", "", "")] + [InlineData("lonG.*", "", "")] + [InlineData("lonG.sillY.meteR", "", "")] + [InlineData("lonG.sillY.meteR.*", "", "")] + [InlineData("lonG.sillY.meteR.namE", "", "")] + [InlineData("lonG.sillY.meteR.namE.*", "", "")] + [InlineData("lonG.sillY.meteR.namE", "instrumenTnamE", "listeneRnamE")] + public void RuleMatchesTest(string meterName, string instrumentName, string listenerName) + { + RemoteExecutor.Invoke((string m, string i, string l) => { + var rule = new InstrumentEnableRule(m, i, l, MeterScope.Global, enable: true); + var meter = new Meter("Long.Silly.Meter.Name"); + var instrument = meter.CreateCounter("InstrumentName"); + Assert.True(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName")); + }, meterName, instrumentName, listenerName).Dispose(); + } + + [Theory] + [MemberData(nameof(IsMoreSpecificTestData))] + public void IsMoreSpecificTest(InstrumentEnableRule rule, InstrumentEnableRule? best) + { + Assert.True(ListenerSubscription.IsMoreSpecific(rule, best)); + + if (best != null) + { + Assert.False(ListenerSubscription.IsMoreSpecific(best, rule)); + } + } + + public static IEnumerable IsMoreSpecificTestData() => new object[][] + { + // Anything is better than null + new object[] { new InstrumentEnableRule(null, null, null, MeterScope.Global, true), null }, + // Any field is better than empty + new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), + new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true), + new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true), + new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, + // Multiple fields are better than one, in order Meter, Instrument, Listener + // Longer Meter Name is better + // Scopes + }; + } +} From 74bc673fa43859625dfc8f6ece9e38bee17c0d99 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 15:10:51 -0700 Subject: [PATCH 19/50] Test cases --- .../src/Metrics/ListenerSubscription.cs | 3 +- .../tests/ListenerSubscriptionTests.cs | 78 ++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 3d6526d19e5aeb..2f4ab996e50502 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -159,7 +159,8 @@ internal static bool RuleMatches(InstrumentEnableRule rule, Instrument instrumen return false; } - // Empty + // Meter + var ruleMeterName = rule.MeterName.AsSpan(); // Rule "System.Net.*" matches meter "System.Net" and "System.Net.Http" if (ruleMeterName.EndsWith(".*".AsSpan(), StringComparison.Ordinal)) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index 9ef7207b3c6c28..6fbd8d713a7dbe 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -12,15 +12,23 @@ namespace Microsoft.Extensions.Diagnostics.Tests { public class ListenerSubscriptionTests { + // TODO: Scopes + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + // [InlineData(null, null, null)] // RemoteExecutor can't handle nulls [InlineData("", "", "")] [InlineData("*", "", "")] + [InlineData("*.*", "", "")] [InlineData("lonG", "", "")] [InlineData("lonG.*", "", "")] [InlineData("lonG.sillY.meteR", "", "")] [InlineData("lonG.sillY.meteR.*", "", "")] [InlineData("lonG.sillY.meteR.namE", "", "")] [InlineData("lonG.sillY.meteR.namE.*", "", "")] + [InlineData("", "instrumenTnamE", "")] + [InlineData("lonG.sillY.meteR.namE", "instrumenTnamE", "")] + [InlineData("", "", "listeneRnamE")] + [InlineData("lonG.sillY.meteR.namE", "", "listeneRnamE")] [InlineData("lonG.sillY.meteR.namE", "instrumenTnamE", "listeneRnamE")] public void RuleMatchesTest(string meterName, string instrumentName, string listenerName) { @@ -32,6 +40,30 @@ public void RuleMatchesTest(string meterName, string instrumentName, string list }, meterName, instrumentName, listenerName).Dispose(); } + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData("", "*", "")] + [InlineData("", "", "*")] + [InlineData("lonG.", "", "")] + [InlineData("lonG*", "", "")] + [InlineData("lonG.sil", "", "")] + [InlineData("sillY.meteR.namE", "", "")] + [InlineData("namE", "", "")] + [InlineData("*.namE", "", "")] + [InlineData("wrongMeter", "", "")] + [InlineData("wrongMeter", "InstrumentName", "")] + [InlineData("wrongMeter", "", "ListenerName")] + [InlineData("", "wrongInstrument", "")] + [InlineData("", "", "wrongListener")] + public void RuleMatchesNegativeTest(string meterName, string instrumentName, string listenerName) + { + RemoteExecutor.Invoke((string m, string i, string l) => { + var rule = new InstrumentEnableRule(m, i, l, MeterScope.Global, enable: true); + var meter = new Meter("Long.Silly.Meter.Name"); + var instrument = meter.CreateCounter("InstrumentName"); + Assert.False(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName")); + }, meterName, instrumentName, listenerName).Dispose(); + } + [Theory] [MemberData(nameof(IsMoreSpecificTestData))] public void IsMoreSpecificTest(InstrumentEnableRule rule, InstrumentEnableRule? best) @@ -48,6 +80,7 @@ public void IsMoreSpecificTest(InstrumentEnableRule rule, InstrumentEnableRule? { // Anything is better than null new object[] { new InstrumentEnableRule(null, null, null, MeterScope.Global, true), null }, + // Any field is better than empty new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, @@ -55,9 +88,50 @@ public void IsMoreSpecificTest(InstrumentEnableRule rule, InstrumentEnableRule? new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, new object[] { new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true), new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, - // Multiple fields are better than one, in order Meter, Instrument, Listener + + // Meter > Instrument > Listener + new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), + new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), + new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), + new InstrumentEnableRule(null, "instrumentName", "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true), + new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, + + // Multiple fields are better than one. + new object[] { new InstrumentEnableRule("meterName", "instrumentName", null, MeterScope.Global, true), + new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", null, "listenerName", MeterScope.Global, true), + new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), + new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, + + new object[] { new InstrumentEnableRule("meterName", "instrumentName", null, MeterScope.Global, true), + new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", null, "listenerName", MeterScope.Global, true), + new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), + new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, + + new object[] { new InstrumentEnableRule("meterName", "instrumentName", null, MeterScope.Global, true), + new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", null, "listenerName", MeterScope.Global, true), + new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), + new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, + // Longer Meter Name is better - // Scopes + new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), + new InstrumentEnableRule("*", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meterName.*", null, null, MeterScope.Global, true), + new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meter.Name", null, null, MeterScope.Global, true), + new InstrumentEnableRule("meter", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentEnableRule("meter.Name", null, null, MeterScope.Global, true), + new InstrumentEnableRule("meter.*", null, null, MeterScope.Global, true) }, + + // TODO: Scopes }; } } From 743d27f8758193ca6213cc8b904df046327aa819 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 15:27:59 -0700 Subject: [PATCH 20/50] Listener name constant --- .../ref/Microsoft.Extensions.Diagnostics.cs | 4 ++++ .../src/Metrics/ConsoleMetricListener.cs | 2 +- .../src/Metrics/ConsoleMetrics.cs | 10 ++++++++++ .../tests/ConsoleMetricListenerTests.cs | 2 +- 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 5793b42e7b2ff0..b4deb31d901c8c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -14,6 +14,10 @@ public static class MetricsServiceExtensions } namespace Microsoft.Extensions.Diagnostics.Metrics { + public static class ConsoleMetrics + { + public static string ListenerName => throw null!; + } public static class MetricsBuilderConsoleExtensions { public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => throw null!; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index 388cb880797275..3dc976e274d3dd 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -19,7 +19,7 @@ public ConsoleMetricListener() _timer = new Timer(OnTimer, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); } - public string Name => "Console"; + public string Name => ConsoleMetrics.ListenerName; public object? InstrumentPublished(Instrument instrument) => null; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs new file mode 100644 index 00000000000000..b0a557e3fe77b4 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs @@ -0,0 +1,10 @@ +// 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.Diagnostics.Metrics +{ + public static class ConsoleMetrics + { + public static string ListenerName => "Console"; + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index d285c5d220f243..21ed896c74614d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -19,7 +19,7 @@ public void ListenerCanBeRegisteredViaDi() services.AddMetrics(builder => { builder.AddDebugConsole(); - builder.EnableMetrics("TestMeter"); + builder.EnableMetrics("TestMeter", null, ConsoleMetrics.ListenerName); }); using var sp = services.BuildServiceProvider(); sp.GetRequiredService().Start(); From c2a249d325e6d7085ec3b3449870ac9fc3cacd42 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 15:57:26 -0700 Subject: [PATCH 21/50] Add/ClearListener --- ...oft.Extensions.Diagnostics.Abstractions.cs | 2 ++ ...Extensions.Diagnostics.Abstractions.csproj | 5 ++++ .../src/Metrics/MetricsBuilderExtensions.cs | 27 +++++++++++++++++-- ...Extensions.Diagnostics.Abstractions.csproj | 2 ++ .../src/Metrics/ListenerSubscription.cs | 12 +-------- .../MetricsBuilderConsoleExtensions.cs | 11 +------- 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 5dbc9d270df82a..030c6a5b7c0cd5 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -42,6 +42,8 @@ public enum MeterScope } public static class MetricsBuilderExtensions { + public static IMetricsBuilder AddListener<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] T> + (this IMetricsBuilder builder) where T : class, IMetricsListener { throw null!; } public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj index 2430dd2a54f721..f7b93cc292f7d7 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -7,6 +7,11 @@ + + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs index c0739de1634a99..ef4f78c21a74f4 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs @@ -1,11 +1,34 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + namespace Microsoft.Extensions.Diagnostics.Metrics { public static class MetricsBuilderExtensions { - public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } - public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } + public static IMetricsBuilder AddListener<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IMetricsBuilder builder) where T : class, IMetricsListener + { + ThrowHelper.ThrowIfNull(builder); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + return builder; + } + + public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) + { + ThrowHelper.ThrowIfNull(builder); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(listener)); + return builder; + } + + public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) + { + ThrowHelper.ThrowIfNull(builder); + builder.Services.RemoveAll(); + return builder; + } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj index 68c5605339092e..651ac8ceec1616 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -30,6 +30,8 @@ Microsoft.Extensions.Logging.Abstractions.NullLogger + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 2f4ab996e50502..a48ba94b2a972b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -250,17 +250,7 @@ internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableR return false; } - public void RecordObservableInstruments() - { - foreach (var instrument in _enabled) - { - if (instrument.IsObservable) - { - // TODO: We can't downcast because we don't know what the T is. - // var = instrument as ObservableInstrument; - } - } - } + public void RecordObservableInstruments() => _meterListener.RecordObservableInstruments(); public void Dispose() { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs index ee782be2582fe6..dfdc1d0d8daf5e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs @@ -1,10 +1,6 @@ // 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 Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace Microsoft.Extensions.Diagnostics.Metrics { /// @@ -17,11 +13,6 @@ public static class MetricsBuilderConsoleExtensions /// /// The metrics builder. /// The original metrics builder for chaining. - public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) - { - ThrowHelper.ThrowIfNull(builder); - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - return builder; - } + public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => builder.AddListener(); } } From 827582027df199baad9ddb95a277e0ea6611b912 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 16:34:23 -0700 Subject: [PATCH 22/50] More console output --- .../src/Metrics/ConsoleMetricListener.cs | 22 ++++++++++++++++--- .../src/Metrics/ListenerSubscription.cs | 3 +++ .../tests/ConsoleMetricListenerTests.cs | 7 ++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index 3dc976e274d3dd..1caf22219d270f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -21,16 +21,32 @@ public ConsoleMetricListener() public string Name => ConsoleMetrics.ListenerName; - public object? InstrumentPublished(Instrument instrument) => null; + public object? InstrumentPublished(Instrument instrument) + { + WriteLine($"{instrument.Meter.Name}-{instrument.Name} Started; Description: {instrument.Description}."); + return this; + } - public void MeasurementsCompleted(Instrument instrument, object? userState) { } + public void MeasurementsCompleted(Instrument instrument, object? userState) + { + // Debug.Assert(userState == this); TODO: State isn't flowing through + WriteLine($"{instrument.Meter.Name}-{instrument.Name} Stopped."); + } public void SetSource(IMetricsSource source) => _source = source; public MeasurementCallback GetMeasurementHandler() where T : struct => MeasurementHandler; private void MeasurementHandler(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct { - _textWriter.WriteLine($"{instrument.Meter.Name}-{instrument.Name} {measurement} {instrument.Unit}"); + WriteLine($"{instrument.Meter.Name}-{instrument.Name} {measurement} {instrument.Unit}"); + } + + private void WriteLine(string output) + { + lock (_textWriter) + { + _textWriter.WriteLine(output); + } } private void OnTimer(object? _) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index a48ba94b2a972b..0b1fedf03f494f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -53,6 +53,8 @@ private void InstrumentPublished(Instrument instrument, MeterListener _) return; } + // TODO: should this state flow somewhere? + var __ = _metricsListener.InstrumentPublished(instrument); _instruments.Add(instrument); RefreshInstrument(instrument); } @@ -73,6 +75,7 @@ private void MeasurementsCompleted(Instrument instrument, object? state) { _meterListener.DisableMeasurementEvents(instrument); } + _metricsListener.MeasurementsCompleted(instrument, state); } else { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index 21ed896c74614d..8898e2b53c93df 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -31,10 +31,13 @@ public void ListenerCanBeRegisteredViaDi() var factory = sp.GetRequiredService(); var meter = factory.Create("TestMeter", "version", new TagList() { { "key1", "value1" }, { "key2", "value2" } }); - var counter = meter.CreateCounter("counter", "blip"); + var counter = meter.CreateCounter("counter", "blip", "I count blips"); counter.Add(1); + sp.Dispose(); - Assert.Equal("TestMeter-counter 1 blip" + Environment.NewLine, output.ToString()); + Assert.Equal("TestMeter-counter Started; Description: I count blips." + Environment.NewLine + + "TestMeter-counter 1 blip" + Environment.NewLine + + "TestMeter-counter Stopped." + Environment.NewLine, output.ToString()); } } } From 3747367c8b1c9140514e6a40d1cf44538b694067 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 16:35:41 -0700 Subject: [PATCH 23/50] Revert DefaultMeterFactory changes --- .../src/Metrics/DefaultMeterFactory.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs index e20c459fd1a8ae..dd1be2d1512d3a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DefaultMeterFactory.cs @@ -13,6 +13,8 @@ internal sealed class DefaultMeterFactory : IMeterFactory private readonly Dictionary> _cachedMeters = new(); private bool _disposed; + public DefaultMeterFactory() { } + public Meter Create(MeterOptions options) { if (options is null) @@ -82,20 +84,20 @@ public void Dispose() _cachedMeters.Clear(); } } + } - internal sealed class FactoryMeter : Meter + internal sealed class FactoryMeter : Meter + { + public FactoryMeter(string name, string? version, IEnumerable>? tags, object? scope) + : base(name, version, tags, scope) { - public FactoryMeter(string name, string? version, IEnumerable>? tags, object? scope) - : base(name, version, tags, scope) - { - } + } - public void Release() => base.Dispose(true); // call the protected Dispose(bool) + public void Release() => base.Dispose(true); // call the protected Dispose(bool) - protected override void Dispose(bool disposing) - { - // no-op, disallow users from disposing of the meters created from the factory. - } + protected override void Dispose(bool disposing) + { + // no-op, disallow users from disposing of the meters created from the factory. } } } From 84eebd67569901dddb229fa9872e3f9e80a7d293 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 16:42:45 -0700 Subject: [PATCH 24/50] Cleanup --- .../ref/Microsoft.Extensions.Diagnostics.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index b4deb31d901c8c..ebce4edb5d91d1 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -40,15 +40,15 @@ internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable { internal TextWriter _textWriter; public string Name { get; } - public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct => throw new NotImplementedException(); - public object? InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument) => throw new NotImplementedException(); - public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState) => throw new NotImplementedException(); - public void SetSource(IMetricsSource source) => throw new NotImplementedException(); - public void Dispose() => throw new NotImplementedException(); + public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct => throw null!; + public object? InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument) => throw null!; + public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState) => throw null!; + public void SetSource(IMetricsSource source) => throw null!; + public void Dispose() => throw null!; } internal sealed class ListenerSubscription { - internal static bool RuleMatches(InstrumentEnableRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw new NotImplementedException(); - internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) => throw new NotImplementedException(); + internal static bool RuleMatches(InstrumentEnableRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw null!; + internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) => throw null!; } } From 52058610ae1dae777b444a8a4fa42fe0acea5b1d Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 27 Jul 2023 16:48:20 -0700 Subject: [PATCH 25/50] Notes --- .../src/Metrics/ListenerSubscription.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 0b1fedf03f494f..fae1e93e47ee34 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -248,7 +248,7 @@ internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableR return false; } - // Scopes TODO:?? + // Scopes TODO: Local is more specific than global (or local & global). return false; } From dd8c3641ec02241a508a888b9874871e5341c45a Mon Sep 17 00:00:00 2001 From: Chris R Date: Fri, 28 Jul 2023 10:08:48 -0700 Subject: [PATCH 26/50] Console updates, SetSoruce --- .../src/Metrics/ListenerSubscription.cs | 9 ++-- .../tests/ConsoleMetricListenerTests.cs | 52 ++++++++++++------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index fae1e93e47ee34..13c3e6e0edbaaf 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -36,6 +36,7 @@ public void Start() _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); _meterListener.Start(); + _metricsListener.SetSource(this); } private void InstrumentPublished(Instrument instrument, MeterListener _) @@ -53,8 +54,6 @@ private void InstrumentPublished(Instrument instrument, MeterListener _) return; } - // TODO: should this state flow somewhere? - var __ = _metricsListener.InstrumentPublished(instrument); _instruments.Add(instrument); RefreshInstrument(instrument); } @@ -74,8 +73,8 @@ private void MeasurementsCompleted(Instrument instrument, object? state) if (_enabled.Remove(instrument)) { _meterListener.DisableMeasurementEvents(instrument); + _metricsListener.MeasurementsCompleted(instrument, state); } - _metricsListener.MeasurementsCompleted(instrument, state); } else { @@ -117,11 +116,15 @@ private void RefreshInstrument(Instrument instrument) { _enabled.Remove(instrument); _meterListener.DisableMeasurementEvents(instrument); + // TODO: State? + _metricsListener.MeasurementsCompleted(instrument, userState: null); } else if (enable && !alreadyEnabled) { _meterListener.EnableMeasurementEvents(instrument); _enabled.Add(instrument); + // TODO: should this returned state flow somewhere? + _metricsListener.InstrumentPublished(instrument); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index 8898e2b53c93df..27fdd68f02152c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Diagnostics.Metrics; using System.IO; +using Microsoft.DotNet.RemoteExecutor; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -12,32 +13,43 @@ namespace Microsoft.Extensions.Diagnostics.Metrics.Tests { public class ConsoleMetricListenerTests { - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ListenerCanBeRegisteredViaDi() { - ServiceCollection services = new ServiceCollection(); - services.AddMetrics(builder => + RemoteExecutor.Invoke(() => { - builder.AddDebugConsole(); - builder.EnableMetrics("TestMeter", null, ConsoleMetrics.ListenerName); - }); - using var sp = services.BuildServiceProvider(); - sp.GetRequiredService().Start(); + ServiceCollection services = new ServiceCollection(); + services.AddMetrics(builder => + { + builder.AddDebugConsole(); + builder.EnableMetrics("TestMeter", null, ConsoleMetrics.ListenerName); + }); + using var sp = services.BuildServiceProvider(); + sp.GetRequiredService().Start(); - var listener = sp.GetRequiredService(); - var consoleListener = Assert.IsType(listener); - var output = new StringWriter(); - consoleListener._textWriter = output; + var listener = sp.GetRequiredService(); + var consoleListener = Assert.IsType(listener); + var output = new StringWriter(); + consoleListener._textWriter = output; - var factory = sp.GetRequiredService(); - var meter = factory.Create("TestMeter", "version", new TagList() { { "key1", "value1" }, { "key2", "value2" } }); - var counter = meter.CreateCounter("counter", "blip", "I count blips"); - counter.Add(1); - sp.Dispose(); + var factory = sp.GetRequiredService(); + var meter = factory.Create("TestMeter"); + var counter = meter.CreateCounter("counter", "blip", "I count blips"); + counter.Add(4); + counter.Add(1); - Assert.Equal("TestMeter-counter Started; Description: I count blips." + Environment.NewLine - + "TestMeter-counter 1 blip" + Environment.NewLine - + "TestMeter-counter Stopped." + Environment.NewLine, output.ToString()); + // The rule doesn't match, we shouldn't get this output. + var negativeMeter = factory.Create("NegativeMeter"); + counter = negativeMeter.CreateCounter("counter", "blop", "I count blops"); + counter.Add(1); + + sp.Dispose(); // TODO: Why did we have to do this to get the Stopped message? Why doesn't disposing of the meter do it? + + Assert.Equal("TestMeter-counter Started; Description: I count blips." + Environment.NewLine + + "TestMeter-counter 4 blip" + Environment.NewLine + + "TestMeter-counter 1 blip" + Environment.NewLine + + "TestMeter-counter Stopped." + Environment.NewLine, output.ToString()); + }).Dispose(); } } } From fc3553e025bca4a3f0be0d72022145ac867a1624 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 31 Jul 2023 15:12:33 -0700 Subject: [PATCH 27/50] Update InstrumentPublished --- ...oft.Extensions.Diagnostics.Abstractions.cs | 2 +- .../src/Metrics/IMetricsListener.cs | 2 +- ...t.Extensions.Diagnostics.Configuration.sln | 14 ++ .../ref/Microsoft.Extensions.Diagnostics.cs | 12 +- .../src/Metrics/ConsoleMetricListener.cs | 8 +- .../src/Metrics/ListenerSubscription.cs | 44 ++-- .../tests/ListenerSubscriptionTests.cs | 220 ++++++++++++++++++ 7 files changed, 282 insertions(+), 20 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 030c6a5b7c0cd5..170545828395c8 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -17,7 +17,7 @@ public interface IMetricsListener { public string Name { get; } public void SetSource(IMetricsSource source); - public object? InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument); + public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState); public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState); public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs index 365d7887500b2e..a41a09b428c0ac 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs @@ -9,7 +9,7 @@ public interface IMetricsListener { public string Name { get; } public void SetSource(IMetricsSource source); - public object? InstrumentPublished(Instrument instrument); + public bool InstrumentPublished(Instrument instrument, out object? userState); public void MeasurementsCompleted(Instrument instrument, object? userState); public MeasurementCallback GetMeasurementHandler() where T : struct; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln index 4627a2575af392..f30a3d0ca671c4 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln @@ -61,6 +61,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Primit EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Primitives", "..\Microsoft.Extensions.Primitives\src\Microsoft.Extensions.Primitives.csproj", "{DFCDD214-BAED-468D-BC61-B1D536FF0AFF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Options", "..\Microsoft.Extensions.Options\src\Microsoft.Extensions.Options.csproj", "{59D2147E-1E97-49F1-87AF-F5C50DCD73B6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Options", "..\Microsoft.Extensions.Options\ref\Microsoft.Extensions.Options.csproj", "{F8409C3F-4C46-4115-AA48-518A7490A14A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -167,6 +171,14 @@ Global {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Release|Any CPU.Build.0 = Release|Any CPU + {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Release|Any CPU.Build.0 = Release|Any CPU + {F8409C3F-4C46-4115-AA48-518A7490A14A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8409C3F-4C46-4115-AA48-518A7490A14A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8409C3F-4C46-4115-AA48-518A7490A14A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8409C3F-4C46-4115-AA48-518A7490A14A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -197,6 +209,8 @@ Global {9AC40671-E708-4FE1-B5CB-ADBBC425F793} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} {DFCDD214-BAED-468D-BC61-B1D536FF0AFF} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {59D2147E-1E97-49F1-87AF-F5C50DCD73B6} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {F8409C3F-4C46-4115-AA48-518A7490A14A} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7D279EE5-E38F-4125-AE82-6ADE52D72F26} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index ebce4edb5d91d1..9451210d6f3f16 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.IO; namespace Microsoft.Extensions.DependencyInjection @@ -41,14 +42,23 @@ internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable internal TextWriter _textWriter; public string Name { get; } public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct => throw null!; - public object? InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument) => throw null!; + public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState) => throw null!; public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState) => throw null!; public void SetSource(IMetricsSource source) => throw null!; public void Dispose() => throw null!; } internal sealed class ListenerSubscription { + internal ListenerSubscription(Microsoft.Extensions.Diagnostics.Metrics.IMetricsListener listener) { } + public void Start() { } + internal void UpdateRules(IList rules) { } internal static bool RuleMatches(InstrumentEnableRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw null!; internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) => throw null!; } + internal sealed class DefaultMeterFactory : System.Diagnostics.Metrics.IMeterFactory + { + public DefaultMeterFactory() { } + public System.Diagnostics.Metrics.Meter Create(System.Diagnostics.Metrics.MeterOptions options) => throw null!; + public void Dispose() { } + } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index 1caf22219d270f..c061013e73c7b3 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Diagnostics.Metrics; using System.IO; using System.Threading; @@ -21,15 +22,16 @@ public ConsoleMetricListener() public string Name => ConsoleMetrics.ListenerName; - public object? InstrumentPublished(Instrument instrument) + public bool InstrumentPublished(Instrument instrument, out object? userState) { WriteLine($"{instrument.Meter.Name}-{instrument.Name} Started; Description: {instrument.Description}."); - return this; + userState = this; + return true; } public void MeasurementsCompleted(Instrument instrument, object? userState) { - // Debug.Assert(userState == this); TODO: State isn't flowing through + Debug.Assert(userState == this); WriteLine($"{instrument.Meter.Name}-{instrument.Name} Stopped."); } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 13c3e6e0edbaaf..20b7d1c72de0d0 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -14,6 +14,7 @@ internal class ListenerSubscription : IMetricsSource, IDisposable private readonly IMetricsListener _metricsListener; private readonly HashSet _instruments = new(); private readonly HashSet _enabled = new(); + private readonly Dictionary _published = new(); private IList _rules = Array.Empty(); private bool _disposed; @@ -59,6 +60,8 @@ private void InstrumentPublished(Instrument instrument, MeterListener _) } } + // Called when we call DisableMeasurementEvents, like when a rule is disabled. + // TODO: When should we remove an Instrument from _instruments? private void MeasurementsCompleted(Instrument instrument, object? state) { lock (_instruments) @@ -68,18 +71,18 @@ private void MeasurementsCompleted(Instrument instrument, object? state) return; } - if (_instruments.Remove(instrument)) + _enabled.Remove(instrument); + + if (_published.TryGetValue(instrument, out var localState)) { - if (_enabled.Remove(instrument)) + _published.Remove(instrument); + var listenerEnabled = localState.Item1; + var userState = localState.Item2; + if (listenerEnabled) { - _meterListener.DisableMeasurementEvents(instrument); - _metricsListener.MeasurementsCompleted(instrument, state); + _metricsListener.MeasurementsCompleted(instrument, userState); } } - else - { - Debug.Assert(false, "InstrumentPublished was not called for this instrument"); - } } } @@ -116,15 +119,28 @@ private void RefreshInstrument(Instrument instrument) { _enabled.Remove(instrument); _meterListener.DisableMeasurementEvents(instrument); - // TODO: State? - _metricsListener.MeasurementsCompleted(instrument, userState: null); } else if (enable && !alreadyEnabled) { - _meterListener.EnableMeasurementEvents(instrument); - _enabled.Add(instrument); - // TODO: should this returned state flow somewhere? - _metricsListener.InstrumentPublished(instrument); + // The first time we enable an instrument, we need to call InstrumentPublished. + bool listenerEnabled; + if (_published.TryGetValue(instrument, out var metadata)) + { + listenerEnabled = metadata.Item1; + } + else + { + listenerEnabled = _metricsListener.InstrumentPublished(instrument, out var state); + + // However, a listener might decline to enable the instrument, remember that. + _published.Add(instrument, (listenerEnabled, state)); + } + + if (listenerEnabled) + { + _meterListener.EnableMeasurementEvents(instrument); + _enabled.Add(instrument); + } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index 6fbd8d713a7dbe..ace9bf880fb72d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Metrics; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Microsoft.Extensions.Diagnostics.Metrics; using Xunit; @@ -12,6 +13,202 @@ namespace Microsoft.Extensions.Diagnostics.Tests { public class ListenerSubscriptionTests { + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void SubscriptionReceivesNewGlobalMetersAndInstruments() + { + // RemoteExecutor.Invoke(() => + { + var publishedTcs = new TaskCompletionSource(); + var completedTcs = new TaskCompletionSource(); + var measurementTcs = new TaskCompletionSource(); + + var fakeListener = new FakeMetricListener(); + fakeListener.OnPublished = (instrument) => + { + Assert.Null(instrument.Meter.Scope); // Global + publishedTcs.TrySetResult(instrument); + return (true, null); + }; + fakeListener.OnCompleted = (instrument, state) => + { + completedTcs.TrySetResult(instrument); + }; + fakeListener.OnMeasurement = (instrument, measurement, tags, state) => + { + measurementTcs.TrySetResult((int)measurement); + }; + + var subscription = new ListenerSubscription(fakeListener); + Assert.Null(fakeListener.Source); + + subscription.Start(); + Assert.Same(subscription, fakeListener.Source); + + // No rules yet, so we shouldn't get any notifications. + var meter = new Meter("TestMeter"); + var counter = meter.CreateCounter("counter", "blip", "I count blips"); + counter.Add(1); + + Assert.False(publishedTcs.Task.IsCompleted); + Assert.False(measurementTcs.Task.IsCompleted); + + // Add a rule that matches the counter. + subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Global, enable: true) }); + + Assert.True(publishedTcs.Task.IsCompleted); + + counter.Add(2); + Assert.True(measurementTcs.Task.IsCompleted); + Assert.Equal(2, measurementTcs.Task.Result); + + Assert.False(completedTcs.Task.IsCompleted); + + // Disable + subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Global, enable: false) }); + + Assert.True(completedTcs.Task.IsCompleted); + + }//).Dispose(); + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void SubscriptionReceivesNewLocalMetersAndInstruments() + { + RemoteExecutor.Invoke(() => + { + var publishedTcs = new TaskCompletionSource(); + var completedTcs = new TaskCompletionSource(); + var measurementTcs = new TaskCompletionSource(); + + var factory = new DefaultMeterFactory(); + + var fakeListener = new FakeMetricListener(); + fakeListener.OnPublished = (instrument) => + { + Assert.Equal(factory, instrument.Meter.Scope); // Local + publishedTcs.TrySetResult(instrument); + return (true, null); + }; + fakeListener.OnCompleted = (instrument, state) => + { + completedTcs.TrySetResult(instrument); + }; + fakeListener.OnMeasurement = (instrument, measurement, tags, state) => + { + measurementTcs.TrySetResult((int)measurement); + }; + + var subscription = new ListenerSubscription(fakeListener); + Assert.Null(fakeListener.Source); + + subscription.Start(); + Assert.Same(subscription, fakeListener.Source); + + // No rules yet, so we shouldn't get any notifications. + var meter = factory.Create("TestMeter"); + var counter = meter.CreateCounter("counter", "blip", "I count blips"); + counter.Add(1); + + Assert.False(publishedTcs.Task.IsCompleted); + Assert.False(measurementTcs.Task.IsCompleted); + + // Add a rule that matches the counter. + subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); + + Assert.True(publishedTcs.Task.IsCompleted); + + counter.Add(2); + Assert.True(measurementTcs.Task.IsCompleted); + Assert.Equal(2, measurementTcs.Task.Result); + + Assert.False(completedTcs.Task.IsCompleted); + + // Disable + subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); + + Assert.True(completedTcs.Task.IsCompleted); + + }).Dispose(); + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void RuleCanBeTurnedOffAndOnAgain() + { + RemoteExecutor.Invoke(() => + { + var publishCalled = 0; + var publishedTcs = new TaskCompletionSource(); + var onCompletedCalled = 0; + var completedTcs = new TaskCompletionSource(); + var measurements = new List(); + + var factory = new DefaultMeterFactory(); + + var fakeListener = new FakeMetricListener(); + fakeListener.OnPublished = (instrument) => + { + publishCalled++; + Assert.Equal(factory, instrument.Meter.Scope); // Local + publishedTcs.TrySetResult(instrument); + return (true, null); + }; + fakeListener.OnCompleted = (instrument, state) => + { + onCompletedCalled++; + Assert.Equal(1, onCompletedCalled); + completedTcs.TrySetResult(instrument); + }; + fakeListener.OnMeasurement = (instrument, measurement, tags, state) => + { + measurements.Add((int)measurement); + }; + + var subscription = new ListenerSubscription(fakeListener); + Assert.Null(fakeListener.Source); + + subscription.Start(); + Assert.Same(subscription, fakeListener.Source); + + // No rules yet, so we shouldn't get any notifications. + var meter = factory.Create("TestMeter"); + var counter = meter.CreateCounter("counter", "blip", "I count blips"); + counter.Add(1); + + Assert.False(publishedTcs.Task.IsCompleted); + Assert.Equal(0, measurements.Count); + Assert.Equal(0, publishCalled); + + // Add a rule that matches the counter. + subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); + + Assert.True(publishedTcs.Task.IsCompleted); + Assert.Equal(1, publishCalled); + + counter.Add(2); + Assert.Equal(1, measurements.Count); + Assert.Equal(2, measurements[0]); + + Assert.False(completedTcs.Task.IsCompleted); + + // Disable + subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); + + Assert.True(completedTcs.Task.IsCompleted); + counter.Add(3); + // Not received + Assert.Equal(1, measurements.Count); + + // Re-enable + subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); + Assert.Equal(2, publishCalled); + + counter.Add(4); + Assert.Equal(2, measurements.Count); + Assert.Equal(4, measurements[1]); + + }).Dispose(); + } + // TODO: Scopes [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -133,5 +330,28 @@ public void IsMoreSpecificTest(InstrumentEnableRule rule, InstrumentEnableRule? // TODO: Scopes }; + + public delegate void FakeMeasurementCallback(Instrument instrument, object measurement, ReadOnlySpan> tags, object? state); + private class FakeMetricListener : IMetricsListener + { + public string Name => "FakeListener"; + + public Func OnPublished { get; set; } = (_) => (true, null); + public Action OnCompleted { get; set; } = (_, _) => { }; + public IMetricsSource? Source { get; set; } + public FakeMeasurementCallback OnMeasurement { get; set; } + + public MeasurementCallback GetMeasurementHandler() where T : struct + => (instrument, value, tags, state) => OnMeasurement(instrument, value, tags, state); + + public bool InstrumentPublished(Instrument instrument, out object? userState) + { + (var published, userState) = OnPublished(instrument); + return published; + } + + public void MeasurementsCompleted(Instrument instrument, object? userState) => OnCompleted(instrument, userState); + public void SetSource(IMetricsSource source) => Source = source; + } } } From 5e042a7c2608aed66529ee02736598a8148026d9 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 31 Jul 2023 15:24:39 -0700 Subject: [PATCH 28/50] Move MetricsBuilderEnableExtensions back --- ...icrosoft.Extensions.Diagnostics.Abstractions.cs | 14 ++++++++++++++ .../src/Metrics/MetricsBuilderEnableExtensions.cs | 0 ...soft.Extensions.Diagnostics.Abstractions.csproj | 1 + .../ref/Microsoft.Extensions.Diagnostics.cs | 14 -------------- .../src/Microsoft.Extensions.Diagnostics.csproj | 1 - 5 files changed, 15 insertions(+), 15 deletions(-) rename src/libraries/{Microsoft.Extensions.Diagnostics => Microsoft.Extensions.Diagnostics.Abstractions}/src/Metrics/MetricsBuilderEnableExtensions.cs (100%) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 170545828395c8..73c2b52c810168 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -47,6 +47,20 @@ public static class MetricsBuilderExtensions public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } } + public static class MetricsBuilderEnableExtensions + { + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName) => throw null!; + public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; + public static MetricsEnableOptions DisableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; + } public class MetricsEnableOptions { public IList Rules { get; } = null!; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs similarity index 100% rename from src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderEnableExtensions.cs rename to src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj index 651ac8ceec1616..441f9cffa29824 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -37,6 +37,7 @@ Microsoft.Extensions.Logging.Abstractions.NullLogger + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 9451210d6f3f16..ceb444efe6df5d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -23,20 +23,6 @@ public static class MetricsBuilderConsoleExtensions { public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => throw null!; } - public static class MetricsBuilderEnableExtensions - { - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; - public static MetricsEnableOptions DisableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; - } internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable { internal TextWriter _textWriter; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index 678d28c6ed6a0f..a9ce586efdc191 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -24,7 +24,6 @@ - From 2852d14c558189fbcabb7b1709750edbfceecaf4 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 1 Aug 2023 15:46:36 -0700 Subject: [PATCH 29/50] API refactor --- ...oft.Extensions.Diagnostics.Abstractions.cs | 44 +++---- .../src/Metrics/IMetricsListener.cs | 2 +- .../Metrics/IMetricsSubscriptionManager.cs | 10 -- ...rce.cs => IObservableInstrumentsSource.cs} | 2 +- ...trumentEnableRule.cs => InstrumentRule.cs} | 6 +- .../src/Metrics/MeterScope.cs | 1 + .../Metrics/MetricsBuilderEnableExtensions.cs | 60 --------- ...> MetricsBuilderExtensions.AddListener.cs} | 2 +- .../MetricsBuilderExtensions.Enable.cs | 58 +++++++++ ...ricsEnableOptions.cs => MetricsOptions.cs} | 4 +- .../MetricsBuilderConfigurationExtensions.cs | 3 +- .../ref/Microsoft.Extensions.Diagnostics.cs | 10 +- .../src/Metrics/ConsoleMetricListener.cs | 4 +- .../src/Metrics/ListenerSubscription.cs | 18 +-- .../src/Metrics/MetricsServiceExtensions.cs | 7 +- .../src/Metrics/MetricsSubscriptionManager.cs | 10 +- .../tests/ConsoleMetricListenerTests.cs | 3 +- .../tests/ListenerSubscriptionTests.cs | 116 +++++++++--------- .../src/HostingHostBuilderExtensions.cs | 1 - .../src/Internal/MetricsHostedService.cs | 27 ---- 20 files changed, 174 insertions(+), 214 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs rename src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/{IMetricsSource.cs => IObservableInstrumentsSource.cs} (83%) rename src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/{InstrumentEnableRule.cs => InstrumentRule.cs} (78%) delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs rename src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/{MetricsBuilderExtensions.cs => MetricsBuilderExtensions.AddListener.cs} (95%) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs rename src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/{MetricsEnableOptions.cs => MetricsOptions.cs} (64%) delete mode 100644 src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 73c2b52c810168..78ab2ba9da9a9f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -16,27 +16,28 @@ public interface IMetricsBuilder public interface IMetricsListener { public string Name { get; } - public void SetSource(IMetricsSource source); + public void Initialize(IObservableInstrumentsSource source); public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState); public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState); public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct; } - public interface IMetricsSource + public interface IObservableInstrumentsSource { public void RecordObservableInstruments(); } - public class InstrumentEnableRule + public class InstrumentRule { - public InstrumentEnableRule(string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) { } - public string? ListenerName { get; } + public InstrumentRule(string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) { } public string? MeterName { get; } - public MeterScope Scopes { get; } public string? InstrumentName { get; } + public string? ListenerName { get; } + public MeterScope Scopes { get; } public bool Enable { get; } } [Flags] public enum MeterScope { + None = 0, Global, Local } @@ -46,28 +47,19 @@ public static class MetricsBuilderExtensions (this IMetricsBuilder builder) where T : class, IMetricsListener { throw null!; } public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } - } - public static class MetricsBuilderEnableExtensions - { - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName) => throw null!; - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; - public static MetricsEnableOptions DisableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) => throw null!; - } - public class MetricsEnableOptions - { - public IList Rules { get; } = null!; - } + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName) => throw null!; + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; - public interface IMetricsSubscriptionManager + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName) => throw null!; + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; + } + public class MetricsOptions { - public void Start(); + public IList Rules { get; } = null!; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs index a41a09b428c0ac..e7e84b3826d411 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics public interface IMetricsListener { public string Name { get; } - public void SetSource(IMetricsSource source); + public void Initialize(IObservableInstrumentsSource source); public bool InstrumentPublished(Instrument instrument, out object? userState); public void MeasurementsCompleted(Instrument instrument, object? userState); public MeasurementCallback GetMeasurementHandler() where T : struct; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs deleted file mode 100644 index 5eaf7d6c6a3094..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSubscriptionManager.cs +++ /dev/null @@ -1,10 +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.Diagnostics.Metrics -{ - public interface IMetricsSubscriptionManager - { - public void Start(); - } -} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSource.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs similarity index 83% rename from src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSource.cs rename to src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs index b10365c58987a4..998558388c996a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsSource.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs @@ -3,7 +3,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - public interface IMetricsSource + public interface IObservableInstrumentsSource { public void RecordObservableInstruments(); } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs similarity index 78% rename from src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs rename to src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs index 1940ba3d8fb99c..6613ab7d5f48b2 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentEnableRule.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs @@ -3,12 +3,12 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - public class InstrumentEnableRule(string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) + public class InstrumentRule(string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) { - public string? ListenerName { get; } = listenerName; public string? MeterName { get; } = meterName; - public MeterScope Scopes { get; } = scopes; public string? InstrumentName { get; } = instrumentName; + public string? ListenerName { get; } = listenerName; + public MeterScope Scopes { get; } = scopes; public bool Enable { get; } = enable; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs index fd36fcfce8c6ef..32862152854d3c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs @@ -8,6 +8,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics [Flags] public enum MeterScope { + None = 0, Global, Local } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs deleted file mode 100644 index 66bcf335d8f51c..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderEnableExtensions.cs +++ /dev/null @@ -1,60 +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.Metrics; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.Extensions.Diagnostics.Metrics -{ - public static class MetricsBuilderEnableExtensions - { - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder) - => builder.ConfigureRule(options => options.EnableMetrics()); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) - => builder.ConfigureRule(options => options.EnableMetrics(meterName)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName) - => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName) - => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName)); - - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) - => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName, scopes)); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options) - => options.AddRule(); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName) - => options.AddRule(meterName: meterName); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName) - => options.AddRule(meterName: meterName, instrumentName: instrumentName); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName) - => options.AddRule(meterName, instrumentName, listenerName); - - public static MetricsEnableOptions EnableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) - => options.AddRule(meterName, instrumentName, listenerName, scopes); - - // TODO: How many overloads of this do we want? - public static MetricsEnableOptions DisableMetrics(this MetricsEnableOptions options, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes) - => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: false); - - private static IMetricsBuilder ConfigureRule(this IMetricsBuilder builder, Action configureOptions) - { - builder.Services.Configure(configureOptions); - return builder; - } - - private static MetricsEnableOptions AddRule(this MetricsEnableOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, - MeterScope scopes = MeterScope.Local | MeterScope.Global, bool? enable = true) - { - ThrowHelper.ThrowIfNull(options); - options.Rules.Add(new InstrumentEnableRule(meterName, instrumentName, listenerName, scopes, enable ?? true)); - return options; - } - } -} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs similarity index 95% rename from src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs rename to src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs index ef4f78c21a74f4..edb66bdafda46d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - public static class MetricsBuilderExtensions + public static partial class MetricsBuilderExtensions { public static IMetricsBuilder AddListener<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IMetricsBuilder builder) where T : class, IMetricsListener { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs new file mode 100644 index 00000000000000..220f1d24d73d34 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs @@ -0,0 +1,58 @@ +// 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 Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public static partial class MetricsBuilderExtensions + { + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) + => builder.ConfigureRule(options => options.EnableMetrics(meterName)); + + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, + MeterScope scopes = MeterScope.Global | MeterScope.Local) + => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName, scopes)); + + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName) + => options.EnableMetrics(meterName: meterName, instrumentName: null); + + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, + MeterScope scopes = MeterScope.Global | MeterScope.Local) + => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: true); + + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName) + => builder.ConfigureRule(options => options.DisableMetrics(meterName)); + + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, + MeterScope scopes = MeterScope.Global | MeterScope.Local) + => builder.ConfigureRule(options => options.DisableMetrics(meterName, instrumentName, listenerName, scopes)); + + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName) + => options.DisableMetrics(meterName: meterName, instrumentName: null); + + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, + MeterScope scopes = MeterScope.Global | MeterScope.Local) + => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: false); + + private static IMetricsBuilder ConfigureRule(this IMetricsBuilder builder, Action configureOptions) + { + ThrowHelper.ThrowIfNull(builder); + builder.Services.Configure(configureOptions); + return builder; + } + + private static MetricsOptions AddRule(this MetricsOptions options, string? meterName, string? instrumentName, string? listenerName, + MeterScope scopes, bool enable) + { + ThrowHelper.ThrowIfNull(options); + if (scopes == MeterScope.None) + { + throw new ArgumentOutOfRangeException(nameof(scopes), "The MeterScope must be Global, Local, or both."); + } + options.Rules.Add(new InstrumentRule(meterName, instrumentName, listenerName, scopes, enable)); + return options; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsOptions.cs similarity index 64% rename from src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs rename to src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsOptions.cs index 247e4073adc619..c0172d760cd54c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsEnableOptions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsOptions.cs @@ -5,8 +5,8 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - public class MetricsEnableOptions + public class MetricsOptions { - public IList Rules { get; } = new List(); + public IList Rules { get; } = new List(); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs index e0d6b85d5ebe08..f8f255ea7031cb 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs @@ -7,6 +7,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public static class MetricsBuilderConfigurationExtensions { - public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) => throw null!; + // TODO: + public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) => builder; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index ceb444efe6df5d..d04345bcb3cd0d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -30,16 +30,16 @@ internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct => throw null!; public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState) => throw null!; public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState) => throw null!; - public void SetSource(IMetricsSource source) => throw null!; + public void Initialize(IObservableInstrumentsSource source) => throw null!; public void Dispose() => throw null!; } internal sealed class ListenerSubscription { internal ListenerSubscription(Microsoft.Extensions.Diagnostics.Metrics.IMetricsListener listener) { } - public void Start() { } - internal void UpdateRules(IList rules) { } - internal static bool RuleMatches(InstrumentEnableRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw null!; - internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) => throw null!; + public void Initialize() { } + internal void UpdateRules(IList rules) { } + internal static bool RuleMatches(InstrumentRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw null!; + internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best) => throw null!; } internal sealed class DefaultMeterFactory : System.Diagnostics.Metrics.IMeterFactory { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index c061013e73c7b3..ae3fbf0e2c760d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -13,7 +13,7 @@ internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable { private readonly Timer _timer; internal TextWriter _textWriter = Console.Out; - private IMetricsSource? _source; + private IObservableInstrumentsSource? _source; public ConsoleMetricListener() { @@ -35,7 +35,7 @@ public void MeasurementsCompleted(Instrument instrument, object? userState) WriteLine($"{instrument.Meter.Name}-{instrument.Name} Stopped."); } - public void SetSource(IMetricsSource source) => _source = source; + public void Initialize(IObservableInstrumentsSource source) => _source = source; public MeasurementCallback GetMeasurementHandler() where T : struct => MeasurementHandler; private void MeasurementHandler(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 20b7d1c72de0d0..30d13575d5442b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -8,14 +8,14 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - internal class ListenerSubscription : IMetricsSource, IDisposable + internal class ListenerSubscription : IObservableInstrumentsSource, IDisposable { private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; private readonly HashSet _instruments = new(); private readonly HashSet _enabled = new(); private readonly Dictionary _published = new(); - private IList _rules = Array.Empty(); + private IList _rules = Array.Empty(); private bool _disposed; internal ListenerSubscription(IMetricsListener metricsListener) @@ -25,7 +25,7 @@ internal ListenerSubscription(IMetricsListener metricsListener) _meterListener = new MeterListener(); } - public void Start() + public void Initialize() { _meterListener.InstrumentPublished = InstrumentPublished; _meterListener.MeasurementsCompleted = MeasurementsCompleted; @@ -37,7 +37,7 @@ public void Start() _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); _meterListener.Start(); - _metricsListener.SetSource(this); + _metricsListener.Initialize(this); } private void InstrumentPublished(Instrument instrument, MeterListener _) @@ -86,7 +86,7 @@ private void MeasurementsCompleted(Instrument instrument, object? state) } } - internal void UpdateRules(IList rules) + internal void UpdateRules(IList rules) { lock (_instruments) { @@ -144,9 +144,9 @@ private void RefreshInstrument(Instrument instrument) } } - private InstrumentEnableRule? GetMostSpecificRule(Instrument instrument) + private InstrumentRule? GetMostSpecificRule(Instrument instrument) { - InstrumentEnableRule? best = null; + InstrumentRule? best = null; foreach (var rule in _rules) { if (RuleMatches(rule, instrument, _metricsListener.Name) && IsMoreSpecific(rule, best)) @@ -159,7 +159,7 @@ private void RefreshInstrument(Instrument instrument) } // internal for testing - internal static bool RuleMatches(InstrumentEnableRule rule, Instrument instrument, string listenerName) + internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, string listenerName) { // Exact match or empty if (!string.IsNullOrEmpty(rule.ListenerName) @@ -221,7 +221,7 @@ internal static bool RuleMatches(InstrumentEnableRule rule, Instrument instrumen // Everything must already match the Instrument and listener, or be blank. // Which rule has more non-blank fields? Or longer Meter name? // internal for testing - internal static bool IsMoreSpecific(InstrumentEnableRule rule, InstrumentEnableRule? best) + internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best) { if (best == null) { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs index 628df89240f9e4..0bfe6cb338ac32 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs @@ -28,7 +28,10 @@ public static IServiceCollection AddMetrics(this IServiceCollection services) services.AddOptions(); services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + // Make sure the subscription manager is started when the host starts. + // The host will trigger options validation. + services.AddOptions().Configure((_, manager) => manager.Initialize()).ValidateOnStart(); return services; } @@ -58,5 +61,7 @@ private sealed class MetricsBuilder(IServiceCollection services) : IMetricsBuild { public IServiceCollection Services { get; } = services; } + + private sealed class NoOpOptions { } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs index c4d8dbb99127a7..7a68ba7edb5f6a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs @@ -8,13 +8,13 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - internal partial class MetricsSubscriptionManager : IMetricsSubscriptionManager + internal partial class MetricsSubscriptionManager { private readonly ListenerSubscription[] _listeners; private readonly IDisposable? _changeTokenRegistration; private bool _disposed; - public MetricsSubscriptionManager(IEnumerable listeners, IOptionsMonitor options) + public MetricsSubscriptionManager(IEnumerable listeners, IOptionsMonitor options) { var list = listeners.ToList(); _listeners = new ListenerSubscription[list.Count]; @@ -26,15 +26,15 @@ public MetricsSubscriptionManager(IEnumerable listeners, IOpti UpdateRules(options.CurrentValue); } - public void Start() + public void Initialize() { foreach (var listener in _listeners) { - listener.Start(); + listener.Initialize(); } } - private void UpdateRules(MetricsEnableOptions options) + private void UpdateRules(MetricsOptions options) { if (_disposed) { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index 27fdd68f02152c..b20c8ec7583e98 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -7,6 +7,7 @@ using System.IO; using Microsoft.DotNet.RemoteExecutor; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Xunit; namespace Microsoft.Extensions.Diagnostics.Metrics.Tests @@ -25,7 +26,7 @@ public void ListenerCanBeRegisteredViaDi() builder.EnableMetrics("TestMeter", null, ConsoleMetrics.ListenerName); }); using var sp = services.BuildServiceProvider(); - sp.GetRequiredService().Start(); + sp.GetRequiredService().Validate(); var listener = sp.GetRequiredService(); var consoleListener = Assert.IsType(listener); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index ace9bf880fb72d..fd938b6f162fc6 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -41,7 +41,7 @@ public void SubscriptionReceivesNewGlobalMetersAndInstruments() var subscription = new ListenerSubscription(fakeListener); Assert.Null(fakeListener.Source); - subscription.Start(); + subscription.Initialize(); Assert.Same(subscription, fakeListener.Source); // No rules yet, so we shouldn't get any notifications. @@ -53,7 +53,7 @@ public void SubscriptionReceivesNewGlobalMetersAndInstruments() Assert.False(measurementTcs.Task.IsCompleted); // Add a rule that matches the counter. - subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Global, enable: true) }); + subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Global, enable: true) }); Assert.True(publishedTcs.Task.IsCompleted); @@ -64,7 +64,7 @@ public void SubscriptionReceivesNewGlobalMetersAndInstruments() Assert.False(completedTcs.Task.IsCompleted); // Disable - subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Global, enable: false) }); + subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Global, enable: false) }); Assert.True(completedTcs.Task.IsCompleted); @@ -101,7 +101,7 @@ public void SubscriptionReceivesNewLocalMetersAndInstruments() var subscription = new ListenerSubscription(fakeListener); Assert.Null(fakeListener.Source); - subscription.Start(); + subscription.Initialize(); Assert.Same(subscription, fakeListener.Source); // No rules yet, so we shouldn't get any notifications. @@ -113,7 +113,7 @@ public void SubscriptionReceivesNewLocalMetersAndInstruments() Assert.False(measurementTcs.Task.IsCompleted); // Add a rule that matches the counter. - subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); + subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); Assert.True(publishedTcs.Task.IsCompleted); @@ -124,7 +124,7 @@ public void SubscriptionReceivesNewLocalMetersAndInstruments() Assert.False(completedTcs.Task.IsCompleted); // Disable - subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); + subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); Assert.True(completedTcs.Task.IsCompleted); @@ -166,7 +166,7 @@ public void RuleCanBeTurnedOffAndOnAgain() var subscription = new ListenerSubscription(fakeListener); Assert.Null(fakeListener.Source); - subscription.Start(); + subscription.Initialize(); Assert.Same(subscription, fakeListener.Source); // No rules yet, so we shouldn't get any notifications. @@ -179,7 +179,7 @@ public void RuleCanBeTurnedOffAndOnAgain() Assert.Equal(0, publishCalled); // Add a rule that matches the counter. - subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); + subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); Assert.True(publishedTcs.Task.IsCompleted); Assert.Equal(1, publishCalled); @@ -191,7 +191,7 @@ public void RuleCanBeTurnedOffAndOnAgain() Assert.False(completedTcs.Task.IsCompleted); // Disable - subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); + subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); Assert.True(completedTcs.Task.IsCompleted); counter.Add(3); @@ -199,7 +199,7 @@ public void RuleCanBeTurnedOffAndOnAgain() Assert.Equal(1, measurements.Count); // Re-enable - subscription.UpdateRules(new[] { new InstrumentEnableRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); + subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); Assert.Equal(2, publishCalled); counter.Add(4); @@ -230,7 +230,7 @@ public void RuleCanBeTurnedOffAndOnAgain() public void RuleMatchesTest(string meterName, string instrumentName, string listenerName) { RemoteExecutor.Invoke((string m, string i, string l) => { - var rule = new InstrumentEnableRule(m, i, l, MeterScope.Global, enable: true); + var rule = new InstrumentRule(m, i, l, MeterScope.Global, enable: true); var meter = new Meter("Long.Silly.Meter.Name"); var instrument = meter.CreateCounter("InstrumentName"); Assert.True(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName")); @@ -254,7 +254,7 @@ public void RuleMatchesTest(string meterName, string instrumentName, string list public void RuleMatchesNegativeTest(string meterName, string instrumentName, string listenerName) { RemoteExecutor.Invoke((string m, string i, string l) => { - var rule = new InstrumentEnableRule(m, i, l, MeterScope.Global, enable: true); + var rule = new InstrumentRule(m, i, l, MeterScope.Global, enable: true); var meter = new Meter("Long.Silly.Meter.Name"); var instrument = meter.CreateCounter("InstrumentName"); Assert.False(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName")); @@ -263,7 +263,7 @@ public void RuleMatchesNegativeTest(string meterName, string instrumentName, str [Theory] [MemberData(nameof(IsMoreSpecificTestData))] - public void IsMoreSpecificTest(InstrumentEnableRule rule, InstrumentEnableRule? best) + public void IsMoreSpecificTest(InstrumentRule rule, InstrumentRule? best) { Assert.True(ListenerSubscription.IsMoreSpecific(rule, best)); @@ -276,57 +276,57 @@ public void IsMoreSpecificTest(InstrumentEnableRule rule, InstrumentEnableRule? public static IEnumerable IsMoreSpecificTestData() => new object[][] { // Anything is better than null - new object[] { new InstrumentEnableRule(null, null, null, MeterScope.Global, true), null }, + new object[] { new InstrumentRule(null, null, null, MeterScope.Global, true), null }, // Any field is better than empty - new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), - new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true), - new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true), - new InstrumentEnableRule(null, null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), + new InstrumentRule(null, null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), + new InstrumentRule(null, null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), + new InstrumentRule(null, null, null, MeterScope.Global, true) }, // Meter > Instrument > Listener - new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), - new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), - new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), - new InstrumentEnableRule(null, "instrumentName", "listenerName", MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true), - new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), + new InstrumentRule(null, "instrumentName", "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, // Multiple fields are better than one. - new object[] { new InstrumentEnableRule("meterName", "instrumentName", null, MeterScope.Global, true), - new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", null, "listenerName", MeterScope.Global, true), - new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), - new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, - - new object[] { new InstrumentEnableRule("meterName", "instrumentName", null, MeterScope.Global, true), - new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", null, "listenerName", MeterScope.Global, true), - new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), - new InstrumentEnableRule(null, "instrumentName", null, MeterScope.Global, true) }, - - new object[] { new InstrumentEnableRule("meterName", "instrumentName", null, MeterScope.Global, true), - new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", null, "listenerName", MeterScope.Global, true), - new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), - new InstrumentEnableRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), + new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, "listenerName", MeterScope.Global, true), + new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), + new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + + new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, "listenerName", MeterScope.Global, true), + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + + new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, "listenerName", MeterScope.Global, true), + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, // Longer Meter Name is better - new object[] { new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true), - new InstrumentEnableRule("*", null, null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meterName.*", null, null, MeterScope.Global, true), - new InstrumentEnableRule("meterName", null, null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meter.Name", null, null, MeterScope.Global, true), - new InstrumentEnableRule("meter", null, null, MeterScope.Global, true) }, - new object[] { new InstrumentEnableRule("meter.Name", null, null, MeterScope.Global, true), - new InstrumentEnableRule("meter.*", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), + new InstrumentRule("*", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meterName.*", null, null, MeterScope.Global, true), + new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meter.Name", null, null, MeterScope.Global, true), + new InstrumentRule("meter", null, null, MeterScope.Global, true) }, + new object[] { new InstrumentRule("meter.Name", null, null, MeterScope.Global, true), + new InstrumentRule("meter.*", null, null, MeterScope.Global, true) }, // TODO: Scopes }; @@ -338,7 +338,7 @@ private class FakeMetricListener : IMetricsListener public Func OnPublished { get; set; } = (_) => (true, null); public Action OnCompleted { get; set; } = (_, _) => { }; - public IMetricsSource? Source { get; set; } + public IObservableInstrumentsSource? Source { get; set; } public FakeMeasurementCallback OnMeasurement { get; set; } public MeasurementCallback GetMeasurementHandler() where T : struct @@ -351,7 +351,7 @@ public bool InstrumentPublished(Instrument instrument, out object? userState) } public void MeasurementsCompleted(Instrument instrument, object? userState) => OnCompleted(instrument, userState); - public void SetSource(IMetricsSource source) => Source = source; + public void Initialize(IObservableInstrumentsSource source) => Source = source; } } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index 07a3f63c6d284c..80447a8ccae0ae 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -320,7 +320,6 @@ internal static void AddDefaultServices(HostBuilderContext hostingContext, IServ { metrics.AddConfiguration(hostingContext.Configuration.GetSection("Metrics")); }); - services.AddHostedService(); } internal static ServiceProviderOptions CreateDefaultServiceProviderOptions(HostBuilderContext context) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs deleted file mode 100644 index dd12bca549923a..00000000000000 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/MetricsHostedService.cs +++ /dev/null @@ -1,27 +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.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Diagnostics.Metrics; - -namespace Microsoft.Extensions.Hosting.Internal -{ - internal sealed class MetricsHostedService : IHostedService - { - private readonly IMetricsSubscriptionManager _manager; - - public MetricsHostedService(IMetricsSubscriptionManager manager) - { - _manager = manager; - } - - public Task StartAsync(CancellationToken cancellationToken) - { - _manager.Start(); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - } -} From 98f3eff5ed1ab3bd13ce400af1109d4f670668f0 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 1 Aug 2023 16:05:50 -0700 Subject: [PATCH 30/50] GetMeasurementHandlers(); --- ...soft.Extensions.Diagnostics.Abstractions.cs | 12 +++++++++++- .../src/Metrics/IMetricsListener.cs | 2 +- .../src/Metrics/MeasurementHandlers.cs | 18 ++++++++++++++++++ .../ref/Microsoft.Extensions.Diagnostics.cs | 2 +- .../src/Metrics/ConsoleMetricListener.cs | 12 +++++++++++- .../src/Metrics/ListenerSubscription.cs | 15 ++++++++------- .../tests/ListenerSubscriptionTests.cs | 17 +++++++++++++++-- 7 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index 78ab2ba9da9a9f..b7755519f36550 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -19,7 +19,17 @@ public interface IMetricsListener public void Initialize(IObservableInstrumentsSource source); public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState); public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState); - public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct; + public MeasurementHandlers GetMeasurementHandlers(); + } + public class MeasurementHandlers + { + public System.Diagnostics.Metrics.MeasurementCallback? ByteHandler { get; set; } + public System.Diagnostics.Metrics.MeasurementCallback? ShortHandler { get; set; } + public System.Diagnostics.Metrics.MeasurementCallback? IntHandler { get; set; } + public System.Diagnostics.Metrics.MeasurementCallback? LongHandler { get; set; } + public System.Diagnostics.Metrics.MeasurementCallback? FloatHandler { get; set; } + public System.Diagnostics.Metrics.MeasurementCallback? DoubleHandler { get; set; } + public System.Diagnostics.Metrics.MeasurementCallback? DecimalHandler { get; set; } } public interface IObservableInstrumentsSource { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs index e7e84b3826d411..fab75ac7f2da29 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs @@ -11,6 +11,6 @@ public interface IMetricsListener public void Initialize(IObservableInstrumentsSource source); public bool InstrumentPublished(Instrument instrument, out object? userState); public void MeasurementsCompleted(Instrument instrument, object? userState); - public MeasurementCallback GetMeasurementHandler() where T : struct; + public MeasurementHandlers GetMeasurementHandlers(); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs new file mode 100644 index 00000000000000..36cf898f94e3db --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.Metrics; + +namespace Microsoft.Extensions.Diagnostics.Metrics +{ + public class MeasurementHandlers + { + public MeasurementCallback? ByteHandler { get; set; } + public MeasurementCallback? ShortHandler { get; set; } + public MeasurementCallback? IntHandler { get; set; } + public MeasurementCallback? LongHandler { get; set; } + public MeasurementCallback? FloatHandler { get; set; } + public MeasurementCallback? DoubleHandler { get; set; } + public MeasurementCallback? DecimalHandler { get; set; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index d04345bcb3cd0d..e0e6c8e2250a4b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -27,7 +27,7 @@ internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable { internal TextWriter _textWriter; public string Name { get; } - public System.Diagnostics.Metrics.MeasurementCallback GetMeasurementHandler() where T : struct => throw null!; + public MeasurementHandlers GetMeasurementHandlers() => throw null!; public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState) => throw null!; public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState) => throw null!; public void Initialize(IObservableInstrumentsSource source) => throw null!; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index ae3fbf0e2c760d..d6d71f27f3fecd 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -36,7 +36,17 @@ public void MeasurementsCompleted(Instrument instrument, object? userState) } public void Initialize(IObservableInstrumentsSource source) => _source = source; - public MeasurementCallback GetMeasurementHandler() where T : struct => MeasurementHandler; + + public MeasurementHandlers GetMeasurementHandlers() => new MeasurementHandlers + { + ByteHandler = MeasurementHandler, + ShortHandler = MeasurementHandler, + IntHandler = MeasurementHandler, + LongHandler = MeasurementHandler, + FloatHandler = MeasurementHandler, + DoubleHandler = MeasurementHandler, + DecimalHandler = MeasurementHandler, + }; private void MeasurementHandler(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 30d13575d5442b..46a67200b68119 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -29,13 +29,14 @@ public void Initialize() { _meterListener.InstrumentPublished = InstrumentPublished; _meterListener.MeasurementsCompleted = MeasurementsCompleted; - _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); - _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); - _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); - _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); - _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); - _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); - _meterListener.SetMeasurementEventCallback(_metricsListener.GetMeasurementHandler()); + var handlers = _metricsListener.GetMeasurementHandlers(); + _meterListener.SetMeasurementEventCallback(handlers.ByteHandler); + _meterListener.SetMeasurementEventCallback(handlers.ShortHandler); + _meterListener.SetMeasurementEventCallback(handlers.IntHandler); + _meterListener.SetMeasurementEventCallback(handlers.LongHandler); + _meterListener.SetMeasurementEventCallback(handlers.FloatHandler); + _meterListener.SetMeasurementEventCallback(handlers.DoubleHandler); + _meterListener.SetMeasurementEventCallback(handlers.DecimalHandler); _meterListener.Start(); _metricsListener.Initialize(this); } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index fd938b6f162fc6..e65d4539117675 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -341,8 +341,21 @@ private class FakeMetricListener : IMetricsListener public IObservableInstrumentsSource? Source { get; set; } public FakeMeasurementCallback OnMeasurement { get; set; } - public MeasurementCallback GetMeasurementHandler() where T : struct - => (instrument, value, tags, state) => OnMeasurement(instrument, value, tags, state); + public MeasurementHandlers GetMeasurementHandlers() => new MeasurementHandlers + { + ByteHandler = CallOnMeasurement, + ShortHandler = CallOnMeasurement, + IntHandler = CallOnMeasurement, + LongHandler = CallOnMeasurement, + FloatHandler = CallOnMeasurement, + DoubleHandler = CallOnMeasurement, + DecimalHandler = CallOnMeasurement, + }; + + private void CallOnMeasurement(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) + { + OnMeasurement(instrument, measurement, tags, state); + } public bool InstrumentPublished(Instrument instrument, out object? userState) { From 5d4335cf99209eaa1395dcad4a7d2e520065cf48 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 1 Aug 2023 16:29:26 -0700 Subject: [PATCH 31/50] Intellisense and defaults --- ...rosoft.Extensions.Diagnostics.Abstractions.cs | 16 ++++++++-------- .../Metrics/MetricsBuilderExtensions.Enable.cs | 16 ++++++++-------- .../tests/ConsoleMetricListenerTests.cs | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index b7755519f36550..de41abf3d40ed7 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -58,15 +58,15 @@ public static class MetricsBuilderExtensions public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; - public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName) => throw null!; - public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null) => throw null!; + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; - public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; - public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; - public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName) => throw null!; - public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null) => throw null!; + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null) => throw null!; + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; } public class MetricsOptions { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs index 220f1d24d73d34..5d3a68bfdedb57 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs @@ -8,31 +8,31 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public static partial class MetricsBuilderExtensions { - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null) => builder.ConfigureRule(options => options.EnableMetrics(meterName)); - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName, scopes)); - public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName) + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null) => options.EnableMetrics(meterName: meterName, instrumentName: null); - public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: true); - public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName) + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null) => builder.ConfigureRule(options => options.DisableMetrics(meterName)); - public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null, string? instrumentName = null, string? listenerName = null, + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => builder.ConfigureRule(options => options.DisableMetrics(meterName, instrumentName, listenerName, scopes)); - public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName) + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null) => options.DisableMetrics(meterName: meterName, instrumentName: null); - public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null, string? instrumentName = null, string? listenerName = null, + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: false); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index b20c8ec7583e98..b051a189d32572 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -23,7 +23,7 @@ public void ListenerCanBeRegisteredViaDi() services.AddMetrics(builder => { builder.AddDebugConsole(); - builder.EnableMetrics("TestMeter", null, ConsoleMetrics.ListenerName); + builder.EnableMetrics("TestMeter", listenerName: ConsoleMetrics.ListenerName); }); using var sp = services.BuildServiceProvider(); sp.GetRequiredService().Validate(); From a7f1b439012d63f2fac20d4527ab6a5849e8b3c7 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 1 Aug 2023 16:39:47 -0700 Subject: [PATCH 32/50] Less default --- .../ref/Microsoft.Extensions.Diagnostics.Abstractions.cs | 2 +- .../src/Metrics/MetricsBuilderExtensions.Enable.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index de41abf3d40ed7..f86069b6215b4f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -58,7 +58,7 @@ public static class MetricsBuilderExtensions public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { throw null!; } public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { throw null!; } - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null) => throw null!; + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null) => throw null!; public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs index 5d3a68bfdedb57..ae8744bc0d6a5b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs @@ -8,28 +8,28 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public static partial class MetricsBuilderExtensions { - public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName = null) + public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => builder.ConfigureRule(options => options.EnableMetrics(meterName)); public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName, scopes)); - public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null) + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName) => options.EnableMetrics(meterName: meterName, instrumentName: null); public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: true); - public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null) + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName) => builder.ConfigureRule(options => options.DisableMetrics(meterName)); public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => builder.ConfigureRule(options => options.DisableMetrics(meterName, instrumentName, listenerName, scopes)); - public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null) + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName) => options.DisableMetrics(meterName: meterName, instrumentName: null); public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, From 0a0596bc4a1b9c0063ff0ed6e7d61c9dc5302c42 Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 7 Aug 2023 13:41:42 -0700 Subject: [PATCH 33/50] Less default refs --- .../ref/Microsoft.Extensions.Diagnostics.Abstractions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs index f86069b6215b4f..3a2dc49933deeb 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/ref/Microsoft.Extensions.Diagnostics.Abstractions.cs @@ -60,12 +60,12 @@ public static class MetricsBuilderExtensions public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; - public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName = null) => throw null!; + public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName) => throw null!; public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; - public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName = null) => throw null!; + public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName) => throw null!; public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; - public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName = null) => throw null!; + public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName) => throw null!; public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => throw null!; } public class MetricsOptions From ccac4703b68b53da7a480d3d7352e80548b497ce Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 09:13:53 -0700 Subject: [PATCH 34/50] Docs, cleaup --- .../README.md | 24 +++--- .../src/Metrics/IMetricsBuilder.cs | 7 ++ .../src/Metrics/IMetricsListener.cs | 30 ++++++++ .../Metrics/IObservableInstrumentsSource.cs | 9 +++ .../src/Metrics/InstrumentRule.cs | 41 ++++++++++ .../src/Metrics/MeasurementHandlers.cs | 30 ++++++++ .../src/Metrics/MeterScope.cs | 16 ++++ .../MetricsBuilderExtensions.AddListener.cs | 20 +++++ .../MetricsBuilderExtensions.Enable.cs | 64 ++++++++++++++++ .../src/Metrics/MetricsOptions.cs | 6 ++ ...Extensions.Diagnostics.Abstractions.csproj | 15 ++-- .../src/Resources/Strings.resx | 3 - .../README.md | 11 ++- .../MetricsBuilderConfigurationExtensions.cs | 15 +++- ...xtensions.Diagnostics.Configuration.csproj | 2 +- .../src/Resources/Strings.resx | 67 +++++++++++++++-- ...ons.Diagnostics.Configuration.Tests.csproj | 1 - .../src/Metrics/ConsoleMetricListener.cs | 1 + .../src/Metrics/ConsoleMetrics.cs | 6 ++ .../src/Metrics/ListenerSubscription.cs | 74 +++++++++++-------- .../src/Metrics/MetricsSubscriptionManager.cs | 2 +- .../Microsoft.Extensions.Diagnostics.csproj | 2 - 22 files changed, 372 insertions(+), 74 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md index e0b0a1710d498e..200587369cc7b9 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/README.md @@ -1,23 +1,21 @@ -# Microsoft.Extensions.Logging.Abstractions +# Microsoft.Extensions.Diagnostics.Abstractions -`Microsoft.Extensions.Logging.Abstractions` provides abstractions of logging. Interfaces defined in this package are implemented by classes in [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging/) and other logging packages. +`Microsoft.Extensions.Diagnostics.Abstractions` provides abstractions of diagnostics. Interfaces defined in this package are implemented by classes in [Microsoft.Extensions.Diagnostics](https://www.nuget.org/packages/Microsoft.Extensions.Diagnostics/) and other diagnostics packages. Commonly Used Types: -- `Microsoft.Extensions.Logging.ILogger` -- `Microsoft.Extensions.Logging.ILoggerFactory` -- `Microsoft.Extensions.Logging.ILogger` -- `Microsoft.Extensions.Logging.LogLevel` -- `Microsoft.Extensions.Logging.Logger` -- `Microsoft.Extensions.Logging.LoggerMessage` -- `Microsoft.Extensions.Logging.Abstractions.NullLogger` +- `Microsoft.Extensions.Diagnostics.Metrics.IMetricsBuilder` +- `Microsoft.Extensions.Diagnostics.Metrics.IMetricsListener` +- `Microsoft.Extensions.Diagnostics.Metrics.InstrumentRule` +- `Microsoft.Extensions.Diagnostics.Metrics.MeterScope` +- `Microsoft.Extensions.Diagnostics.Metrics.MetricsBuilderExtensions` +- `Microsoft.Extensions.Diagnostics.Metrics.MetricsOptions` -Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/logging. +Documentation can be found at https://learn.microsoft.com/en-us/dotnet/core/extensions/diagnostics. ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](../../libraries/README.md#primary-bar) -- [x] [We consider PRs that target this library for improvements to the logging source generator](../../libraries/README.md#secondary-bars) -The APIs and functionality are mature, but do get extended occasionally. +The APIs and functionality are new in .NET 8 and will continue to be developed. ## Deployment -[Microsoft.Extensions.Logging.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Abstractions) is included in the ASP.NET Core shared framework. The package is deployed as out-of-band (OOB) too and can be referenced into projects directly. \ No newline at end of file +[Microsoft.Extensions.Diagnostics.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.Diagnostics.Abstractions) is included in the ASP.NET Core shared framework. The package is deployed as out-of-band (OOB) too and can be referenced into projects directly. diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs index 8e9919aa6f317a..76e0d8dd451b21 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsBuilder.cs @@ -5,8 +5,15 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// Represents a type used to configure the metrics system by registering IMetricsListeners and using rules + /// to determine which metrics are enabled. + /// public interface IMetricsBuilder { + /// + /// The application . This is used by extension methods to register services. + /// IServiceCollection Services { get; } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs index fab75ac7f2da29..e22ea317c1dcaf 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs @@ -5,12 +5,42 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// Represents a type used to listen to metrics emitted from the system. + /// public interface IMetricsListener { + /// + /// The name of the listener. This is used to identify the listener in the rules configuration. + /// public string Name { get; } + + /// + /// Called once by the runtime to provide a used to pull for fresh metrics data. + /// + /// A that can be called to request current metrics. public void Initialize(IObservableInstrumentsSource source); + + /// + /// Called when a new instrument is created and enabled by a matching rule. + /// + /// The new . + /// Listener state associated with this instrument. This should be returned to + /// and . + /// Returns true if the listener wants to subscribe to this instrument, otherwise false. public bool InstrumentPublished(Instrument instrument, out object? userState); + + /// + /// Called when a instrument is disabled by the producer or a rules change. + /// + /// The being disabled. + /// The original listener state returned by . public void MeasurementsCompleted(Instrument instrument, object? userState); + + /// + /// Called once to get the that will be used to process measurements. + /// + /// The . public MeasurementHandlers GetMeasurementHandlers(); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs index 998558388c996a..351217d4b93087 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs @@ -1,10 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.Metrics; + namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// A callback registered with that the listener + /// can call to request the current set of metrics for enabled instruments be sent to the listener's 's. + /// public interface IObservableInstrumentsSource { + /// + /// Requests that the current set of metrics for enabled instruments be sent to the listener's 's. + /// public void RecordObservableInstruments(); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs index 6613ab7d5f48b2..0f5f0fc3eaba1a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs @@ -1,14 +1,55 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.Metrics; + namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// A set of parameters used to determine which instruments are enabled for which listeners. Unspecified + /// parameters match anything. + /// + /// + /// The most specific rule that matches a given instrument will be used. The priority of parameters is as follows: + /// - MeterName, either an exact match, or the longest prefix match. See . + /// - InstrumentName, an exact match. . + /// - ListenerName, an exact match. . + /// - Scopes + /// + /// The or prefix. + /// The . + /// The . + /// The 's to consider. + /// Enables or disabled the matched instrument for this listener. public class InstrumentRule(string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) { + /// + /// The , either an exact match or the longest prefix match. Only full segment matches are considered. + /// All meters are matched if this is null. + /// public string? MeterName { get; } = meterName; + + /// + /// The , an exact match. + /// All instruments for the given meter are matched if this is null. + /// public string? InstrumentName { get; } = instrumentName; + + /// + /// The , an exact match. + /// All listeners are matched if this is null. + /// public string? ListenerName { get; } = listenerName; + + /// + /// The . This is used to distinguish between meters created via constructors () + /// and those created via Dependency Injection with ()."/>. + /// public MeterScope Scopes { get; } = scopes; + + /// + /// Indicates if the instrument should be enabled for the listener. + /// public bool Enable { get; } = enable; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs index 36cf898f94e3db..027c8e1e639dea 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeasurementHandlers.cs @@ -5,14 +5,44 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// A set of supported measurement types. If a listener does not support a given type, the measurement will be skipped. + /// public class MeasurementHandlers { + /// + /// A for . If null, byte measurements will be skipped. + /// public MeasurementCallback? ByteHandler { get; set; } + + /// + /// A for . If null, short measurements will be skipped. + /// public MeasurementCallback? ShortHandler { get; set; } + + /// + /// A for . If null, int measurements will be skipped. + /// public MeasurementCallback? IntHandler { get; set; } + + /// + /// A for . If null, long measurements will be skipped. + /// public MeasurementCallback? LongHandler { get; set; } + + /// + /// A for . If null, float measurements will be skipped. + /// public MeasurementCallback? FloatHandler { get; set; } + + /// + /// A for . If null, double measurements will be skipped. + /// public MeasurementCallback? DoubleHandler { get; set; } + + /// + /// A for . If null, decimal measurements will be skipped. + /// public MeasurementCallback? DecimalHandler { get; set; } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs index 32862152854d3c..a117c7b4371f9f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MeterScope.cs @@ -2,14 +2,30 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.Metrics; namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// This is used by to distinguish between meters created via constructors () + /// and those created via Dependency Injection with ()."/>. + /// [Flags] public enum MeterScope { + /// + /// No scope is specified. This should not be used. + /// None = 0, + + /// + /// Indicates instances created via constructors. + /// Global, + + /// + /// Indicates instances created via Dependency Injection with . + /// Local } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs index edb66bdafda46d..040acbb20cba08 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs @@ -8,8 +8,17 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// Extension methods for to add or clear registrations. + /// public static partial class MetricsBuilderExtensions { + /// + /// Registers a new of type . + /// + /// The implementation type of the listener. + /// The . + /// Returns the original for chaining. public static IMetricsBuilder AddListener<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(this IMetricsBuilder builder) where T : class, IMetricsListener { ThrowHelper.ThrowIfNull(builder); @@ -17,6 +26,12 @@ public static partial class MetricsBuilderExtensions return builder; } + /// + /// Registers a new instance. + /// + /// The implementation type of the listener. + /// The . + /// Returns the original for chaining. public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetricsListener listener) { ThrowHelper.ThrowIfNull(builder); @@ -24,6 +39,11 @@ public static IMetricsBuilder AddListener(this IMetricsBuilder builder, IMetrics return builder; } + /// + /// Removes all registrations from the dependency injection container. + /// + /// The . + /// Returns the original for chaining. public static IMetricsBuilder ClearListeners(this IMetricsBuilder builder) { ThrowHelper.ThrowIfNull(builder); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs index ae8744bc0d6a5b..8ad6d6d77752c9 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs @@ -2,36 +2,100 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.Metrics; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// Extension methods for to enable or disable metrics. + /// public static partial class MetricsBuilderExtensions { + /// + /// Enables all 's for the given meter, for all registered 's. + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The original for chaining. public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName) => builder.ConfigureRule(options => options.EnableMetrics(meterName)); + /// + /// Enables a specified for the given and . + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The . A null value matches all instruments. + /// The .Name. A null value matches all listeners. + /// Indicates which 's to consider. Default to all scopes. + /// The original for chaining. public static IMetricsBuilder EnableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => builder.ConfigureRule(options => options.EnableMetrics(meterName, instrumentName, listenerName, scopes)); + /// + /// Enables all 's for the given meter, for all registered 's. + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The original for chaining. public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName) => options.EnableMetrics(meterName: meterName, instrumentName: null); + /// + /// Enables a specified for the given and . + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The . A null value matches all instruments. + /// The .Name. A null value matches all listeners. + /// Indicates which 's to consider. Default to all scopes. + /// The original for chaining. public static MetricsOptions EnableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: true); + /// + /// Disables all 's for the given meter, for all registered 's. + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The original for chaining. public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName) => builder.ConfigureRule(options => options.DisableMetrics(meterName)); + /// + /// Disables a specified for the given and . + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The . A null value matches all instruments. + /// The .Name. A null value matches all listeners. + /// Indicates which 's to consider. Default to all scopes. + /// The original for chaining. public static IMetricsBuilder DisableMetrics(this IMetricsBuilder builder, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => builder.ConfigureRule(options => options.DisableMetrics(meterName, instrumentName, listenerName, scopes)); + /// + /// Disables all 's for the given meter, for all registered 's. + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The original for chaining. public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName) => options.DisableMetrics(meterName: meterName, instrumentName: null); + /// + /// Disables a specified for the given and . + /// + /// The . + /// The or prefix. A null value matches all meters. + /// The . A null value matches all instruments. + /// The .Name. A null value matches all listeners. + /// Indicates which 's to consider. Default to all scopes. + /// The original for chaining. public static MetricsOptions DisableMetrics(this MetricsOptions options, string? meterName, string? instrumentName = null, string? listenerName = null, MeterScope scopes = MeterScope.Global | MeterScope.Local) => options.AddRule(meterName, instrumentName, listenerName, scopes, enable: false); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsOptions.cs index c0172d760cd54c..1dfbe1f57f148e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsOptions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsOptions.cs @@ -5,8 +5,14 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// Options for configuring the metrics system. + /// public class MetricsOptions { + /// + /// A list of 's that identify which metrics, instruments, and listeners are enabled. + /// public IList Rules { get; } = new List(); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj index 441f9cffa29824..a56170373401ee 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Microsoft.Extensions.Diagnostics.Abstractions.csproj @@ -4,8 +4,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true true - - $(NoWarn);CS1591 @@ -13,13 +11,12 @@ Diagnostic abstractions for Microsoft.Extensions.Diagnostics. Commonly Used Types: -Microsoft.Extensions.Logging.ILogger -Microsoft.Extensions.Logging.ILoggerFactory -Microsoft.Extensions.Logging.ILogger<TCategoryName> -Microsoft.Extensions.Logging.LogLevel -Microsoft.Extensions.Logging.Logger<T> -Microsoft.Extensions.Logging.LoggerMessage -Microsoft.Extensions.Logging.Abstractions.NullLogger +Microsoft.Extensions.Diagnostics.Metrics.IMetricsBuilder +Microsoft.Extensions.Diagnostics.Metrics.IMetricsListener +Microsoft.Extensions.Diagnostics.Metrics.InstrumentRule +Microsoft.Extensions.Diagnostics.Metrics.MeterScope +Microsoft.Extensions.Diagnostics.Metrics.MetricsBuilderExtensions +Microsoft.Extensions.Diagnostics.Metrics.MetricsOptions diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx index 5112bed03e659f..1af7de150c99c1 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Resources/Strings.resx @@ -117,7 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - The format string '{0}' does not have the expected number of named parameters. Expected {1} parameter(s) but found {2} parameter(s). - \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md index 776b53340b7833..952325ae2dab64 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md @@ -1,15 +1,14 @@ -# Microsoft.Extensions.Diagnostics +# Microsoft.Extensions.Diagnostics.Configuration -`Microsoft.Extensions.Diagnostics` contains the default implementation of meter factory and extension methods for registering this default meter factory to the DI. +`Microsoft.Extensions.Diagnostics.Configuration` contains the `IConfiguration` integration for Meters and Metrics. Commonly Used APIS: -- MetricsServiceExtensions.AddMetrics(this IServiceCollection services) -- MeterFactoryExtensions.Create(this IMeterFactory, string name, string? version = null, IEnumerable> tags = null, object? scope = null) +- MetricsBuilderConfigurationExtensions.AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](https://github.com/dotnet/runtime/tree/main/src/libraries#contribution-bar) -The APIs and functionality are mature, but do get extended occasionally. +The APIs and functionality are new in .NET 8 and will continue to be developed. ## Deployment -[Microsoft.Extensions.Diagnostics](https://www.nuget.org/packages/Microsoft.Extensions.Diagnostics) is included in the ASP.NET Core shared framework. The package is deployed as out-of-band (OOB) too and can be referenced into projects directly. \ No newline at end of file +[Microsoft.Extensions.Diagnostics.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Diagnostics.Configuration) is included in the ASP.NET Core shared framework. The package is deployed as out-of-band (OOB) too and can be referenced into projects directly. diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs index f8f255ea7031cb..c2bfc43f0f8bc8 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs @@ -1,13 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.Metrics; using Microsoft.Extensions.Configuration; namespace Microsoft.Extensions.Diagnostics.Metrics { public static class MetricsBuilderConfigurationExtensions { - // TODO: - public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) => builder; + /// + /// Reads metrics configuration from the provided section and configures + /// which 's, 's, and 's are enabled. + /// + /// The . + /// The section to load. + /// The original for chaining. + public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) + { + // TODO: + return builder; + } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj index afab95956105c2..f4d3b3ed180a61 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj @@ -10,7 +10,7 @@ should be removed in order to re-enable validation. --> true true - This package includes the default implementation of IMeterFactory and additional extension methods to easily register it with the Dependency Injection framework. + This package includes the IConfiguration integration for configuring Meters, Instruments, and IMetricsListeners. diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx index ab0d5ca5a461b3..1af7de150c99c1 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx @@ -1,4 +1,64 @@ - + + + @@ -57,7 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - The meter factory does not allow a custom scope value when creating a meter. - - + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj index 0296919ebc510c..924a31b177e206 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj @@ -4,7 +4,6 @@ $(NetCoreAppCurrent);$(NetFrameworkMinimum) true true - false diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs index d6d71f27f3fecd..e4bdf5d1d5320a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs @@ -50,6 +50,7 @@ public void MeasurementsCompleted(Instrument instrument, object? userState) private void MeasurementHandler(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct { + Debug.Assert(state == this); WriteLine($"{instrument.Meter.Name}-{instrument.Name} {measurement} {instrument.Unit}"); } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs index b0a557e3fe77b4..051e424076636b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs @@ -3,8 +3,14 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// Constants for the Console metrics listener. + /// public static class ConsoleMetrics { + /// + /// The name of the listener used in configuration and enabling instruments. + /// public static string ListenerName => "Console"; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 46a67200b68119..06506f798030cd 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Metrics; +#if NET +using System.Runtime.CompilerServices; +#endif namespace Microsoft.Extensions.Diagnostics.Metrics { @@ -12,9 +15,12 @@ internal class ListenerSubscription : IObservableInstrumentsSource, IDisposable { private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; - private readonly HashSet _instruments = new(); - private readonly HashSet _enabled = new(); - private readonly Dictionary _published = new(); +#if NET + private readonly ConditionalWeakTable _instruments = new(); +#else + // TODO: Can't enumerate ConditionalWeakTable in .NET Standard 2.0. How do we clean up the instruments? + private readonly Dictionary _instruments = new(); +#endif private IList _rules = Array.Empty(); private bool _disposed; @@ -50,19 +56,19 @@ private void InstrumentPublished(Instrument instrument, MeterListener _) return; } - if (_instruments.Contains(instrument)) + if (_instruments.TryGetValue(instrument, out var _)) { Debug.Assert(false, "InstrumentPublished called twice for the same instrument"); return; } - _instruments.Add(instrument); - RefreshInstrument(instrument); + var status = new InstrumentStatus(); + _instruments.Add(instrument, status); + RefreshInstrument(instrument, status); } } // Called when we call DisableMeasurementEvents, like when a rule is disabled. - // TODO: When should we remove an Instrument from _instruments? private void MeasurementsCompleted(Instrument instrument, object? state) { lock (_instruments) @@ -72,16 +78,19 @@ private void MeasurementsCompleted(Instrument instrument, object? state) return; } - _enabled.Remove(instrument); + if (!_instruments.TryGetValue(instrument, out var status)) + { + Debug.Assert(false, "MeasurementsCompleted called for an instrument that was never published"); + return; + } + status.Enabled = false; - if (_published.TryGetValue(instrument, out var localState)) + if (status.Published) { - _published.Remove(instrument); - var listenerEnabled = localState.Item1; - var userState = localState.Item2; - if (listenerEnabled) + status.Published = false; + if (status.ListenerEnabled) { - _metricsListener.MeasurementsCompleted(instrument, userState); + _metricsListener.MeasurementsCompleted(instrument, status.State); } } } @@ -98,17 +107,17 @@ internal void UpdateRules(IList rules) _rules = rules; - foreach (var instrument in _instruments) + foreach (var instrumentPair in _instruments) { - RefreshInstrument(instrument); + RefreshInstrument(instrumentPair.Key, instrumentPair.Value); } } } // Called under _instrument lock - private void RefreshInstrument(Instrument instrument) + private void RefreshInstrument(Instrument instrument, InstrumentStatus status) { - var alreadyEnabled = _enabled.Contains(instrument); + var alreadyEnabled = status.Enabled; var enable = false; var rule = GetMostSpecificRule(instrument); if (rule != null) @@ -118,29 +127,24 @@ private void RefreshInstrument(Instrument instrument) if (!enable && alreadyEnabled) { - _enabled.Remove(instrument); + status.Enabled = false; _meterListener.DisableMeasurementEvents(instrument); } else if (enable && !alreadyEnabled) { // The first time we enable an instrument, we need to call InstrumentPublished. - bool listenerEnabled; - if (_published.TryGetValue(instrument, out var metadata)) - { - listenerEnabled = metadata.Item1; - } - else + if (!status.Published) { - listenerEnabled = _metricsListener.InstrumentPublished(instrument, out var state); - // However, a listener might decline to enable the instrument, remember that. - _published.Add(instrument, (listenerEnabled, state)); + status.Published = true; + status.ListenerEnabled = _metricsListener.InstrumentPublished(instrument, out var state); + status.State = state; } - if (listenerEnabled) + if (status.ListenerEnabled) { - _meterListener.EnableMeasurementEvents(instrument); - _enabled.Add(instrument); + _meterListener.EnableMeasurementEvents(instrument, status.State); + status.Enabled = true; } } } @@ -280,5 +284,13 @@ public void Dispose() _disposed = true; _meterListener.Dispose(); } + + private class InstrumentStatus + { + public object? State { get; set; } + public bool Published { get; set; } + public bool Enabled { get; set; } + public bool ListenerEnabled { get; set; } + } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs index 7a68ba7edb5f6a..ae0589d9eb3e17 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - internal partial class MetricsSubscriptionManager + internal class MetricsSubscriptionManager { private readonly ListenerSubscription[] _listeners; private readonly IDisposable? _changeTokenRegistration; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index a9ce586efdc191..537203974df339 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -3,8 +3,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true - - $(NoWarn);CS1591 From 1d0197308844c1b4d5ff7f460dbdfa3e95cb4c0a Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 10:05:00 -0700 Subject: [PATCH 35/50] More scopes --- .../ref/Microsoft.Extensions.Diagnostics.cs | 2 +- .../src/Metrics/ListenerSubscription.cs | 38 +++++++++-- .../tests/ListenerSubscriptionTests.cs | 67 +++++++++++-------- 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index e0e6c8e2250a4b..3834738f3ea36c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -39,7 +39,7 @@ internal ListenerSubscription(Microsoft.Extensions.Diagnostics.Metrics.IMetricsL public void Initialize() { } internal void UpdateRules(IList rules) { } internal static bool RuleMatches(InstrumentRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw null!; - internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best) => throw null!; + internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best, bool isLocalScope) => throw null!; } internal sealed class DefaultMeterFactory : System.Diagnostics.Metrics.IMeterFactory { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 06506f798030cd..16a1b8bee060e7 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -154,7 +154,8 @@ private void RefreshInstrument(Instrument instrument, InstrumentStatus status) InstrumentRule? best = null; foreach (var rule in _rules) { - if (RuleMatches(rule, instrument, _metricsListener.Name) && IsMoreSpecific(rule, best)) + if (RuleMatches(rule, instrument, _metricsListener.Name) + && IsMoreSpecific(rule, best, isLocalScope: instrument.Meter.Scope != null)) { best = rule; } @@ -224,9 +225,8 @@ internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, str } // Everything must already match the Instrument and listener, or be blank. - // Which rule has more non-blank fields? Or longer Meter name? // internal for testing - internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best) + internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best, bool isLocalScope) { if (best == null) { @@ -272,9 +272,37 @@ internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best) return false; } - // Scopes TODO: Local is more specific than global (or local & global). + // Scope - return false; + // Already matched as local + if (isLocalScope) + { + // Local is more specific than Local+Global + if (!rule.Scopes.HasFlag(MeterScope.Global) && best.Scopes.HasFlag(MeterScope.Global)) + { + return true; + } + else if (rule.Scopes.HasFlag(MeterScope.Global) && !best.Scopes.HasFlag(MeterScope.Global)) + { + return false; + } + } + // Already matched as global + else + { + // Global is more specific than Local+Global + if (!rule.Scopes.HasFlag(MeterScope.Local) && best.Scopes.HasFlag(MeterScope.Local)) + { + return true; + } + else if (rule.Scopes.HasFlag(MeterScope.Local) && !best.Scopes.HasFlag(MeterScope.Local)) + { + return false; + } + } + + // All things being equal, take the last one. + return true; } public void RecordObservableInstruments() => _meterListener.RecordObservableInstruments(); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index e65d4539117675..2041b1813e2a88 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -16,7 +16,7 @@ public class ListenerSubscriptionTests [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void SubscriptionReceivesNewGlobalMetersAndInstruments() { - // RemoteExecutor.Invoke(() => + RemoteExecutor.Invoke(() => { var publishedTcs = new TaskCompletionSource(); var completedTcs = new TaskCompletionSource(); @@ -68,7 +68,7 @@ public void SubscriptionReceivesNewGlobalMetersAndInstruments() Assert.True(completedTcs.Task.IsCompleted); - }//).Dispose(); + }).Dispose(); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -263,74 +263,87 @@ public void RuleMatchesNegativeTest(string meterName, string instrumentName, str [Theory] [MemberData(nameof(IsMoreSpecificTestData))] - public void IsMoreSpecificTest(InstrumentRule rule, InstrumentRule? best) + public void IsMoreSpecificTest(InstrumentRule rule, InstrumentRule? best, bool isLocalScope) { - Assert.True(ListenerSubscription.IsMoreSpecific(rule, best)); + Assert.True(ListenerSubscription.IsMoreSpecific(rule, best, isLocalScope)); if (best != null) { - Assert.False(ListenerSubscription.IsMoreSpecific(best, rule)); + Assert.False(ListenerSubscription.IsMoreSpecific(best, rule, isLocalScope)); } } public static IEnumerable IsMoreSpecificTestData() => new object[][] { // Anything is better than null - new object[] { new InstrumentRule(null, null, null, MeterScope.Global, true), null }, + new object[] { new InstrumentRule(null, null, null, MeterScope.Global, true), null, false }, // Any field is better than empty new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), - new InstrumentRule(null, null, null, MeterScope.Global, true) }, + new InstrumentRule(null, null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), - new InstrumentRule(null, null, null, MeterScope.Global, true) }, + new InstrumentRule(null, null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), - new InstrumentRule(null, null, null, MeterScope.Global, true) }, + new InstrumentRule(null, null, null, MeterScope.Global, true), false }, // Meter > Instrument > Listener new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), - new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), - new InstrumentRule(null, "instrumentName", "listenerName", MeterScope.Global, true) }, + new InstrumentRule(null, "instrumentName", "listenerName", MeterScope.Global, true), false }, new object[] { new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, // Multiple fields are better than one. new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), - new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + new InstrumentRule("meterName", null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", null, "listenerName", MeterScope.Global, true), - new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + new InstrumentRule("meterName", null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), - new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + new InstrumentRule("meterName", null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), - new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", null, "listenerName", MeterScope.Global, true), - new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), - new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true) }, + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", null, "listenerName", MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true) }, + new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, // Longer Meter Name is better new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), - new InstrumentRule("*", null, null, MeterScope.Global, true) }, + new InstrumentRule("*", null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName.*", null, null, MeterScope.Global, true), - new InstrumentRule("meterName", null, null, MeterScope.Global, true) }, + new InstrumentRule("meterName", null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meter.Name", null, null, MeterScope.Global, true), - new InstrumentRule("meter", null, null, MeterScope.Global, true) }, + new InstrumentRule("meter", null, null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meter.Name", null, null, MeterScope.Global, true), - new InstrumentRule("meter.*", null, null, MeterScope.Global, true) }, + new InstrumentRule("meter.*", null, null, MeterScope.Global, true), false }, - // TODO: Scopes + // Scopes: Local > Global+Local, Global > Global+Local + new object[] { new InstrumentRule(null, null, null, MeterScope.Local, true), + new InstrumentRule(null, null, null, MeterScope.Global | MeterScope.Local, true), true }, + new object[] { new InstrumentRule(null, null, null, MeterScope.Global, true), + new InstrumentRule(null, null, null, MeterScope.Global | MeterScope.Local, true), false }, }; + [Fact] + public void EqualMatchRulesTakeLastTest() + { + var emptyTrue = new InstrumentRule(null, null, null, MeterScope.Global, true); + var emptyFalse = new InstrumentRule(null, null, null, MeterScope.Global, false); + Assert.True(ListenerSubscription.IsMoreSpecific(emptyFalse, emptyTrue, isLocalScope: false)); + Assert.True(ListenerSubscription.IsMoreSpecific(emptyTrue, emptyFalse, isLocalScope: false)); + } + public delegate void FakeMeasurementCallback(Instrument instrument, object measurement, ReadOnlySpan> tags, object? state); private class FakeMetricListener : IMetricsListener { From 6ccde3878c25e7fc88cfa52aa8895d76333dce29 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 13:51:45 -0700 Subject: [PATCH 36/50] Renames --- .../ref/Microsoft.Extensions.Diagnostics.cs | 12 ++++-------- .../src/Metrics/ConsoleMetrics.cs | 2 +- ...tricListener.cs => DebugConsoleMetricListener.cs} | 6 +++--- .../src/Metrics/MetricsBuilderConsoleExtensions.cs | 2 +- .../tests/ConsoleMetricListenerTests.cs | 4 ++-- 5 files changed, 11 insertions(+), 15 deletions(-) rename src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/{ConsoleMetricListener.cs => DebugConsoleMetricListener.cs} (92%) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 3834738f3ea36c..3dad1ba1537a3a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -1,10 +1,6 @@ // 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.IO; - namespace Microsoft.Extensions.DependencyInjection { public static class MetricsServiceExtensions @@ -17,15 +13,15 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { public static class ConsoleMetrics { - public static string ListenerName => throw null!; + public static string DebugListenerName => throw null!; } public static class MetricsBuilderConsoleExtensions { public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => throw null!; } - internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable + internal sealed class DebugConsoleMetricListener : IMetricsListener, System.IDisposable { - internal TextWriter _textWriter; + internal System.IO.TextWriter _textWriter; public string Name { get; } public MeasurementHandlers GetMeasurementHandlers() => throw null!; public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState) => throw null!; @@ -37,7 +33,7 @@ internal sealed class ListenerSubscription { internal ListenerSubscription(Microsoft.Extensions.Diagnostics.Metrics.IMetricsListener listener) { } public void Initialize() { } - internal void UpdateRules(IList rules) { } + internal void UpdateRules(System.Collections.Generic.IList rules) { } internal static bool RuleMatches(InstrumentRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw null!; internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best, bool isLocalScope) => throw null!; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs index 051e424076636b..4a2136677b21b6 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetrics.cs @@ -11,6 +11,6 @@ public static class ConsoleMetrics /// /// The name of the listener used in configuration and enabling instruments. /// - public static string ListenerName => "Console"; + public static string DebugListenerName => "DebugConsole"; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs similarity index 92% rename from src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs rename to src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs index e4bdf5d1d5320a..ffda9607a4ad0b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs @@ -9,18 +9,18 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - internal sealed class ConsoleMetricListener : IMetricsListener, IDisposable + internal sealed class DebugConsoleMetricListener : IMetricsListener, IDisposable { private readonly Timer _timer; internal TextWriter _textWriter = Console.Out; private IObservableInstrumentsSource? _source; - public ConsoleMetricListener() + public DebugConsoleMetricListener() { _timer = new Timer(OnTimer, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); } - public string Name => ConsoleMetrics.ListenerName; + public string Name => ConsoleMetrics.DebugListenerName; public bool InstrumentPublished(Instrument instrument, out object? userState) { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs index dfdc1d0d8daf5e..b5613cc46e1a73 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConsoleExtensions.cs @@ -13,6 +13,6 @@ public static class MetricsBuilderConsoleExtensions /// /// The metrics builder. /// The original metrics builder for chaining. - public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => builder.AddListener(); + public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => builder.AddListener(); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs index b051a189d32572..c139d3f74360a7 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs @@ -23,13 +23,13 @@ public void ListenerCanBeRegisteredViaDi() services.AddMetrics(builder => { builder.AddDebugConsole(); - builder.EnableMetrics("TestMeter", listenerName: ConsoleMetrics.ListenerName); + builder.EnableMetrics("TestMeter", listenerName: ConsoleMetrics.DebugListenerName); }); using var sp = services.BuildServiceProvider(); sp.GetRequiredService().Validate(); var listener = sp.GetRequiredService(); - var consoleListener = Assert.IsType(listener); + var consoleListener = Assert.IsType(listener); var output = new StringWriter(); consoleListener._textWriter = output; From 224c834b07c18774b65c577637a7224f912e4fa5 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 14:13:54 -0700 Subject: [PATCH 37/50] Merge Configuration assembly --- ...t.Extensions.Diagnostics.Configuration.sln | 218 ------------------ .../README.md | 14 -- ...ft.Extensions.Diagnostics.Configuration.cs | 21 -- ...xtensions.Diagnostics.Configuration.csproj | 16 -- ...xtensions.Diagnostics.Configuration.csproj | 21 -- .../src/Resources/Strings.resx | 120 ---------- ...ons.Diagnostics.Configuration.Tests.csproj | 16 -- .../Microsoft.Extensions.Diagnostics.sln | 14 ++ .../README.md | 1 + .../ref/Microsoft.Extensions.Diagnostics.cs | 15 ++ .../Microsoft.Extensions.Diagnostics.csproj | 1 + .../IMetricListenerConfiguration.cs | 7 + .../IMetricListenerConfigurationFactory.cs | 8 + .../MetricsBuilderConfigurationExtensions.cs | 3 + .../Microsoft.Extensions.Diagnostics.csproj | 1 + .../Microsoft.Extensions.Hosting.sln | 14 -- .../src/Microsoft.Extensions.Hosting.csproj | 2 +- 17 files changed, 51 insertions(+), 441 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj rename src/libraries/{Microsoft.Extensions.Diagnostics.Configuration => Microsoft.Extensions.Diagnostics}/src/Metrics/Configuration/IMetricListenerConfiguration.cs (54%) rename src/libraries/{Microsoft.Extensions.Diagnostics.Configuration => Microsoft.Extensions.Diagnostics}/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs (51%) rename src/libraries/{Microsoft.Extensions.Diagnostics.Configuration => Microsoft.Extensions.Diagnostics}/src/Metrics/MetricsBuilderConfigurationExtensions.cs (87%) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln deleted file mode 100644 index f30a3d0ca671c4..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/Microsoft.Extensions.Diagnostics.Configuration.sln +++ /dev/null @@ -1,218 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.6.33801.468 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\ref\Microsoft.Bcl.AsyncInterfaces.csproj", "{E7A3B914-598D-4ABC-B973-6CC444DAFE52}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bcl.AsyncInterfaces", "..\Microsoft.Bcl.AsyncInterfaces\src\Microsoft.Bcl.AsyncInterfaces.csproj", "{280FDDEA-50B1-4BD3-83B1-475B15829538}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Abstractions", "..\Microsoft.Extensions.DependencyInjection.Abstractions\ref\Microsoft.Extensions.DependencyInjection.Abstractions.csproj", "{CE53C256-EE31-4E4A-8A05-70350840448F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Abstractions", "..\Microsoft.Extensions.DependencyInjection.Abstractions\src\Microsoft.Extensions.DependencyInjection.Abstractions.csproj", "{3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection", "..\Microsoft.Extensions.DependencyInjection\ref\Microsoft.Extensions.DependencyInjection.csproj", "{C175A982-E0E0-4E22-8A3B-0A9C00EE7730}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection", "..\Microsoft.Extensions.DependencyInjection\src\Microsoft.Extensions.DependencyInjection.csproj", "{1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "ref\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{EF75497C-6CB7-4471-980A-619EA1AB8CF6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "src\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{09E28D94-B771-48EB-800C-5A80C2C0055C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.DiagnosticSource", "..\System.Diagnostics.DiagnosticSource\ref\System.Diagnostics.DiagnosticSource.csproj", "{F452AA57-7BEC-4E64-BAB5-166078865EC4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Diagnostics.DiagnosticSource", "..\System.Diagnostics.DiagnosticSource\src\System.Diagnostics.DiagnosticSource.csproj", "{AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComInterfaceGenerator", "..\System.Runtime.InteropServices\gen\ComInterfaceGenerator\ComInterfaceGenerator.csproj", "{ED105ED3-0060-4035-AD5E-1F857F94C2DF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryImportGenerator", "..\System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj", "{0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Interop.SourceGeneration", "..\System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj", "{3FEA305D-0B5F-46A6-8E18-587387FCBFBF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime", "..\System.Runtime\ref\System.Runtime.csproj", "{3D040E9F-C39B-49C6-8C87-68D427AECA8F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9BF048D0-411D-4C2A-8C32-3A3255501D27}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A447D0CB-601B-479E-A2B2-76E48F5D4D61}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{66953A8A-9E31-486F-AF8E-7310F6707E4F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\ref\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{AA790584-200C-4301-8545-8B2854B2F6CC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\src\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{40525D17-4553-405E-8B21-4603B07D126A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration.Tests", "tests\Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj", "{85133A0C-2427-4085-AA71-02881F46AEAE}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics", "..\Microsoft.Extensions.Diagnostics\ref\Microsoft.Extensions.Diagnostics.csproj", "{BCB6C67A-A765-41A0-80EA-7807720714D0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics", "..\Microsoft.Extensions.Diagnostics\src\Microsoft.Extensions.Diagnostics.csproj", "{CB8A4E34-76F8-408A-98BE-5881D12BA02C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Tests", "..\Microsoft.Extensions.Diagnostics\tests\Microsoft.Extensions.Diagnostics.Tests.csproj", "{F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "..\Microsoft.Extensions.Configuration.Abstractions\ref\Microsoft.Extensions.Configuration.Abstractions.csproj", "{0D43A981-ED8F-434A-B349-3227DDD38D2E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "..\Microsoft.Extensions.Configuration.Abstractions\src\Microsoft.Extensions.Configuration.Abstractions.csproj", "{9AC40671-E708-4FE1-B5CB-ADBBC425F793}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Primitives", "..\Microsoft.Extensions.Primitives\ref\Microsoft.Extensions.Primitives.csproj", "{CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Primitives", "..\Microsoft.Extensions.Primitives\src\Microsoft.Extensions.Primitives.csproj", "{DFCDD214-BAED-468D-BC61-B1D536FF0AFF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Options", "..\Microsoft.Extensions.Options\src\Microsoft.Extensions.Options.csproj", "{59D2147E-1E97-49F1-87AF-F5C50DCD73B6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Options", "..\Microsoft.Extensions.Options\ref\Microsoft.Extensions.Options.csproj", "{F8409C3F-4C46-4115-AA48-518A7490A14A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE}.Release|Any CPU.Build.0 = Release|Any CPU - {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7A3B914-598D-4ABC-B973-6CC444DAFE52}.Release|Any CPU.Build.0 = Release|Any CPU - {280FDDEA-50B1-4BD3-83B1-475B15829538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {280FDDEA-50B1-4BD3-83B1-475B15829538}.Debug|Any CPU.Build.0 = Debug|Any CPU - {280FDDEA-50B1-4BD3-83B1-475B15829538}.Release|Any CPU.ActiveCfg = Release|Any CPU - {280FDDEA-50B1-4BD3-83B1-475B15829538}.Release|Any CPU.Build.0 = Release|Any CPU - {CE53C256-EE31-4E4A-8A05-70350840448F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE53C256-EE31-4E4A-8A05-70350840448F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE53C256-EE31-4E4A-8A05-70350840448F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE53C256-EE31-4E4A-8A05-70350840448F}.Release|Any CPU.Build.0 = Release|Any CPU - {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6}.Release|Any CPU.Build.0 = Release|Any CPU - {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C175A982-E0E0-4E22-8A3B-0A9C00EE7730}.Release|Any CPU.Build.0 = Release|Any CPU - {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6}.Release|Any CPU.Build.0 = Release|Any CPU - {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF75497C-6CB7-4471-980A-619EA1AB8CF6}.Release|Any CPU.Build.0 = Release|Any CPU - {09E28D94-B771-48EB-800C-5A80C2C0055C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {09E28D94-B771-48EB-800C-5A80C2C0055C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {09E28D94-B771-48EB-800C-5A80C2C0055C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {09E28D94-B771-48EB-800C-5A80C2C0055C}.Release|Any CPU.Build.0 = Release|Any CPU - {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F452AA57-7BEC-4E64-BAB5-166078865EC4}.Release|Any CPU.Build.0 = Release|Any CPU - {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0}.Release|Any CPU.Build.0 = Release|Any CPU - {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED105ED3-0060-4035-AD5E-1F857F94C2DF}.Release|Any CPU.Build.0 = Release|Any CPU - {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2}.Release|Any CPU.Build.0 = Release|Any CPU - {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3FEA305D-0B5F-46A6-8E18-587387FCBFBF}.Release|Any CPU.Build.0 = Release|Any CPU - {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3D040E9F-C39B-49C6-8C87-68D427AECA8F}.Release|Any CPU.Build.0 = Release|Any CPU - {AA790584-200C-4301-8545-8B2854B2F6CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA790584-200C-4301-8545-8B2854B2F6CC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA790584-200C-4301-8545-8B2854B2F6CC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA790584-200C-4301-8545-8B2854B2F6CC}.Release|Any CPU.Build.0 = Release|Any CPU - {40525D17-4553-405E-8B21-4603B07D126A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {40525D17-4553-405E-8B21-4603B07D126A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.Build.0 = Release|Any CPU - {85133A0C-2427-4085-AA71-02881F46AEAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85133A0C-2427-4085-AA71-02881F46AEAE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85133A0C-2427-4085-AA71-02881F46AEAE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85133A0C-2427-4085-AA71-02881F46AEAE}.Release|Any CPU.Build.0 = Release|Any CPU - {BCB6C67A-A765-41A0-80EA-7807720714D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BCB6C67A-A765-41A0-80EA-7807720714D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BCB6C67A-A765-41A0-80EA-7807720714D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BCB6C67A-A765-41A0-80EA-7807720714D0}.Release|Any CPU.Build.0 = Release|Any CPU - {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CB8A4E34-76F8-408A-98BE-5881D12BA02C}.Release|Any CPU.Build.0 = Release|Any CPU - {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913}.Release|Any CPU.Build.0 = Release|Any CPU - {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0D43A981-ED8F-434A-B349-3227DDD38D2E}.Release|Any CPU.Build.0 = Release|Any CPU - {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9AC40671-E708-4FE1-B5CB-ADBBC425F793}.Release|Any CPU.Build.0 = Release|Any CPU - {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151}.Release|Any CPU.Build.0 = Release|Any CPU - {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DFCDD214-BAED-468D-BC61-B1D536FF0AFF}.Release|Any CPU.Build.0 = Release|Any CPU - {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {59D2147E-1E97-49F1-87AF-F5C50DCD73B6}.Release|Any CPU.Build.0 = Release|Any CPU - {F8409C3F-4C46-4115-AA48-518A7490A14A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8409C3F-4C46-4115-AA48-518A7490A14A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8409C3F-4C46-4115-AA48-518A7490A14A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8409C3F-4C46-4115-AA48-518A7490A14A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {B6663ACE-6FE4-4BB4-8B35-AB98EF62EAAE} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} - {E7A3B914-598D-4ABC-B973-6CC444DAFE52} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {280FDDEA-50B1-4BD3-83B1-475B15829538} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {CE53C256-EE31-4E4A-8A05-70350840448F} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {3B8833A4-2E9E-47BD-93DE-65934DCEB9A6} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {C175A982-E0E0-4E22-8A3B-0A9C00EE7730} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {1D851FA7-2F3C-4E3C-8F22-AF5E13D9BEE6} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {EF75497C-6CB7-4471-980A-619EA1AB8CF6} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {09E28D94-B771-48EB-800C-5A80C2C0055C} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {F452AA57-7BEC-4E64-BAB5-166078865EC4} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {AE5566CE-EC5E-47B0-B5A3-89E90B3893F0} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {ED105ED3-0060-4035-AD5E-1F857F94C2DF} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} - {0A0D7CB1-3864-478F-98FC-5AA53C6A72C2} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} - {3FEA305D-0B5F-46A6-8E18-587387FCBFBF} = {66953A8A-9E31-486F-AF8E-7310F6707E4F} - {3D040E9F-C39B-49C6-8C87-68D427AECA8F} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {AA790584-200C-4301-8545-8B2854B2F6CC} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {40525D17-4553-405E-8B21-4603B07D126A} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {85133A0C-2427-4085-AA71-02881F46AEAE} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} - {BCB6C67A-A765-41A0-80EA-7807720714D0} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {CB8A4E34-76F8-408A-98BE-5881D12BA02C} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {F4EBDF97-E5B4-443A-B8D9-ABE68CA1D913} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} - {0D43A981-ED8F-434A-B349-3227DDD38D2E} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {9AC40671-E708-4FE1-B5CB-ADBBC425F793} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {CBE0A0A6-FB44-41CE-8AF3-6486E3A28151} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - {DFCDD214-BAED-468D-BC61-B1D536FF0AFF} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {59D2147E-1E97-49F1-87AF-F5C50DCD73B6} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} - {F8409C3F-4C46-4115-AA48-518A7490A14A} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7D279EE5-E38F-4125-AE82-6ADE52D72F26} - EndGlobalSection -EndGlobal diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md deleted file mode 100644 index 952325ae2dab64..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Microsoft.Extensions.Diagnostics.Configuration - -`Microsoft.Extensions.Diagnostics.Configuration` contains the `IConfiguration` integration for Meters and Metrics. - -Commonly Used APIS: -- MetricsBuilderConfigurationExtensions.AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) - -## Contribution Bar -- [x] [We consider new features, new APIs, bug fixes, and performance changes](https://github.com/dotnet/runtime/tree/main/src/libraries#contribution-bar) - -The APIs and functionality are new in .NET 8 and will continue to be developed. - -## Deployment -[Microsoft.Extensions.Diagnostics.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Diagnostics.Configuration) is included in the ASP.NET Core shared framework. The package is deployed as out-of-band (OOB) too and can be referenced into projects directly. diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs deleted file mode 100644 index 9d92d4f27ec330..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.cs +++ /dev/null @@ -1,21 +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.Diagnostics.Metrics -{ - public static class MetricsBuilderConfigurationExtensions - { - public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) => throw null!; - } -} -namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration -{ - public interface IMetricListenerConfiguration - { - Microsoft.Extensions.Configuration.IConfiguration Configuration { get; } - } - public interface IMetricListenerConfigurationFactory - { - Microsoft.Extensions.Configuration.IConfiguration GetConfiguration(System.Type listenerType); - } -} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj deleted file mode 100644 index dff8b4cce8bd7b..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/ref/Microsoft.Extensions.Diagnostics.Configuration.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) - - - - - - - - - - - - diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj deleted file mode 100644 index f4d3b3ed180a61..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Microsoft.Extensions.Diagnostics.Configuration.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) - true - - CS1591 - - true - true - This package includes the IConfiguration integration for configuring Meters, Instruments, and IMetricsListeners. - - - - - - - - diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx deleted file mode 100644 index 1af7de150c99c1..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Resources/Strings.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj deleted file mode 100644 index 924a31b177e206..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/tests/Microsoft.Extensions.Diagnostics.Configuration.Tests.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - $(NetCoreAppCurrent);$(NetFrameworkMinimum) - true - true - - - - - - - - - - diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln index a0e1e68d675d33..26433eda6ff945 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln +++ b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln @@ -47,6 +47,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagno EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\src\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{40525D17-4553-405E-8B21-4603B07D126A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "..\Microsoft.Extensions.Configuration.Abstractions\ref\Microsoft.Extensions.Configuration.Abstractions.csproj", "{2D529E61-474E-45C6-8A7E-35AAD70B9801}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "..\Microsoft.Extensions.Configuration.Abstractions\src\Microsoft.Extensions.Configuration.Abstractions.csproj", "{30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -125,6 +129,14 @@ Global {40525D17-4553-405E-8B21-4603B07D126A}.Debug|Any CPU.Build.0 = Debug|Any CPU {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.ActiveCfg = Release|Any CPU {40525D17-4553-405E-8B21-4603B07D126A}.Release|Any CPU.Build.0 = Release|Any CPU + {2D529E61-474E-45C6-8A7E-35AAD70B9801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D529E61-474E-45C6-8A7E-35AAD70B9801}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D529E61-474E-45C6-8A7E-35AAD70B9801}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D529E61-474E-45C6-8A7E-35AAD70B9801}.Release|Any CPU.Build.0 = Release|Any CPU + {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -148,6 +160,8 @@ Global {3D040E9F-C39B-49C6-8C87-68D427AECA8F} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} {AA790584-200C-4301-8545-8B2854B2F6CC} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} {40525D17-4553-405E-8B21-4603B07D126A} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {2D529E61-474E-45C6-8A7E-35AAD70B9801} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} + {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7D279EE5-E38F-4125-AE82-6ADE52D72F26} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/README.md b/src/libraries/Microsoft.Extensions.Diagnostics/README.md index 776b53340b7833..b8e0a36534193c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/README.md +++ b/src/libraries/Microsoft.Extensions.Diagnostics/README.md @@ -5,6 +5,7 @@ Commonly Used APIS: - MetricsServiceExtensions.AddMetrics(this IServiceCollection services) - MeterFactoryExtensions.Create(this IMeterFactory, string name, string? version = null, IEnumerable> tags = null, object? scope = null) +- MetricsBuilderConfigurationExtensions.AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) ## Contribution Bar - [x] [We consider new features, new APIs, bug fixes, and performance changes](https://github.com/dotnet/runtime/tree/main/src/libraries#contribution-bar) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 3dad1ba1537a3a..9e838cc8551439 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -19,6 +19,10 @@ public static class MetricsBuilderConsoleExtensions { public static IMetricsBuilder AddDebugConsole(this IMetricsBuilder builder) => throw null!; } + public static class MetricsBuilderConfigurationExtensions + { + public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) => throw null!; + } internal sealed class DebugConsoleMetricListener : IMetricsListener, System.IDisposable { internal System.IO.TextWriter _textWriter; @@ -44,3 +48,14 @@ public DefaultMeterFactory() { } public void Dispose() { } } } +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + public interface IMetricListenerConfiguration + { + Microsoft.Extensions.Configuration.IConfiguration Configuration { get; } + } + public interface IMetricListenerConfigurationFactory + { + Microsoft.Extensions.Configuration.IConfiguration GetConfiguration(System.Type listenerType); + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj index 96a403f6b9780a..bbda0c54a95456 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj @@ -13,6 +13,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfiguration.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfiguration.cs similarity index 54% rename from src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfiguration.cs rename to src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfiguration.cs index 802ba4224880ca..3762dcee1d7230 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfiguration.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfiguration.cs @@ -5,8 +5,15 @@ namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { + /// + /// Used to retrieve the metrics configuration for the given T type of listener. + /// + /// The type of metric listener. public interface IMetricListenerConfiguration { + /// + /// The configuration for the given T type of listener. + /// IConfiguration Configuration { get; } } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs similarity index 51% rename from src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs rename to src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs index 3fa961597e6928..1d7619665d5209 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs @@ -6,8 +6,16 @@ namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { + /// + /// Used to retrieve the metrics configuration for any type of listener. + /// public interface IMetricListenerConfigurationFactory { + /// + /// Gets the configuration for the given type of listener. + /// + /// The type of listener. + /// The configuration for this listener type. IConfiguration GetConfiguration(Type listenerType); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConfigurationExtensions.cs similarity index 87% rename from src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs rename to src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConfigurationExtensions.cs index c2bfc43f0f8bc8..086cca3e4398eb 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Configuration/src/Metrics/MetricsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConfigurationExtensions.cs @@ -6,6 +6,9 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { + /// + /// Extensions for for enabling metrics based on . + /// public static class MetricsBuilderConfigurationExtensions { /// diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index 537203974df339..5255f4ef669aea 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -21,6 +21,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln b/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln index d761fb5ddc15aa..3b0d013e478b87 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln +++ b/src/libraries/Microsoft.Extensions.Hosting/Microsoft.Extensions.Hosting.sln @@ -173,10 +173,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagno EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\src\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{FB173557-75BD-4943-B813-0AD13E0337A3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "..\Microsoft.Extensions.Diagnostics.Configuration\src\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{96FC685A-DEC6-4CB1-B6AF-597060B3D478}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Configuration", "..\Microsoft.Extensions.Diagnostics.Configuration\ref\Microsoft.Extensions.Diagnostics.Configuration.csproj", "{68401297-58B6-4A4C-BCC0-419EA594DA74}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "..\Microsoft.Extensions.Diagnostics.Abstractions\ref\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{982C5C02-DD1E-452C-B5BC-13207D2CB14F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics", "..\Microsoft.Extensions.Diagnostics\ref\Microsoft.Extensions.Diagnostics.csproj", "{A9181F84-BD47-4B46-AD71-908E289A695E}" @@ -511,14 +507,6 @@ Global {FB173557-75BD-4943-B813-0AD13E0337A3}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB173557-75BD-4943-B813-0AD13E0337A3}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB173557-75BD-4943-B813-0AD13E0337A3}.Release|Any CPU.Build.0 = Release|Any CPU - {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Debug|Any CPU.Build.0 = Debug|Any CPU - {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Release|Any CPU.ActiveCfg = Release|Any CPU - {96FC685A-DEC6-4CB1-B6AF-597060B3D478}.Release|Any CPU.Build.0 = Release|Any CPU - {68401297-58B6-4A4C-BCC0-419EA594DA74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68401297-58B6-4A4C-BCC0-419EA594DA74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68401297-58B6-4A4C-BCC0-419EA594DA74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68401297-58B6-4A4C-BCC0-419EA594DA74}.Release|Any CPU.Build.0 = Release|Any CPU {982C5C02-DD1E-452C-B5BC-13207D2CB14F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {982C5C02-DD1E-452C-B5BC-13207D2CB14F}.Debug|Any CPU.Build.0 = Debug|Any CPU {982C5C02-DD1E-452C-B5BC-13207D2CB14F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -613,8 +601,6 @@ Global {0813853E-8C78-429A-B01A-3FB2EF1898F8} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {5B630ECF-5C4D-4F66-9AB9-59B014520A5C} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} {FB173557-75BD-4943-B813-0AD13E0337A3} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {96FC685A-DEC6-4CB1-B6AF-597060B3D478} = {59A29BF0-B76B-41F8-A733-E2A0847AB992} - {68401297-58B6-4A4C-BCC0-419EA594DA74} = {E041754F-1A93-443A-9294-87DC1C30B471} {982C5C02-DD1E-452C-B5BC-13207D2CB14F} = {E041754F-1A93-443A-9294-87DC1C30B471} {A9181F84-BD47-4B46-AD71-908E289A695E} = {E041754F-1A93-443A-9294-87DC1C30B471} EndGlobalSection diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index f70223b68e86c0..aac48bc7861490 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -35,7 +35,7 @@ - + From 5748df4e003a763d383ba00f6a5d1182dd4bbf5c Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 15:06:22 -0700 Subject: [PATCH 38/50] Tests, cleanup --- .../src/Metrics/DebugConsoleMetricListener.cs | 3 +- .../tests/ConsoleMetricListenerTests.cs | 56 ----------- .../tests/DebugConsoleMetricListenerTests.cs | 96 +++++++++++++++++++ 3 files changed, 98 insertions(+), 57 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs index ffda9607a4ad0b..888d5e7f740357 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs @@ -2,6 +2,7 @@ // 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.IO; @@ -48,7 +49,7 @@ public void MeasurementsCompleted(Instrument instrument, object? userState) DecimalHandler = MeasurementHandler, }; - private void MeasurementHandler(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct + private void MeasurementHandler(Instrument instrument, T measurement, ReadOnlySpan> tags, object? state) where T : struct { Debug.Assert(state == this); WriteLine($"{instrument.Meter.Name}-{instrument.Name} {measurement} {instrument.Unit}"); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs deleted file mode 100644 index c139d3f74360a7..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ConsoleMetricListenerTests.cs +++ /dev/null @@ -1,56 +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; -using System.Diagnostics.Metrics; -using System.IO; -using Microsoft.DotNet.RemoteExecutor; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Xunit; - -namespace Microsoft.Extensions.Diagnostics.Metrics.Tests -{ - public class ConsoleMetricListenerTests - { - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ListenerCanBeRegisteredViaDi() - { - RemoteExecutor.Invoke(() => - { - ServiceCollection services = new ServiceCollection(); - services.AddMetrics(builder => - { - builder.AddDebugConsole(); - builder.EnableMetrics("TestMeter", listenerName: ConsoleMetrics.DebugListenerName); - }); - using var sp = services.BuildServiceProvider(); - sp.GetRequiredService().Validate(); - - var listener = sp.GetRequiredService(); - var consoleListener = Assert.IsType(listener); - var output = new StringWriter(); - consoleListener._textWriter = output; - - var factory = sp.GetRequiredService(); - var meter = factory.Create("TestMeter"); - var counter = meter.CreateCounter("counter", "blip", "I count blips"); - counter.Add(4); - counter.Add(1); - - // The rule doesn't match, we shouldn't get this output. - var negativeMeter = factory.Create("NegativeMeter"); - counter = negativeMeter.CreateCounter("counter", "blop", "I count blops"); - counter.Add(1); - - sp.Dispose(); // TODO: Why did we have to do this to get the Stopped message? Why doesn't disposing of the meter do it? - - Assert.Equal("TestMeter-counter Started; Description: I count blips." + Environment.NewLine - + "TestMeter-counter 4 blip" + Environment.NewLine - + "TestMeter-counter 1 blip" + Environment.NewLine - + "TestMeter-counter Stopped." + Environment.NewLine, output.ToString()); - }).Dispose(); - } - } -} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs new file mode 100644 index 00000000000000..d9ca7cc259057a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs @@ -0,0 +1,96 @@ +// 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.Metrics; +using System.IO; +using Microsoft.DotNet.RemoteExecutor; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Tests +{ + public class DebugConsoleMetricListenerTests + { + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void ListenerCanResolveLocalMetrics() + { + RemoteExecutor.Invoke(() => + { + ServiceCollection services = new ServiceCollection(); + services.AddMetrics(builder => + { + builder.AddDebugConsole(); + builder.EnableMetrics("TestMeter", scopes: MeterScope.Local, listenerName: ConsoleMetrics.DebugListenerName); + }); + using var sp = services.BuildServiceProvider(); + // Make sure the subscription manager is started. + sp.GetRequiredService().Validate(); + + var listener = sp.GetRequiredService(); + var consoleListener = Assert.IsType(listener); + var output = new StringWriter(); + consoleListener._textWriter = output; + + var factory = sp.GetRequiredService(); + var meter = factory.Create("TestMeter"); + var counter = meter.CreateCounter("counter", "blip", "I count blips"); + counter.Add(4); + counter.Add(1); + + // The rule doesn't match, we shouldn't get this output. + var negativeMeter = factory.Create("NegativeMeter"); + counter = negativeMeter.CreateCounter("counter", "blop", "I count blops"); + counter.Add(1); + + // Meters from the factory can't be disposed, you have to dispose the whole factory. + factory.Dispose(); + + Assert.Equal("TestMeter-counter Started; Description: I count blips." + Environment.NewLine + + "TestMeter-counter 4 blip" + Environment.NewLine + + "TestMeter-counter 1 blip" + Environment.NewLine + + "TestMeter-counter Stopped." + Environment.NewLine, output.ToString()); + }).Dispose(); + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void ListenerCanResolveGlobalMetrics() + { + RemoteExecutor.Invoke(() => + { + ServiceCollection services = new ServiceCollection(); + services.AddMetrics(builder => + { + builder.AddDebugConsole(); + builder.EnableMetrics("TestMeter", scopes: MeterScope.Global, listenerName: ConsoleMetrics.DebugListenerName); + }); + using var sp = services.BuildServiceProvider(); + // Make sure the subscription manager is started. + sp.GetRequiredService().Validate(); + + var listener = sp.GetRequiredService(); + var consoleListener = Assert.IsType(listener); + var output = new StringWriter(); + consoleListener._textWriter = output; + + var meter = new Meter("TestMeter"); + var counter = meter.CreateCounter("counter", "blip", "I count blips"); + counter.Add(4); + counter.Add(1); + + // The rule doesn't match, we shouldn't get this output. + var negativeMeter = new Meter("NegativeMeter"); + counter = negativeMeter.CreateCounter("counter", "blop", "I count blops"); + counter.Add(1); + + meter.Dispose(); + + Assert.Equal("TestMeter-counter Started; Description: I count blips." + Environment.NewLine + + "TestMeter-counter 4 blip" + Environment.NewLine + + "TestMeter-counter 1 blip" + Environment.NewLine + + "TestMeter-counter Stopped." + Environment.NewLine, output.ToString()); + }).Dispose(); + } + } +} From b9263f48a9e461eca44640d342731e640952f67b Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 15:19:25 -0700 Subject: [PATCH 39/50] Scope validation --- .../src/Metrics/InstrumentRule.cs | 5 ++++- .../src/Metrics/MetricsBuilderExtensions.Enable.cs | 4 ---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs index 0f5f0fc3eaba1a..d8f95b3fe4942e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/InstrumentRule.cs @@ -1,6 +1,7 @@ // 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.Metrics; namespace Microsoft.Extensions.Diagnostics.Metrics @@ -45,7 +46,9 @@ public class InstrumentRule(string? meterName, string? instrumentName, string? l /// The . This is used to distinguish between meters created via constructors () /// and those created via Dependency Injection with ()."/>. /// - public MeterScope Scopes { get; } = scopes; + public MeterScope Scopes { get; } = scopes == MeterScope.None + ? throw new ArgumentOutOfRangeException(nameof(scopes), scopes, "The MeterScope must be Global, Local, or both.") + : scopes; /// /// Indicates if the instrument should be enabled for the listener. diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs index 8ad6d6d77752c9..87cdc54335d6ea 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs @@ -111,10 +111,6 @@ private static MetricsOptions AddRule(this MetricsOptions options, string? meter MeterScope scopes, bool enable) { ThrowHelper.ThrowIfNull(options); - if (scopes == MeterScope.None) - { - throw new ArgumentOutOfRangeException(nameof(scopes), "The MeterScope must be Global, Local, or both."); - } options.Rules.Add(new InstrumentRule(meterName, instrumentName, listenerName, scopes, enable)); return options; } From 482888f531aa4dba94f4c70861b30e1c655b1854 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 16:24:54 -0700 Subject: [PATCH 40/50] Split tests, fix naming --- ...ft.Extensions.Diagnostics.Abstractions.sln | 7 + ... => MetricsBuilderExtensions.Listeners.cs} | 0 ...e.cs => MetricsBuilderExtensions.Rules.cs} | 0 .../MetricsBuilderExtensionsListenersTests.cs | 91 +++++++++ .../MetricsBuilderExtensionsRulesTests.cs | 188 ++++++++++++++++++ ...ions.Diagnostics.Abstractions.Tests.csproj | 13 ++ .../Microsoft.Extensions.Diagnostics.sln | 7 + ...rosoft.Extensions.Diagnostics.Tests.csproj | 2 - 8 files changed, 306 insertions(+), 2 deletions(-) rename src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/{MetricsBuilderExtensions.AddListener.cs => MetricsBuilderExtensions.Listeners.cs} (100%) rename src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/{MetricsBuilderExtensions.Enable.cs => MetricsBuilderExtensions.Rules.cs} (100%) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsListenersTests.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsRulesTests.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/Microsoft.Extensions.Diagnostics.Abstractions.Tests.csproj diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln index fe6f8c75be9dca..4e51f48f75fbaa 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/Microsoft.Extensions.Diagnostics.Abstractions.sln @@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagno EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions", "ref\Microsoft.Extensions.Diagnostics.Abstractions.csproj", "{EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions.Tests", "tests\Microsoft.Extensions.Diagnostics.Abstractions.Tests.csproj", "{CB373FE5-F976-4CB2-A04E-6188D67A5816}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,6 +59,10 @@ Global {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C}.Release|Any CPU.Build.0 = Release|Any CPU + {CB373FE5-F976-4CB2-A04E-6188D67A5816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB373FE5-F976-4CB2-A04E-6188D67A5816}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB373FE5-F976-4CB2-A04E-6188D67A5816}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB373FE5-F976-4CB2-A04E-6188D67A5816}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -69,6 +75,7 @@ Global {527CCF66-AC37-487C-871E-A4F6B94E1731} = {548DF5F7-790C-4A1C-89EB-BD904CA1BA86} {0588387D-FB65-4BA9-A8B2-DA6790027CC2} = {548DF5F7-790C-4A1C-89EB-BD904CA1BA86} {EF2C8F2A-6088-4A89-9AE2-9C2FDFB0241C} = {7631380A-FB73-4241-9987-0891A21E9769} + {CB373FE5-F976-4CB2-A04E-6188D67A5816} = {4DE63935-DCA9-4D63-9C1F-AAE79C89CA8B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {450DA749-CBDC-4BDC-950F-8A491CF59D49} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Listeners.cs similarity index 100% rename from src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.AddListener.cs rename to src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Listeners.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Rules.cs similarity index 100% rename from src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Enable.cs rename to src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Rules.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsListenersTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsListenersTests.cs new file mode 100644 index 00000000000000..5108f9d6b63904 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsListenersTests.cs @@ -0,0 +1,91 @@ +// 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.Metrics; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.Metrics; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.Tests +{ + public class MetricsBuilderExtensionsListenersTests + { + [Fact] + public void CanAddListenersByType() + { + var services = new ServiceCollection(); + var builder = new FakeBuilder(services); + + builder.AddListener(); + var container = services.BuildServiceProvider(); + Assert.IsType(Assert.Single(container.GetServices())); + + builder.AddListener(); + container = services.BuildServiceProvider(); + Assert.Equal(2, container.GetServices().Count()); + } + + [Fact] + public void CanAddListenersByInstance() + { + var services = new ServiceCollection(); + var builder = new FakeBuilder(services); + + var instanceA = new FakeListenerA(); + builder.AddListener(instanceA); + var container = services.BuildServiceProvider(); + Assert.Same(instanceA, Assert.Single(container.GetServices())); + + var instanceB = new FakeListenerB(); + builder.AddListener(instanceB); + container = services.BuildServiceProvider(); + var listeners = container.GetServices().ToList(); + Assert.Equal(2, listeners.Count); + Assert.Same(instanceA, listeners[0]); + Assert.Same(instanceB, listeners[1]); + } + + [Fact] + public void CanClearListeners() + { + var services = new ServiceCollection(); + var builder = new FakeBuilder(services); + + builder.AddListener(); + builder.AddListener(new FakeListenerB()); + var container = services.BuildServiceProvider(); + Assert.Equal(2, container.GetServices().Count()); + + builder.ClearListeners(); + container = services.BuildServiceProvider(); + Assert.Empty(container.GetServices()); + } + + private class FakeBuilder(IServiceCollection services) : IMetricsBuilder + { + public IServiceCollection Services { get; } = services; + } + + private class FakeListenerA : IMetricsListener + { + public string Name => "Fake"; + + public MeasurementHandlers GetMeasurementHandlers() => throw new NotImplementedException(); + public void Initialize(IObservableInstrumentsSource source) => throw new NotImplementedException(); + public bool InstrumentPublished(Instrument instrument, out object? userState) => throw new NotImplementedException(); + public void MeasurementsCompleted(Instrument instrument, object? userState) => throw new NotImplementedException(); + } + + private class FakeListenerB : IMetricsListener + { + public string Name => "Fake"; + + public MeasurementHandlers GetMeasurementHandlers() => throw new NotImplementedException(); + public void Initialize(IObservableInstrumentsSource source) => throw new NotImplementedException(); + public bool InstrumentPublished(Instrument instrument, out object? userState) => throw new NotImplementedException(); + public void MeasurementsCompleted(Instrument instrument, object? userState) => throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsRulesTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsRulesTests.cs new file mode 100644 index 00000000000000..18568a8480392a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/MetricsBuilderExtensionsRulesTests.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.Metrics; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.Tests +{ + public class MetricsBuilderExtensionsRulesTests + { + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("*")] + [InlineData("foo")] + public void BuilderEnableMetricsAddsRule(string meterName) + { + var services = new ServiceCollection(); + services.AddOptions(); + var builder = new FakeBuilder(services); + + builder.EnableMetrics(meterName); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal(meterName, rule.MeterName); + Assert.Null(rule.InstrumentName); + Assert.Null(rule.ListenerName); + Assert.Equal(MeterScope.Local | MeterScope.Global, rule.Scopes); + Assert.True(rule.Enable); + } + + [Fact] + public void BuilderEnableMetricsWithAllParamsAddsRule() + { + var services = new ServiceCollection(); + services.AddOptions(); + var builder = new FakeBuilder(services); + + builder.EnableMetrics("meter", "instance", "listener", MeterScope.Local); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal("meter", rule.MeterName); + Assert.Equal("instance", rule.InstrumentName); + Assert.Equal("listener", rule.ListenerName); + Assert.Equal(MeterScope.Local, rule.Scopes); + Assert.True(rule.Enable); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("*")] + [InlineData("foo")] + public void OptionsEnableMetricsAddsRule(string meterName) + { + var services = new ServiceCollection(); + services.AddOptions(); + services.Configure(options => options.EnableMetrics(meterName)); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal(meterName, rule.MeterName); + Assert.Null(rule.InstrumentName); + Assert.Null(rule.ListenerName); + Assert.Equal(MeterScope.Local | MeterScope.Global, rule.Scopes); + Assert.True(rule.Enable); + } + + [Fact] + public void OptionsEnableMetricsAllParamsAddsRule() + { + var services = new ServiceCollection(); + services.AddOptions(); + services.Configure(options + => options.EnableMetrics("meter", "instrument", "listener", MeterScope.Global)); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal("meter", rule.MeterName); + Assert.Equal("instrument", rule.InstrumentName); + Assert.Equal("listener", rule.ListenerName); + Assert.Equal(MeterScope.Global, rule.Scopes); + Assert.True(rule.Enable); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("*")] + [InlineData("foo")] + public void BuilderDisableMetricsAddsRule(string meterName) + { + var services = new ServiceCollection(); + services.AddOptions(); + var builder = new FakeBuilder(services); + + builder.DisableMetrics(meterName); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal(meterName, rule.MeterName); + Assert.Null(rule.InstrumentName); + Assert.Null(rule.ListenerName); + Assert.Equal(MeterScope.Local | MeterScope.Global, rule.Scopes); + Assert.False(rule.Enable); + } + + [Fact] + public void BuilderDisableMetricsWithAllParamsAddsRule() + { + var services = new ServiceCollection(); + services.AddOptions(); + var builder = new FakeBuilder(services); + + builder.DisableMetrics("meter", "instance", "listener", MeterScope.Local); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal("meter", rule.MeterName); + Assert.Equal("instance", rule.InstrumentName); + Assert.Equal("listener", rule.ListenerName); + Assert.Equal(MeterScope.Local, rule.Scopes); + Assert.False(rule.Enable); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("*")] + [InlineData("foo")] + public void OptionsDisableMetricsAddsRule(string meterName) + { + var services = new ServiceCollection(); + services.AddOptions(); + services.Configure(options => options.DisableMetrics(meterName)); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal(meterName, rule.MeterName); + Assert.Null(rule.InstrumentName); + Assert.Null(rule.ListenerName); + Assert.Equal(MeterScope.Local | MeterScope.Global, rule.Scopes); + Assert.False(rule.Enable); + } + + [Fact] + public void OptionsDisableMetricsAllParamsAddsRule() + { + var services = new ServiceCollection(); + services.AddOptions(); + services.Configure(options + => options.DisableMetrics("meter", "instrument", "listener", MeterScope.Global)); + + var container = services.BuildServiceProvider(); + var options = container.GetRequiredService>(); + var instance = options.Value; + var rule = Assert.Single(instance.Rules); + Assert.Equal("meter", rule.MeterName); + Assert.Equal("instrument", rule.InstrumentName); + Assert.Equal("listener", rule.ListenerName); + Assert.Equal(MeterScope.Global, rule.Scopes); + Assert.False(rule.Enable); + } + + private class FakeBuilder(IServiceCollection services) : IMetricsBuilder + { + public IServiceCollection Services { get; } = services; + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/Microsoft.Extensions.Diagnostics.Abstractions.Tests.csproj b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/Microsoft.Extensions.Diagnostics.Abstractions.Tests.csproj new file mode 100644 index 00000000000000..b3043a4481a1db --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/Microsoft.Extensions.Diagnostics.Abstractions.Tests.csproj @@ -0,0 +1,13 @@ + + + + $(NetCoreAppCurrent);$(NetFrameworkMinimum) + true + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln index 26433eda6ff945..99477251eaf03e 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln +++ b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln @@ -51,6 +51,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Config EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.Abstractions", "..\Microsoft.Extensions.Configuration.Abstractions\src\Microsoft.Extensions.Configuration.Abstractions.csproj", "{30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions.Tests", "..\Microsoft.Extensions.Diagnostics.Abstractions\tests\Microsoft.Extensions.Diagnostics.Abstractions.Tests.csproj", "{2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -137,6 +139,10 @@ Global {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}.Debug|Any CPU.Build.0 = Debug|Any CPU {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}.Release|Any CPU.ActiveCfg = Release|Any CPU {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D}.Release|Any CPU.Build.0 = Release|Any CPU + {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -162,6 +168,7 @@ Global {40525D17-4553-405E-8B21-4603B07D126A} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} {2D529E61-474E-45C6-8A7E-35AAD70B9801} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7D279EE5-E38F-4125-AE82-6ADE52D72F26} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj index 31b45c09024daa..17dbc06b08e822 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj @@ -8,9 +8,7 @@ - - From 2c7a7e9922303ff9b86d1f22af925c27c3e2dce7 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 8 Aug 2023 16:35:02 -0700 Subject: [PATCH 41/50] Cleanup --- .../tests/InstrumentRuleTests.cs | 18 ++++++++++++++++++ .../tests/DebugConsoleMetricListenerTests.cs | 2 +- .../tests/ListenerSubscriptionTests.cs | 2 -- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/InstrumentRuleTests.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/InstrumentRuleTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/InstrumentRuleTests.cs new file mode 100644 index 00000000000000..f7fd045f361594 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/tests/InstrumentRuleTests.cs @@ -0,0 +1,18 @@ +// 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 Xunit; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Tests +{ + public class InstrumentRuleTests + { + [Fact] + public void ScopeRequired() + { + var ex = Assert.Throws(() => new InstrumentRule(null, null, null, MeterScope.None, true)); + Assert.Equal("scopes", ex.ParamName); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs index d9ca7cc259057a..4b5656aace177a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs @@ -18,7 +18,7 @@ public void ListenerCanResolveLocalMetrics() { RemoteExecutor.Invoke(() => { - ServiceCollection services = new ServiceCollection(); + var services = new ServiceCollection(); services.AddMetrics(builder => { builder.AddDebugConsole(); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index 2041b1813e2a88..9d092d5f838891 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -209,8 +209,6 @@ public void RuleCanBeTurnedOffAndOnAgain() }).Dispose(); } - // TODO: Scopes - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] // [InlineData(null, null, null)] // RemoteExecutor can't handle nulls [InlineData("", "", "")] From a461a6b6baaa81fbcc3b21dbb0b15762a2e6c7fe Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 9 Aug 2023 15:56:26 -0700 Subject: [PATCH 42/50] Implement configuration reading --- .../Microsoft.Extensions.Diagnostics.sln | 21 +++ .../ref/Microsoft.Extensions.Diagnostics.cs | 11 +- .../Microsoft.Extensions.Diagnostics.csproj | 1 + .../IMetricListenerConfiguration.cs | 19 --- .../IMetricListenerConfigurationFactory.cs | 9 +- .../MetricListenerConfigurationFactory.cs | 32 +++++ .../MetricsBuilderConfigurationExtensions.cs | 18 ++- .../Configuration/MetricsConfiguration.cs | 18 +++ .../Configuration/MetricsConfigureOptions.cs | 113 +++++++++++++++ .../src/Metrics/MetricsServiceExtensions.cs | 3 + .../Microsoft.Extensions.Diagnostics.csproj | 3 +- .../tests/MetricsConfigureOptionsTests.cs | 132 ++++++++++++++++++ 12 files changed, 350 insertions(+), 30 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfiguration.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs rename src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/{ => Configuration}/MetricsBuilderConfigurationExtensions.cs (60%) create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs create mode 100644 src/libraries/Microsoft.Extensions.Diagnostics/tests/MetricsConfigureOptionsTests.cs diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln index 99477251eaf03e..5e4931ef73fb1d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln +++ b/src/libraries/Microsoft.Extensions.Diagnostics/Microsoft.Extensions.Diagnostics.sln @@ -53,6 +53,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Config EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.Abstractions.Tests", "..\Microsoft.Extensions.Diagnostics.Abstractions\tests\Microsoft.Extensions.Diagnostics.Abstractions.Tests.csproj", "{2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Options.ConfigurationExtensions", "..\Microsoft.Extensions.Options.ConfigurationExtensions\src\Microsoft.Extensions.Options.ConfigurationExtensions.csproj", "{A2853038-B04A-4BAA-B0B4-0481457003B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration", "..\Microsoft.Extensions.Configuration\src\Microsoft.Extensions.Configuration.csproj", "{A77E804D-4576-4962-A248-92E538ED997C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Options", "..\Microsoft.Extensions.Options\ref\Microsoft.Extensions.Options.csproj", "{DBAB1C82-A3A0-4ADC-95BC-B87557C61C42}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -143,6 +149,18 @@ Global {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}.Debug|Any CPU.Build.0 = Debug|Any CPU {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B}.Release|Any CPU.Build.0 = Release|Any CPU + {A2853038-B04A-4BAA-B0B4-0481457003B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2853038-B04A-4BAA-B0B4-0481457003B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2853038-B04A-4BAA-B0B4-0481457003B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2853038-B04A-4BAA-B0B4-0481457003B8}.Release|Any CPU.Build.0 = Release|Any CPU + {A77E804D-4576-4962-A248-92E538ED997C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A77E804D-4576-4962-A248-92E538ED997C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A77E804D-4576-4962-A248-92E538ED997C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A77E804D-4576-4962-A248-92E538ED997C}.Release|Any CPU.Build.0 = Release|Any CPU + {DBAB1C82-A3A0-4ADC-95BC-B87557C61C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBAB1C82-A3A0-4ADC-95BC-B87557C61C42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBAB1C82-A3A0-4ADC-95BC-B87557C61C42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBAB1C82-A3A0-4ADC-95BC-B87557C61C42}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -169,6 +187,9 @@ Global {2D529E61-474E-45C6-8A7E-35AAD70B9801} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} {30E93D8B-4B53-4DC1-A5E3-FCAC1E37303D} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} {2DC4F9C2-7C2B-4B8C-9AB3-74F3F06D359B} = {76DC9C4C-EE53-47E6-B6BF-7B135EA8CAF3} + {A2853038-B04A-4BAA-B0B4-0481457003B8} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {A77E804D-4576-4962-A248-92E538ED997C} = {A447D0CB-601B-479E-A2B2-76E48F5D4D61} + {DBAB1C82-A3A0-4ADC-95BC-B87557C61C42} = {9BF048D0-411D-4C2A-8C32-3A3255501D27} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7D279EE5-E38F-4125-AE82-6ADE52D72F26} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 9e838cc8551439..915334255c4e4d 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -50,12 +50,15 @@ public void Dispose() { } } namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { - public interface IMetricListenerConfiguration + public interface IMetricListenerConfigurationFactory { - Microsoft.Extensions.Configuration.IConfiguration Configuration { get; } + Microsoft.Extensions.Configuration.IConfiguration GetConfiguration(string listenerName); } - public interface IMetricListenerConfigurationFactory + internal class MetricsConfigureOptions : Microsoft.Extensions.Options.IConfigureOptions { - Microsoft.Extensions.Configuration.IConfiguration GetConfiguration(System.Type listenerType); + public MetricsConfigureOptions(Microsoft.Extensions.Configuration.IConfiguration configuration) { } + public void Configure(MetricsOptions options) => throw null!; + internal static void LoadMeterRules(MetricsOptions options, Microsoft.Extensions.Configuration.IConfigurationSection configurationSection, MeterScope scopes, string? listenerName) => throw null!; + internal static void LoadInstrumentRules(MetricsOptions options, Microsoft.Extensions.Configuration.IConfigurationSection meterSection, MeterScope scopes, string? listenerName) => throw null!; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj index bbda0c54a95456..b3f65430e56abf 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj @@ -15,6 +15,7 @@ + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfiguration.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfiguration.cs deleted file mode 100644 index 3762dcee1d7230..00000000000000 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Extensions.Configuration; - -namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration -{ - /// - /// Used to retrieve the metrics configuration for the given T type of listener. - /// - /// The type of metric listener. - public interface IMetricListenerConfiguration - { - /// - /// The configuration for the given T type of listener. - /// - IConfiguration Configuration { get; } - } -} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs index 1d7619665d5209..a8ac79fd1897dd 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/IMetricListenerConfigurationFactory.cs @@ -1,21 +1,20 @@ // 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 Microsoft.Extensions.Configuration; namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { /// - /// Used to retrieve the metrics configuration for any type of listener. + /// Used to retrieve the metrics configuration for any listener name. /// public interface IMetricListenerConfigurationFactory { /// - /// Gets the configuration for the given type of listener. + /// Gets the configuration for the given listener. /// - /// The type of listener. + /// The name of listener. /// The configuration for this listener type. - IConfiguration GetConfiguration(Type listenerType); + IConfiguration GetConfiguration(string listenerName); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs new file mode 100644 index 00000000000000..ff36592f184016 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + internal class MetricListenerConfigurationFactory : IMetricListenerConfigurationFactory + { + private readonly IEnumerable _configurations; + + public MetricListenerConfigurationFactory(IEnumerable configurations) + { + _configurations = configurations ?? throw new ArgumentNullException(nameof(configurations)); + } + + public IConfiguration GetConfiguration(string listenerName) + { + ThrowHelper.ThrowIfNull(listenerName); + + var configurationBuilder = new ConfigurationBuilder(); + foreach (MetricsConfiguration configuration in _configurations) + { + var section = configuration.Configuration.GetSection(listenerName); + configurationBuilder.AddConfiguration(section); + } + return configurationBuilder.Build(); + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsBuilderConfigurationExtensions.cs similarity index 60% rename from src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConfigurationExtensions.cs rename to src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsBuilderConfigurationExtensions.cs index 086cca3e4398eb..9061c2461fb819 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsBuilderConfigurationExtensions.cs @@ -1,8 +1,12 @@ // 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.Metrics; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.Metrics.Configuration; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Diagnostics.Metrics { @@ -20,7 +24,19 @@ public static class MetricsBuilderConfigurationExtensions /// The original for chaining. public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, IConfiguration configuration) { - // TODO: + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + builder.Services.AddSingleton>(new MetricsConfigureOptions(configuration)); + builder.Services.AddSingleton>(new ConfigurationChangeTokenSource(configuration)); + builder.Services.AddSingleton(new MetricsConfiguration(configuration)); return builder; } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs new file mode 100644 index 00000000000000..bf9538e9a02aae --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs @@ -0,0 +1,18 @@ +// 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 Microsoft.Extensions.Configuration; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + internal class MetricsConfiguration + { + public MetricsConfiguration(IConfiguration configuration) + { + Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + } + + public IConfiguration Configuration { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs new file mode 100644 index 00000000000000..d564b294ae3948 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs @@ -0,0 +1,113 @@ +// 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.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + internal class MetricsConfigureOptions : IConfigureOptions + { + private const string EnabledMetricsKey = "EnabledMetrics"; + private const string EnabledGlobalMetricsKey = "EnabledGlobalMetrics"; + private const string EnabledLocalMetricsKey = "EnabledLocalMetrics"; + private const string DefaultKey = "Default"; + private readonly IConfiguration _configuration; + + public MetricsConfigureOptions(IConfiguration configuration) + { + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + } + + public void Configure(MetricsOptions options) => LoadConfig(options); + + private void LoadConfig(MetricsOptions options) + { + foreach (var configurationSection in _configuration.GetChildren()) + { + if (configurationSection.Key.Equals(EnabledMetricsKey, StringComparison.OrdinalIgnoreCase)) + { + // Load listener defaults + LoadMeterRules(options, configurationSection, MeterScope.Global | MeterScope.Local, null); + } + else if (configurationSection.Key.Equals(EnabledGlobalMetricsKey, StringComparison.OrdinalIgnoreCase)) + { + // Load global listener defaults + LoadMeterRules(options, configurationSection, MeterScope.Global, null); + } + else if (configurationSection.Key.Equals(EnabledLocalMetricsKey, StringComparison.OrdinalIgnoreCase)) + { + // Load local listener defaults + LoadMeterRules(options, configurationSection, MeterScope.Local, null); + } + else + { + // Load listener specific rules + var listenerName = configurationSection.Key; + var enabledMetricsSection = configurationSection.GetSection(EnabledMetricsKey); + if (enabledMetricsSection != null) + { + LoadMeterRules(options, enabledMetricsSection, MeterScope.Global | MeterScope.Local, listenerName); + } + var enabledGlobalMetricsSection = configurationSection.GetSection(EnabledGlobalMetricsKey); + if (enabledGlobalMetricsSection != null) + { + LoadMeterRules(options, enabledGlobalMetricsSection, MeterScope.Global, listenerName); + } + var enabledLocalMetricsSection = configurationSection.GetSection(EnabledLocalMetricsKey); + if (enabledLocalMetricsSection != null) + { + LoadMeterRules(options, enabledLocalMetricsSection, MeterScope.Local, listenerName); + } + } + } + } + + // Internal for testing + internal static void LoadMeterRules(MetricsOptions options, IConfigurationSection configurationSection, MeterScope scopes, string? listenerName) + { + foreach (var meterSection in configurationSection.GetChildren()) + { + // Is the meter a simple on/off bool or is it an object listing individual instruments? + if (meterSection.GetChildren().Any()) + { + // It's an object, load individual instruments + LoadInstrumentRules(options, meterSection, scopes, listenerName); + } + // Otherwise, it's a simple bool + else if (bool.TryParse(meterSection.Value, out var meterEnabled)) + { + var meterName = meterSection.Key; + if (string.Equals(DefaultKey, meterName, StringComparison.OrdinalIgnoreCase)) + { + // "Default" is a special key that applies to all meters + meterName = null; + } + // Simple bool, enable/disable all instruments for this meter + options.Rules.Add(new InstrumentRule(meterName, instrumentName: null, listenerName, scopes, meterEnabled)); + } + } + } + + // Internal for testing + internal static void LoadInstrumentRules(MetricsOptions options, IConfigurationSection meterSection, MeterScope scopes, string? listenerName) + { + foreach (var instrumentPair in meterSection.AsEnumerable(makePathsRelative: true)) + { + if (bool.TryParse(instrumentPair.Value, out var instrumentEnabled)) + { + var instrumentName = instrumentPair.Key; + if (string.Equals(DefaultKey, instrumentName, StringComparison.OrdinalIgnoreCase)) + { + // "Default" is a special key that applies to all instruments + instrumentName = null; + } + // Simple bool, enable/disable all instruments for this meter + options.Rules.Add(new InstrumentRule(meterSection.Key, instrumentName, listenerName, scopes, instrumentEnabled)); + } + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs index 0bfe6cb338ac32..79a08c78b82e7c 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsServiceExtensions.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Diagnostics.Metrics; +using Microsoft.Extensions.Diagnostics.Metrics.Configuration; using System; using System.Diagnostics.Metrics; @@ -33,6 +34,8 @@ public static IServiceCollection AddMetrics(this IServiceCollection services) // The host will trigger options validation. services.AddOptions().Configure((_, manager) => manager.Initialize()).ValidateOnStart(); + services.TryAddSingleton(); + return services; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index 5255f4ef669aea..cf7cfe4a3a9dc4 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -21,8 +21,9 @@ - + + diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/MetricsConfigureOptionsTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/MetricsConfigureOptionsTests.cs new file mode 100644 index 00000000000000..f4ac522987df82 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/MetricsConfigureOptionsTests.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration +{ + public class MetricsConfigureOptionsTests + { + [Fact] + public void LoadInstrumentRulesTest() + { + var options = new MetricsOptions(); + var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); + configuration["MeterName1:InstrumentName1"] = "true"; + configuration["MeterName1:InstrumentName2"] = "false"; + configuration["MeterName1:Default"] = "true"; + + MetricsConfigureOptions.LoadInstrumentRules(options, configuration.GetSection("MeterName1"), MeterScope.Local, "ListenerName"); + + Assert.Equal(3, options.Rules.Count); + + var rule1 = options.Rules.Single(rule => rule.InstrumentName == "InstrumentName1"); + AssertRule(rule1, "MeterName1", "InstrumentName1", "ListenerName", MeterScope.Local, true); + var rule2 = options.Rules.Single(rule => rule.InstrumentName == "InstrumentName2"); + AssertRule(rule2, "MeterName1", "InstrumentName2", "ListenerName", MeterScope.Local, false); + var rule3 = options.Rules.Single(rule => rule.InstrumentName == null); + AssertRule(rule3, "MeterName1", null, "ListenerName", MeterScope.Local, true); + } + + [Fact] + public void LoadMeterRulesBoolTest() + { + var options = new MetricsOptions(); + var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); + configuration["Section:MeterName1"] = "true"; + configuration["Section:MeterName2"] = "false"; + configuration["Section:Default"] = "true"; + MetricsConfigureOptions.LoadMeterRules(options, configuration.GetSection("Section"), MeterScope.Local, listenerName: "ListenerName"); + + Assert.Equal(3, options.Rules.Count); + + var rule1 = options.Rules.Single(rule => rule.MeterName == "MeterName1"); + AssertRule(rule1, "MeterName1", null, "ListenerName", MeterScope.Local, true); + var rule2 = options.Rules.Single(rule => rule.MeterName == "MeterName2"); + AssertRule(rule2, "MeterName2", null, "ListenerName", MeterScope.Local, false); + var rule3 = options.Rules.Single(rule => rule.MeterName == null); + AssertRule(rule3, null, null, "ListenerName", MeterScope.Local, true); + } + + [Fact] + public void LoadMeterRulesWithInstrumentsTest() + { + var options = new MetricsOptions(); + var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); + configuration["Section:MeterName1:InstrumentName1"] = "true"; + configuration["Section:MeterName1:InstrumentName2"] = "false"; + configuration["Section:MeterName1:Default"] = "true"; + configuration["Section:MeterName2:InstrumentName1"] = "true"; + configuration["Section:MeterName2:InstrumentName2"] = "false"; + configuration["Section:MeterName2:Default"] = "true"; + configuration["Section:Default"] = "true"; + MetricsConfigureOptions.LoadMeterRules(options, configuration.GetSection("Section"), MeterScope.Local, listenerName: "ListenerName"); + + Assert.Equal(7, options.Rules.Count); + + var rule1 = options.Rules.Single(rule => rule.MeterName == "MeterName1" && rule.InstrumentName == "InstrumentName1"); + AssertRule(rule1, "MeterName1", "InstrumentName1", "ListenerName", MeterScope.Local, true); + var rule2 = options.Rules.Single(rule => rule.MeterName == "MeterName1" && rule.InstrumentName == "InstrumentName2"); + AssertRule(rule2, "MeterName1", "InstrumentName2", "ListenerName", MeterScope.Local, false); + var rule3 = options.Rules.Single(rule => rule.MeterName == "MeterName1" && rule.InstrumentName == null); + AssertRule(rule3, "MeterName1", null, "ListenerName", MeterScope.Local, true); + + var rule4 = options.Rules.Single(rule => rule.MeterName == "MeterName2" && rule.InstrumentName == "InstrumentName1"); + AssertRule(rule4, "MeterName2", "InstrumentName1", "ListenerName", MeterScope.Local, true); + var rule5 = options.Rules.Single(rule => rule.MeterName == "MeterName2" && rule.InstrumentName == "InstrumentName2"); + AssertRule(rule5, "MeterName2", "InstrumentName2", "ListenerName", MeterScope.Local, false); + var rule6 = options.Rules.Single(rule => rule.MeterName == "MeterName2" && rule.InstrumentName == null); + AssertRule(rule6, "MeterName2", null, "ListenerName", MeterScope.Local, true); + + var rule7 = options.Rules.Single(rule => rule.MeterName == null); + AssertRule(rule7, null, null, "ListenerName", MeterScope.Local, true); + } + + [Theory] + [InlineData("EnabledMetrics:Default", "true", null, null, null, MeterScope.Global | MeterScope.Local, true)] + [InlineData("EnabledMetrics:MeterName", "false", "MeterName", null, null, MeterScope.Global | MeterScope.Local, false)] + [InlineData("EnabledMetrics:MeterName:Default", "true", "MeterName", null, null, MeterScope.Global | MeterScope.Local, true)] + [InlineData("EnabledMetrics:MeterName:InstrumentName", "false", "MeterName", "InstrumentName", null, MeterScope.Global | MeterScope.Local, false)] + [InlineData("EnabledGlobalMetrics:Default", "true", null, null, null, MeterScope.Global, true)] + [InlineData("EnabledGlobalMetrics:MeterName", "false", "MeterName", null, null, MeterScope.Global, false)] + [InlineData("EnabledGlobalMetrics:MeterName:Default", "true", "MeterName", null, null, MeterScope.Global, true)] + [InlineData("EnabledGlobalMetrics:MeterName:InstrumentName", "false", "MeterName", "InstrumentName", null, MeterScope.Global, false)] + [InlineData("EnabledLocalMetrics:Default", "true", null, null, null, MeterScope.Local, true)] + [InlineData("EnabledLocalMetrics:MeterName", "false", "MeterName", null, null, MeterScope.Local, false)] + [InlineData("EnabledLocalMetrics:MeterName:Default", "true", "MeterName", null, null, MeterScope.Local, true)] + [InlineData("EnabledLocalMetrics:MeterName:InstrumentName", "false", "MeterName", "InstrumentName", null, MeterScope.Local, false)] + [InlineData("Listener:EnabledMetrics:Default", "true", null, null, "Listener", MeterScope.Global | MeterScope.Local, true)] + [InlineData("Listener:EnabledMetrics:MeterName", "false", "MeterName", null, "Listener", MeterScope.Global | MeterScope.Local, false)] + [InlineData("Listener:EnabledMetrics:MeterName:Default", "true", "MeterName", null, "Listener", MeterScope.Global | MeterScope.Local, true)] + [InlineData("Listener:EnabledMetrics:MeterName:InstrumentName", "false", "MeterName", "InstrumentName", "Listener", MeterScope.Global | MeterScope.Local, false)] + [InlineData("Listener:EnabledGlobalMetrics:Default", "true", null, null, "Listener", MeterScope.Global, true)] + [InlineData("Listener:EnabledGlobalMetrics:MeterName", "false", "MeterName", null, "Listener", MeterScope.Global, false)] + [InlineData("Listener:EnabledGlobalMetrics:MeterName:Default", "true", "MeterName", null, "Listener", MeterScope.Global, true)] + [InlineData("Listener:EnabledGlobalMetrics:MeterName:InstrumentName", "false", "MeterName", "InstrumentName", "Listener", MeterScope.Global, false)] + [InlineData("Listener:EnabledLocalMetrics:Default", "true", null, null, "Listener", MeterScope.Local, true)] + [InlineData("Listener:EnabledLocalMetrics:MeterName", "false", "MeterName", null, "Listener", MeterScope.Local, false)] + [InlineData("Listener:EnabledLocalMetrics:MeterName:Default", "true", "MeterName", null, "Listener", MeterScope.Local, true)] + [InlineData("Listener:EnabledLocalMetrics:MeterName:InstrumentName", "false", "MeterName", "InstrumentName", "Listener", MeterScope.Local, false)] + public void LoadTopLevelRulesTest(string key, string value, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enabled) + { + var options = new MetricsOptions(); + var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); + configuration[key] = value; + + new MetricsConfigureOptions(configuration).Configure(options); + var rule = Assert.Single(options.Rules); + AssertRule(rule, meterName, instrumentName, listenerName, scopes, enabled); + } + + private static void AssertRule(InstrumentRule rule, string? meterName, string? instrumentName, string? listenerName, MeterScope scopes, bool enable) + { + Assert.Equal(meterName, rule.MeterName); + Assert.Equal(instrumentName, rule.InstrumentName); + Assert.Equal(listenerName, rule.ListenerName); + Assert.Equal(scopes, rule.Scopes); + Assert.Equal(enable, rule.Enable); + } + } +} From 898ec8ff617b3cfd9ea63494b15875c527ab062d Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 9 Aug 2023 16:46:57 -0700 Subject: [PATCH 43/50] Add new Abstractions to transport package --- src/libraries/NetCoreAppLibrary.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/NetCoreAppLibrary.props b/src/libraries/NetCoreAppLibrary.props index fe63bb48a4dbdc..49fc1048b2b1cf 100644 --- a/src/libraries/NetCoreAppLibrary.props +++ b/src/libraries/NetCoreAppLibrary.props @@ -199,6 +199,7 @@ Microsoft.Extensions.DependencyInjection; Microsoft.Extensions.DependencyInjection.Abstractions; Microsoft.Extensions.Diagnostics; + Microsoft.Extensions.Diagnostics.Abstractions; Microsoft.Extensions.FileProviders.Abstractions; Microsoft.Extensions.FileProviders.Composite; Microsoft.Extensions.FileProviders.Physical; From 8d3dc1d1e3072b4b65113621f7696dd15332d2ae Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 10 Aug 2023 11:51:22 -0700 Subject: [PATCH 44/50] Remove IVT, cleanup --- .../src/Metrics/IMetricsListener.cs | 2 +- .../Metrics/IObservableInstrumentsSource.cs | 4 +-- .../ref/Microsoft.Extensions.Diagnostics.cs | 31 ------------------- .../Microsoft.Extensions.Diagnostics.csproj | 4 --- .../MetricListenerConfigurationFactory.cs | 2 +- .../Configuration/MetricsConfiguration.cs | 2 +- .../Configuration/MetricsConfigureOptions.cs | 2 +- .../src/Metrics/ListenerSubscription.cs | 4 +-- .../src/Metrics/MetricsSubscriptionManager.cs | 2 +- .../Microsoft.Extensions.Diagnostics.csproj | 4 --- .../tests/DebugConsoleMetricListenerTests.cs | 4 +-- .../tests/ListenerSubscriptionTests.cs | 11 +++++-- ...rosoft.Extensions.Diagnostics.Tests.csproj | 6 ++++ 13 files changed, 25 insertions(+), 53 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs index e22ea317c1dcaf..e4c7fa173576ed 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IMetricsListener.cs @@ -25,7 +25,7 @@ public interface IMetricsListener /// Called when a new instrument is created and enabled by a matching rule. /// /// The new . - /// Listener state associated with this instrument. This should be returned to + /// Listener state associated with this instrument. This will be returned to /// and . /// Returns true if the listener wants to subscribe to this instrument, otherwise false. public bool InstrumentPublished(Instrument instrument, out object? userState); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs index 351217d4b93087..29657c5b069337 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/IObservableInstrumentsSource.cs @@ -6,8 +6,8 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { /// - /// A callback registered with that the listener - /// can call to request the current set of metrics for enabled instruments be sent to the listener's 's. + /// An interface registered with each IMetricsListener using . The listener + /// can call to receive the current set of measurements for enabled observable instruments. /// public interface IObservableInstrumentsSource { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs index 915334255c4e4d..47fe8a3cf524fa 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.cs @@ -23,30 +23,6 @@ public static class MetricsBuilderConfigurationExtensions { public static IMetricsBuilder AddConfiguration(this IMetricsBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) => throw null!; } - internal sealed class DebugConsoleMetricListener : IMetricsListener, System.IDisposable - { - internal System.IO.TextWriter _textWriter; - public string Name { get; } - public MeasurementHandlers GetMeasurementHandlers() => throw null!; - public bool InstrumentPublished(System.Diagnostics.Metrics.Instrument instrument, out object? userState) => throw null!; - public void MeasurementsCompleted(System.Diagnostics.Metrics.Instrument instrument, object? userState) => throw null!; - public void Initialize(IObservableInstrumentsSource source) => throw null!; - public void Dispose() => throw null!; - } - internal sealed class ListenerSubscription - { - internal ListenerSubscription(Microsoft.Extensions.Diagnostics.Metrics.IMetricsListener listener) { } - public void Initialize() { } - internal void UpdateRules(System.Collections.Generic.IList rules) { } - internal static bool RuleMatches(InstrumentRule rule, System.Diagnostics.Metrics.Instrument instrument, string listenerName) => throw null!; - internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best, bool isLocalScope) => throw null!; - } - internal sealed class DefaultMeterFactory : System.Diagnostics.Metrics.IMeterFactory - { - public DefaultMeterFactory() { } - public System.Diagnostics.Metrics.Meter Create(System.Diagnostics.Metrics.MeterOptions options) => throw null!; - public void Dispose() { } - } } namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { @@ -54,11 +30,4 @@ public interface IMetricListenerConfigurationFactory { Microsoft.Extensions.Configuration.IConfiguration GetConfiguration(string listenerName); } - internal class MetricsConfigureOptions : Microsoft.Extensions.Options.IConfigureOptions - { - public MetricsConfigureOptions(Microsoft.Extensions.Configuration.IConfiguration configuration) { } - public void Configure(MetricsOptions options) => throw null!; - internal static void LoadMeterRules(MetricsOptions options, Microsoft.Extensions.Configuration.IConfigurationSection configurationSection, MeterScope scopes, string? listenerName) => throw null!; - internal static void LoadInstrumentRules(MetricsOptions options, Microsoft.Extensions.Configuration.IConfigurationSection meterSection, MeterScope scopes, string? listenerName) => throw null!; - } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj index b3f65430e56abf..1299aac25941d9 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/ref/Microsoft.Extensions.Diagnostics.csproj @@ -8,10 +8,6 @@ - - - - diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs index ff36592f184016..4c5ee6cefff6fe 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricListenerConfigurationFactory.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { - internal class MetricListenerConfigurationFactory : IMetricListenerConfigurationFactory + internal sealed class MetricListenerConfigurationFactory : IMetricListenerConfigurationFactory { private readonly IEnumerable _configurations; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs index bf9538e9a02aae..70f9d3348ffe87 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfiguration.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { - internal class MetricsConfiguration + internal sealed class MetricsConfiguration { public MetricsConfiguration(IConfiguration configuration) { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs index d564b294ae3948..9af86bcdde6458 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/Configuration/MetricsConfigureOptions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics.Configuration { - internal class MetricsConfigureOptions : IConfigureOptions + internal sealed class MetricsConfigureOptions : IConfigureOptions { private const string EnabledMetricsKey = "EnabledMetrics"; private const string EnabledGlobalMetricsKey = "EnabledGlobalMetrics"; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 16a1b8bee060e7..9beee709df2c46 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - internal class ListenerSubscription : IObservableInstrumentsSource, IDisposable + internal sealed class ListenerSubscription : IObservableInstrumentsSource, IDisposable { private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; @@ -313,7 +313,7 @@ public void Dispose() _meterListener.Dispose(); } - private class InstrumentStatus + private sealed class InstrumentStatus { public object? State { get; set; } public bool Published { get; set; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs index ae0589d9eb3e17..a89277170bd49f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { - internal class MetricsSubscriptionManager + internal sealed class MetricsSubscriptionManager { private readonly ListenerSubscription[] _listeners; private readonly IDisposable? _changeTokenRegistration; diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj index cf7cfe4a3a9dc4..443137251e6412 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Microsoft.Extensions.Diagnostics.csproj @@ -11,10 +11,6 @@ This package includes the default implementation of IMeterFactory and additional extension methods to easily register it with the Dependency Injection framework. - - - - diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs index 4b5656aace177a..5df16163e478cd 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs @@ -21,7 +21,7 @@ public void ListenerCanResolveLocalMetrics() var services = new ServiceCollection(); services.AddMetrics(builder => { - builder.AddDebugConsole(); + builder.AddListener(); builder.EnableMetrics("TestMeter", scopes: MeterScope.Local, listenerName: ConsoleMetrics.DebugListenerName); }); using var sp = services.BuildServiceProvider(); @@ -62,7 +62,7 @@ public void ListenerCanResolveGlobalMetrics() ServiceCollection services = new ServiceCollection(); services.AddMetrics(builder => { - builder.AddDebugConsole(); + builder.AddListener(); builder.EnableMetrics("TestMeter", scopes: MeterScope.Global, listenerName: ConsoleMetrics.DebugListenerName); }); using var sp = services.BuildServiceProvider(); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index 9d092d5f838891..2ce710ce07fe0a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics.Metrics; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.Metrics; using Xunit; @@ -80,7 +81,8 @@ public void SubscriptionReceivesNewLocalMetersAndInstruments() var completedTcs = new TaskCompletionSource(); var measurementTcs = new TaskCompletionSource(); - var factory = new DefaultMeterFactory(); + using var services = new ServiceCollection().AddMetrics().BuildServiceProvider(); + var factory = services.GetRequiredService(); var fakeListener = new FakeMetricListener(); fakeListener.OnPublished = (instrument) => @@ -142,7 +144,8 @@ public void RuleCanBeTurnedOffAndOnAgain() var completedTcs = new TaskCompletionSource(); var measurements = new List(); - var factory = new DefaultMeterFactory(); + using var services = new ServiceCollection().AddMetrics().BuildServiceProvider(); + var factory = services.GetRequiredService(); var fakeListener = new FakeMetricListener(); fakeListener.OnPublished = (instrument) => @@ -155,7 +158,6 @@ public void RuleCanBeTurnedOffAndOnAgain() fakeListener.OnCompleted = (instrument, state) => { onCompletedCalled++; - Assert.Equal(1, onCompletedCalled); completedTcs.TrySetResult(instrument); }; fakeListener.OnMeasurement = (instrument, measurement, tags, state) => @@ -194,6 +196,7 @@ public void RuleCanBeTurnedOffAndOnAgain() subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); Assert.True(completedTcs.Task.IsCompleted); + Assert.Equal(1, onCompletedCalled); counter.Add(3); // Not received Assert.Equal(1, measurements.Count); @@ -206,6 +209,8 @@ public void RuleCanBeTurnedOffAndOnAgain() Assert.Equal(2, measurements.Count); Assert.Equal(4, measurements[1]); + services.Dispose(); + Assert.Equal(2, onCompletedCalled); }).Dispose(); } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj b/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj index 17dbc06b08e822..5a7d75e403232a 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/Microsoft.Extensions.Diagnostics.Tests.csproj @@ -6,6 +6,12 @@ true + + + + + + From e0450584204f199190d372fb53883c9cb4a304a2 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 10 Aug 2023 12:21:05 -0700 Subject: [PATCH 45/50] Only track active instruments --- .../src/Metrics/ListenerSubscription.cs | 79 ++++++------------- 1 file changed, 23 insertions(+), 56 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 9beee709df2c46..23a39b84bf7f0f 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -5,9 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Metrics; -#if NET -using System.Runtime.CompilerServices; -#endif namespace Microsoft.Extensions.Diagnostics.Metrics { @@ -15,12 +12,7 @@ internal sealed class ListenerSubscription : IObservableInstrumentsSource, IDisp { private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; -#if NET - private readonly ConditionalWeakTable _instruments = new(); -#else - // TODO: Can't enumerate ConditionalWeakTable in .NET Standard 2.0. How do we clean up the instruments? - private readonly Dictionary _instruments = new(); -#endif + private readonly Dictionary _instruments = new(); private IList _rules = Array.Empty(); private bool _disposed; @@ -43,8 +35,8 @@ public void Initialize() _meterListener.SetMeasurementEventCallback(handlers.FloatHandler); _meterListener.SetMeasurementEventCallback(handlers.DoubleHandler); _meterListener.SetMeasurementEventCallback(handlers.DecimalHandler); - _meterListener.Start(); _metricsListener.Initialize(this); + _meterListener.Start(); } private void InstrumentPublished(Instrument instrument, MeterListener _) @@ -56,19 +48,18 @@ private void InstrumentPublished(Instrument instrument, MeterListener _) return; } - if (_instruments.TryGetValue(instrument, out var _)) + if (_instruments.ContainsKey(instrument)) { - Debug.Assert(false, "InstrumentPublished called twice for the same instrument"); + Debug.Assert(false, "InstrumentPublished called for an instrument we're already listening to."); return; } - var status = new InstrumentStatus(); - _instruments.Add(instrument, status); - RefreshInstrument(instrument, status); + RefreshInstrument(instrument); } } - // Called when we call DisableMeasurementEvents, like when a rule is disabled. + // Called when we call DisableMeasurementEvents, like when a rule is disabled, + // or when the meter/factory is disposed. private void MeasurementsCompleted(Instrument instrument, object? state) { lock (_instruments) @@ -78,20 +69,11 @@ private void MeasurementsCompleted(Instrument instrument, object? state) return; } - if (!_instruments.TryGetValue(instrument, out var status)) + if (_instruments.TryGetValue(instrument, out var listenerState)) { - Debug.Assert(false, "MeasurementsCompleted called for an instrument that was never published"); - return; - } - status.Enabled = false; - - if (status.Published) - { - status.Published = false; - if (status.ListenerEnabled) - { - _metricsListener.MeasurementsCompleted(instrument, status.State); - } + _instruments.Remove(instrument); + _metricsListener.MeasurementsCompleted(instrument, listenerState); + _meterListener.DisableMeasurementEvents(instrument); } } } @@ -107,17 +89,17 @@ internal void UpdateRules(IList rules) _rules = rules; - foreach (var instrumentPair in _instruments) - { - RefreshInstrument(instrumentPair.Key, instrumentPair.Value); - } + // Get a fresh list of instruments to compare against the new rules. + using var tempListener = new MeterListener(); + tempListener.InstrumentPublished = (instrument, _) => RefreshInstrument(instrument); + tempListener.Start(); } } // Called under _instrument lock - private void RefreshInstrument(Instrument instrument, InstrumentStatus status) + private void RefreshInstrument(Instrument instrument) { - var alreadyEnabled = status.Enabled; + var alreadyEnabled = _instruments.TryGetValue(instrument, out var state); var enable = false; var rule = GetMostSpecificRule(instrument); if (rule != null) @@ -127,24 +109,17 @@ private void RefreshInstrument(Instrument instrument, InstrumentStatus status) if (!enable && alreadyEnabled) { - status.Enabled = false; + _instruments.Remove(instrument); + _metricsListener.MeasurementsCompleted(instrument, state); _meterListener.DisableMeasurementEvents(instrument); } else if (enable && !alreadyEnabled) { - // The first time we enable an instrument, we need to call InstrumentPublished. - if (!status.Published) - { - // However, a listener might decline to enable the instrument, remember that. - status.Published = true; - status.ListenerEnabled = _metricsListener.InstrumentPublished(instrument, out var state); - status.State = state; - } - - if (status.ListenerEnabled) + // The listener gets a chance to decline the instrument. + if (_metricsListener.InstrumentPublished(instrument, out state)) { - _meterListener.EnableMeasurementEvents(instrument, status.State); - status.Enabled = true; + _instruments.Add(instrument, state); + _meterListener.EnableMeasurementEvents(instrument, state); } } } @@ -312,13 +287,5 @@ public void Dispose() _disposed = true; _meterListener.Dispose(); } - - private sealed class InstrumentStatus - { - public object? State { get; set; } - public bool Published { get; set; } - public bool Enabled { get; set; } - public bool ListenerEnabled { get; set; } - } } } From cc52509800cc558185c5144126adefd27c4a08fb Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 10 Aug 2023 12:34:17 -0700 Subject: [PATCH 46/50] Check that the instrument matches the current scope --- .../src/Metrics/ListenerSubscription.cs | 11 ++++++----- .../src/Metrics/MetricsSubscriptionManager.cs | 5 +++-- .../tests/ListenerSubscriptionTests.cs | 16 +++++++++++----- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 23a39b84bf7f0f..dcb54325fcfc4b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -12,14 +12,15 @@ internal sealed class ListenerSubscription : IObservableInstrumentsSource, IDisp { private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; + private readonly IMeterFactory _meterFactory; private readonly Dictionary _instruments = new(); private IList _rules = Array.Empty(); private bool _disposed; - internal ListenerSubscription(IMetricsListener metricsListener) + internal ListenerSubscription(IMetricsListener metricsListener, IMeterFactory meterFactory) { _metricsListener = metricsListener; - + _meterFactory = meterFactory; _meterListener = new MeterListener(); } @@ -129,7 +130,7 @@ private void RefreshInstrument(Instrument instrument) InstrumentRule? best = null; foreach (var rule in _rules) { - if (RuleMatches(rule, instrument, _metricsListener.Name) + if (RuleMatches(rule, instrument, _metricsListener.Name, _meterFactory) && IsMoreSpecific(rule, best, isLocalScope: instrument.Meter.Scope != null)) { best = rule; @@ -140,7 +141,7 @@ private void RefreshInstrument(Instrument instrument) } // internal for testing - internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, string listenerName) + internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, string listenerName, IMeterFactory meterFactory) { // Exact match or empty if (!string.IsNullOrEmpty(rule.ListenerName) @@ -157,7 +158,7 @@ internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, str } if (!(rule.Scopes.HasFlag(MeterScope.Global) && instrument.Meter.Scope == null) - && !(rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope != null)) // TODO: What should we be comparing Scope to, the DefaultMeterFactory / IMeterFactory? + && !(rule.Scopes.HasFlag(MeterScope.Local) && instrument.Meter.Scope == meterFactory)) { return false; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs index a89277170bd49f..b8d3aa31d89c03 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/MetricsSubscriptionManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Metrics; using System.Linq; using Microsoft.Extensions.Options; @@ -14,13 +15,13 @@ internal sealed class MetricsSubscriptionManager private readonly IDisposable? _changeTokenRegistration; private bool _disposed; - public MetricsSubscriptionManager(IEnumerable listeners, IOptionsMonitor options) + public MetricsSubscriptionManager(IEnumerable listeners, IOptionsMonitor options, IMeterFactory meterFactory) { var list = listeners.ToList(); _listeners = new ListenerSubscription[list.Count]; for (int i = 0; i < _listeners.Length; i++) { - _listeners[i] = new ListenerSubscription(list[i]); + _listeners[i] = new ListenerSubscription(list[i], meterFactory); } _changeTokenRegistration = options.OnChange(UpdateRules); UpdateRules(options.CurrentValue); diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index 2ce710ce07fe0a..7e0199e2e93fb9 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -39,7 +39,7 @@ public void SubscriptionReceivesNewGlobalMetersAndInstruments() measurementTcs.TrySetResult((int)measurement); }; - var subscription = new ListenerSubscription(fakeListener); + var subscription = new ListenerSubscription(fakeListener, new FakeMeterFactory()); Assert.Null(fakeListener.Source); subscription.Initialize(); @@ -100,7 +100,7 @@ public void SubscriptionReceivesNewLocalMetersAndInstruments() measurementTcs.TrySetResult((int)measurement); }; - var subscription = new ListenerSubscription(fakeListener); + var subscription = new ListenerSubscription(fakeListener, factory); Assert.Null(fakeListener.Source); subscription.Initialize(); @@ -165,7 +165,7 @@ public void RuleCanBeTurnedOffAndOnAgain() measurements.Add((int)measurement); }; - var subscription = new ListenerSubscription(fakeListener); + var subscription = new ListenerSubscription(fakeListener, factory); Assert.Null(fakeListener.Source); subscription.Initialize(); @@ -236,7 +236,7 @@ public void RuleMatchesTest(string meterName, string instrumentName, string list var rule = new InstrumentRule(m, i, l, MeterScope.Global, enable: true); var meter = new Meter("Long.Silly.Meter.Name"); var instrument = meter.CreateCounter("InstrumentName"); - Assert.True(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName")); + Assert.True(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName", new FakeMeterFactory())); }, meterName, instrumentName, listenerName).Dispose(); } @@ -260,7 +260,7 @@ public void RuleMatchesNegativeTest(string meterName, string instrumentName, str var rule = new InstrumentRule(m, i, l, MeterScope.Global, enable: true); var meter = new Meter("Long.Silly.Meter.Name"); var instrument = meter.CreateCounter("InstrumentName"); - Assert.False(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName")); + Assert.False(ListenerSubscription.RuleMatches(rule, instrument, "ListenerName", new FakeMeterFactory())); }, meterName, instrumentName, listenerName).Dispose(); } @@ -382,5 +382,11 @@ public bool InstrumentPublished(Instrument instrument, out object? userState) public void MeasurementsCompleted(Instrument instrument, object? userState) => OnCompleted(instrument, userState); public void Initialize(IObservableInstrumentsSource source) => Source = source; } + + private class FakeMeterFactory : IMeterFactory + { + public Meter Create(MeterOptions options) => throw new NotImplementedException(); + public void Dispose() => throw new NotImplementedException(); + } } } From e07073c96c0a1a2b8e258a58c4f6a416748e57b2 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 10 Aug 2023 13:10:59 -0700 Subject: [PATCH 47/50] Priorities, cleanup --- .../src/Metrics/ListenerSubscription.cs | 28 +++++++++------ .../tests/DebugConsoleMetricListenerTests.cs | 2 +- .../tests/ListenerSubscriptionTests.cs | 36 +++++++++---------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index dcb54325fcfc4b..6dea3bae1d050b 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -131,7 +131,7 @@ private void RefreshInstrument(Instrument instrument) foreach (var rule in _rules) { if (RuleMatches(rule, instrument, _metricsListener.Name, _meterFactory) - && IsMoreSpecific(rule, best, isLocalScope: instrument.Meter.Scope != null)) + && IsMoreSpecific(rule, best, isLocalScope: instrument.Meter.Scope == _meterFactory)) { best = rule; } @@ -166,6 +166,12 @@ internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, str // Meter var ruleMeterName = rule.MeterName.AsSpan(); + // Don't allow "*" anywhere except at the end. + var starIndex = ruleMeterName.IndexOf('*'); + if (starIndex != -1 && starIndex != ruleMeterName.Length - 1) + { + return false; + } // Rule "System.Net.*" matches meter "System.Net" and "System.Net.Http" if (ruleMeterName.EndsWith(".*".AsSpan(), StringComparison.Ordinal)) { @@ -209,6 +215,16 @@ internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best, b return true; } + // Listener name + if (!string.IsNullOrEmpty(rule.ListenerName) && string.IsNullOrEmpty(best.ListenerName)) + { + return true; + } + else if (string.IsNullOrEmpty(rule.ListenerName) && !string.IsNullOrEmpty(best.ListenerName)) + { + return false; + } + // Meter name if (!string.IsNullOrEmpty(rule.MeterName)) { @@ -238,16 +254,6 @@ internal static bool IsMoreSpecific(InstrumentRule rule, InstrumentRule? best, b return false; } - // Listener name - if (!string.IsNullOrEmpty(rule.ListenerName) && string.IsNullOrEmpty(best.ListenerName)) - { - return true; - } - else if (string.IsNullOrEmpty(rule.ListenerName) && !string.IsNullOrEmpty(best.ListenerName)) - { - return false; - } - // Scope // Already matched as local diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs index 5df16163e478cd..3758b7cef276da 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/DebugConsoleMetricListenerTests.cs @@ -45,7 +45,7 @@ public void ListenerCanResolveLocalMetrics() counter.Add(1); // Meters from the factory can't be disposed, you have to dispose the whole factory. - factory.Dispose(); + sp.Dispose(); Assert.Equal("TestMeter-counter Started; Description: I count blips." + Environment.NewLine + "TestMeter-counter 4 blip" + Environment.NewLine diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index 7e0199e2e93fb9..0a1377034f4697 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -139,9 +139,7 @@ public void RuleCanBeTurnedOffAndOnAgain() RemoteExecutor.Invoke(() => { var publishCalled = 0; - var publishedTcs = new TaskCompletionSource(); var onCompletedCalled = 0; - var completedTcs = new TaskCompletionSource(); var measurements = new List(); using var services = new ServiceCollection().AddMetrics().BuildServiceProvider(); @@ -152,17 +150,17 @@ public void RuleCanBeTurnedOffAndOnAgain() { publishCalled++; Assert.Equal(factory, instrument.Meter.Scope); // Local - publishedTcs.TrySetResult(instrument); - return (true, null); + return (true, fakeListener); }; fakeListener.OnCompleted = (instrument, state) => { onCompletedCalled++; - completedTcs.TrySetResult(instrument); + Assert.Equal(fakeListener, state); }; fakeListener.OnMeasurement = (instrument, measurement, tags, state) => { measurements.Add((int)measurement); + Assert.Equal(fakeListener, state); }; var subscription = new ListenerSubscription(fakeListener, factory); @@ -176,26 +174,23 @@ public void RuleCanBeTurnedOffAndOnAgain() var counter = meter.CreateCounter("counter", "blip", "I count blips"); counter.Add(1); - Assert.False(publishedTcs.Task.IsCompleted); Assert.Equal(0, measurements.Count); Assert.Equal(0, publishCalled); + Assert.Equal(0, onCompletedCalled); // Add a rule that matches the counter. subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: true) }); - Assert.True(publishedTcs.Task.IsCompleted); Assert.Equal(1, publishCalled); counter.Add(2); Assert.Equal(1, measurements.Count); Assert.Equal(2, measurements[0]); - - Assert.False(completedTcs.Task.IsCompleted); + Assert.Equal(0, onCompletedCalled); // Disable subscription.UpdateRules(new[] { new InstrumentRule("TestMeter", "counter", null, MeterScope.Local, enable: false) }); - Assert.True(completedTcs.Task.IsCompleted); Assert.Equal(1, onCompletedCalled); counter.Add(3); // Not received @@ -218,7 +213,6 @@ public void RuleCanBeTurnedOffAndOnAgain() // [InlineData(null, null, null)] // RemoteExecutor can't handle nulls [InlineData("", "", "")] [InlineData("*", "", "")] - [InlineData("*.*", "", "")] [InlineData("lonG", "", "")] [InlineData("lonG.*", "", "")] [InlineData("lonG.sillY.meteR", "", "")] @@ -241,6 +235,7 @@ public void RuleMatchesTest(string meterName, string instrumentName, string list } [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData("*.*", "", "")] [InlineData("", "*", "")] [InlineData("", "", "*")] [InlineData("lonG.", "", "")] @@ -289,15 +284,15 @@ public void IsMoreSpecificTest(InstrumentRule rule, InstrumentRule? best, bool i new object[] { new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), new InstrumentRule(null, null, null, MeterScope.Global, true), false }, - // Meter > Instrument > Listener - new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), + // Listener > Meter > Instrument + new object[] { new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), + new InstrumentRule("meterName", null, null, MeterScope.Global, true), false }, + new object[] { new InstrumentRule(null, "instrumentName", "listenerName", MeterScope.Global, true), + new InstrumentRule("meterName", null, null, MeterScope.Global, true), false }, + new object[] { new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, - new object[] { new InstrumentRule("meterName", null, null, MeterScope.Global, true), - new InstrumentRule(null, "instrumentName", "listenerName", MeterScope.Global, true), false }, - new object[] { new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, + new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), false }, // Multiple fields are better than one. new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), @@ -314,8 +309,9 @@ public void IsMoreSpecificTest(InstrumentRule rule, InstrumentRule? best, bool i new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), new InstrumentRule(null, "instrumentName", null, MeterScope.Global, true), false }, - new object[] { new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), - new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, + // Except Listener wins regardless + new object[] { new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), + new InstrumentRule("meterName", "instrumentName", null, MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", null, "listenerName", MeterScope.Global, true), new InstrumentRule(null, null, "listenerName", MeterScope.Global, true), false }, new object[] { new InstrumentRule("meterName", "instrumentName", "listenerName", MeterScope.Global, true), From 9f32fa08a42917b727f3b3058996988fccc19efe Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 10 Aug 2023 13:41:46 -0700 Subject: [PATCH 48/50] Review feedback --- .../MetricsBuilderExtensions.Listeners.cs | 2 +- .../Metrics/MetricsBuilderExtensions.Rules.cs | 2 +- .../src/Metrics/DebugConsoleMetricListener.cs | 17 ++++++++--- .../src/Metrics/ListenerSubscription.cs | 28 +++++-------------- .../ref/Microsoft.Extensions.Hosting.cs | 4 +-- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Listeners.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Listeners.cs index 040acbb20cba08..f405af4adf83b2 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Listeners.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Listeners.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { /// - /// Extension methods for to add or clear registrations. + /// Extension methods for to add or clear registrations, and to enable or disable metrics. /// public static partial class MetricsBuilderExtensions { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Rules.cs b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Rules.cs index 87cdc54335d6ea..6b896aeffd75a9 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Rules.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics.Abstractions/src/Metrics/MetricsBuilderExtensions.Rules.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Diagnostics.Metrics { /// - /// Extension methods for to enable or disable metrics. + /// Extension methods for to add or clear registrations, and to enable or disable metrics. /// public static partial class MetricsBuilderExtensions { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs index 888d5e7f740357..4023c7cf9cb9dc 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs @@ -13,18 +13,26 @@ namespace Microsoft.Extensions.Diagnostics.Metrics internal sealed class DebugConsoleMetricListener : IMetricsListener, IDisposable { private readonly Timer _timer; - internal TextWriter _textWriter = Console.Out; + private int _timerStarted; private IObservableInstrumentsSource? _source; + // For testing + internal TextWriter? _textWriter; public DebugConsoleMetricListener() { - _timer = new Timer(OnTimer, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); + _timer = new Timer(OnTimer, null, Timeout.Infinite, Timeout.Infinite); } public string Name => ConsoleMetrics.DebugListenerName; public bool InstrumentPublished(Instrument instrument, out object? userState) { + // Start the timer if this is the first observable instrument. + if (instrument.IsObservable && Interlocked.Exchange(ref _timerStarted, 1) == 0) + { + _timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); + } + WriteLine($"{instrument.Meter.Name}-{instrument.Name} Started; Description: {instrument.Description}."); userState = this; return true; @@ -57,9 +65,10 @@ private void MeasurementHandler(Instrument instrument, T measurement, ReadOnl private void WriteLine(string output) { - lock (_textWriter) + var writer = _textWriter ?? Console.Out; + lock (writer) { - _textWriter.WriteLine(output); + writer.WriteLine(output); } } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 6dea3bae1d050b..5e1eee44d0b319 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -13,7 +13,7 @@ internal sealed class ListenerSubscription : IObservableInstrumentsSource, IDisp private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; private readonly IMeterFactory _meterFactory; - private readonly Dictionary _instruments = new(); + private readonly Dictionary _instruments = new(ReferenceEqualityComparer.Instance); private IList _rules = Array.Empty(); private bool _disposed; @@ -184,26 +184,12 @@ internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, str return true; } - // Rule "System.Net.Http" doesn't match meter "System.Net" - if (ruleMeterName.Length > instrument.Meter.Name.Length) - { - return false; - } - - // Exact match +/- ".*" - if (ruleMeterName.Equals(instrument.Meter.Name.AsSpan(), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - // Rule "System.Data" doesn't match meter "System.Net" - if (!instrument.Meter.Name.AsSpan().StartsWith(ruleMeterName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - // Only allow StartsWith on segment boundaries - return instrument.Meter.Name[ruleMeterName.Length] == '.'; + // "System.Net" matches "System.Net" and "System.Net.Http" + return instrument.Meter.Name.AsSpan().StartsWith(ruleMeterName, StringComparison.OrdinalIgnoreCase) + // Exact match +/- ".*" + && (ruleMeterName.Length == instrument.Meter.Name.Length + // Only allow StartsWith on segment boundaries + || ruleMeterName[instrument.Meter.Name.Length] == '.'); } // Everything must already match the Instrument and listener, or be blank. diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs index b0ddb0e2274c47..b9c2a6021282de 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs @@ -73,6 +73,8 @@ public static partial class HostingHostBuilderExtensions public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureHostOptions(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureOptions) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } + public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureMetrics(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureMetrics) => throw null!; + public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureMetrics(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureMetrics) => throw null!; public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureServices(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] @@ -98,8 +100,6 @@ public static partial class HostingHostBuilderExtensions public static Microsoft.Extensions.Hosting.IHostBuilder UseDefaultServiceProvider(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configure) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseDefaultServiceProvider(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configure) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseEnvironment(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, string environment) { throw null; } - public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureMetrics(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureMetrics) => throw null!; - public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureMetrics(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureMetrics) => throw null!; } public partial class HostOptions { From 5b155237d8512797c9204eac75251c766fb22153 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 10 Aug 2023 13:50:09 -0700 Subject: [PATCH 49/50] Compiler issues --- .../src/Metrics/DebugConsoleMetricListener.cs | 5 +++-- .../src/Metrics/ListenerSubscription.cs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs index 4023c7cf9cb9dc..293023e13e7eb2 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/DebugConsoleMetricListener.cs @@ -15,8 +15,9 @@ internal sealed class DebugConsoleMetricListener : IMetricsListener, IDisposable private readonly Timer _timer; private int _timerStarted; private IObservableInstrumentsSource? _source; - // For testing - internal TextWriter? _textWriter; +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value + internal TextWriter? _textWriter; // For testing +#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value public DebugConsoleMetricListener() { diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 5e1eee44d0b319..1958c039b8cd71 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -13,7 +13,7 @@ internal sealed class ListenerSubscription : IObservableInstrumentsSource, IDisp private readonly MeterListener _meterListener; private readonly IMetricsListener _metricsListener; private readonly IMeterFactory _meterFactory; - private readonly Dictionary _instruments = new(ReferenceEqualityComparer.Instance); + private readonly Dictionary _instruments = new(); private IList _rules = Array.Empty(); private bool _disposed; @@ -189,7 +189,7 @@ internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, str // Exact match +/- ".*" && (ruleMeterName.Length == instrument.Meter.Name.Length // Only allow StartsWith on segment boundaries - || ruleMeterName[instrument.Meter.Name.Length] == '.'); + || instrument.Meter.Name[ruleMeterName.Length] == '.'); } // Everything must already match the Instrument and listener, or be blank. From c9d6b3754ec64160c27202eb8f9b5f1169a7ea04 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 10 Aug 2023 14:02:58 -0700 Subject: [PATCH 50/50] Foo* --- .../src/Metrics/ListenerSubscription.cs | 9 +++++++-- .../tests/ListenerSubscriptionTests.cs | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs index 1958c039b8cd71..24c6d0b7b7f297 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/src/Metrics/ListenerSubscription.cs @@ -177,9 +177,14 @@ internal static bool RuleMatches(InstrumentRule rule, Instrument instrument, str { ruleMeterName = ruleMeterName.Slice(0, ruleMeterName.Length - 2); } + // System.Net* matches System.Net and System.Net.Http + else if (starIndex != -1) + { + ruleMeterName = ruleMeterName.Slice(0, ruleMeterName.Length - 1); + } - // Rule "" or "*" matches everything - if (ruleMeterName.IsEmpty || ruleMeterName.Equals("*".AsSpan(), StringComparison.Ordinal)) + // Rule "" matches everything + if (ruleMeterName.IsEmpty) { return true; } diff --git a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs index 0a1377034f4697..da59cffc28d6a6 100644 --- a/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs +++ b/src/libraries/Microsoft.Extensions.Diagnostics/tests/ListenerSubscriptionTests.cs @@ -214,8 +214,10 @@ public void RuleCanBeTurnedOffAndOnAgain() [InlineData("", "", "")] [InlineData("*", "", "")] [InlineData("lonG", "", "")] + [InlineData("lonG*", "", "")] [InlineData("lonG.*", "", "")] [InlineData("lonG.sillY.meteR", "", "")] + [InlineData("lonG.sillY.meteR*", "", "")] [InlineData("lonG.sillY.meteR.*", "", "")] [InlineData("lonG.sillY.meteR.namE", "", "")] [InlineData("lonG.sillY.meteR.namE.*", "", "")] @@ -239,8 +241,8 @@ public void RuleMatchesTest(string meterName, string instrumentName, string list [InlineData("", "*", "")] [InlineData("", "", "*")] [InlineData("lonG.", "", "")] - [InlineData("lonG*", "", "")] [InlineData("lonG.sil", "", "")] + [InlineData("lonG.sil*", "", "")] [InlineData("sillY.meteR.namE", "", "")] [InlineData("namE", "", "")] [InlineData("*.namE", "", "")]