diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 0413b558a486..e6807a616d9c 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -62,6 +62,7 @@ and are generated based on the last package release. + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index c7b19af3f9a8..66a8b1739965 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -385,6 +385,10 @@ https://github.com/dotnet/arcade e2334b2be36919347923d0ec872a46acddb1e385 + + https://github.com/dotnet/extensions + a0e9c8794e3e0ba27033a9f54a545385228d0876 + https://github.com/nuget/nuget.client 8fef55f5a55a3b4f2c96cd1a9b5ddc51d4b927f8 diff --git a/eng/Versions.props b/eng/Versions.props index 74436a8eb639..f4ad7dd980d9 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -134,6 +134,8 @@ 8.0.0-preview.6.23318.9 8.0.0-preview.6.23318.9 8.0.0-preview.6.23318.9 + + 8.0.0-preview.6.23320.3 8.0.0-preview.6.23319.5 8.0.0-preview.6.23319.5 diff --git a/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs b/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs index 34850626e089..f9efe83a2716 100644 --- a/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs +++ b/src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Diagnostics.Metrics; using System.Diagnostics.Tracing; using System.Reflection; using Microsoft.AspNetCore.Http; @@ -12,6 +11,7 @@ using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; +using Microsoft.Extensions.Telemetry.Testing.Metering; using Moq; namespace Microsoft.AspNetCore.Hosting.Tests; @@ -52,10 +52,10 @@ public async Task EventCountersAndMetricsValues() var hostingApplication1 = CreateApplication(out var features1, eventSource: hostingEventSource, meterFactory: testMeterFactory1); var hostingApplication2 = CreateApplication(out var features2, eventSource: hostingEventSource, meterFactory: testMeterFactory2); - using var currentRequestsRecorder1 = new InstrumentRecorder(testMeterFactory1, HostingMetrics.MeterName, "http-server-current-requests"); - using var currentRequestsRecorder2 = new InstrumentRecorder(testMeterFactory2, HostingMetrics.MeterName, "http-server-current-requests"); - using var requestDurationRecorder1 = new InstrumentRecorder(testMeterFactory1, HostingMetrics.MeterName, "http-server-request-duration"); - using var requestDurationRecorder2 = new InstrumentRecorder(testMeterFactory2, HostingMetrics.MeterName, "http-server-request-duration"); + using var currentRequestsRecorder1 = new MetricCollector(testMeterFactory1, HostingMetrics.MeterName, "http-server-current-requests"); + using var currentRequestsRecorder2 = new MetricCollector(testMeterFactory2, HostingMetrics.MeterName, "http-server-current-requests"); + using var requestDurationRecorder1 = new MetricCollector(testMeterFactory1, HostingMetrics.MeterName, "http-server-request-duration"); + using var requestDurationRecorder2 = new MetricCollector(testMeterFactory2, HostingMetrics.MeterName, "http-server-request-duration"); // Act/Assert 1 var context1 = hostingApplication1.CreateContext(features1); @@ -74,15 +74,15 @@ public async Task EventCountersAndMetricsValues() Assert.Equal(0, await currentRequestValues.FirstOrDefault(v => v == 0)); Assert.Equal(0, await failedRequestValues.FirstOrDefault(v => v == 0)); - Assert.Collection(currentRequestsRecorder1.GetMeasurements(), + Assert.Collection(currentRequestsRecorder1.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - Assert.Collection(currentRequestsRecorder2.GetMeasurements(), + Assert.Collection(currentRequestsRecorder2.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - Assert.Collection(requestDurationRecorder1.GetMeasurements(), + Assert.Collection(requestDurationRecorder1.GetMeasurementSnapshot(), m => Assert.True(m.Value > 0)); - Assert.Collection(requestDurationRecorder2.GetMeasurements(), + Assert.Collection(requestDurationRecorder2.GetMeasurementSnapshot(), m => Assert.True(m.Value > 0)); // Act/Assert 2 @@ -105,20 +105,20 @@ public async Task EventCountersAndMetricsValues() Assert.Equal(0, await currentRequestValues.FirstOrDefault(v => v == 0)); Assert.Equal(2, await failedRequestValues.FirstOrDefault(v => v == 2)); - Assert.Collection(currentRequestsRecorder1.GetMeasurements(), + Assert.Collection(currentRequestsRecorder1.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - Assert.Collection(currentRequestsRecorder2.GetMeasurements(), + Assert.Collection(currentRequestsRecorder2.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - Assert.Collection(requestDurationRecorder1.GetMeasurements(), + Assert.Collection(requestDurationRecorder1.GetMeasurementSnapshot(), m => Assert.True(m.Value > 0), m => Assert.True(m.Value > 0)); - Assert.Collection(requestDurationRecorder2.GetMeasurements(), + Assert.Collection(requestDurationRecorder2.GetMeasurementSnapshot(), m => Assert.True(m.Value > 0), m => Assert.True(m.Value > 0)); } diff --git a/src/Hosting/Hosting/test/HostingMetricsTests.cs b/src/Hosting/Hosting/test/HostingMetricsTests.cs index 9fedea8f0807..33598ec8abce 100644 --- a/src/Hosting/Hosting/test/HostingMetricsTests.cs +++ b/src/Hosting/Hosting/test/HostingMetricsTests.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Telemetry.Testing.Metering; namespace Microsoft.AspNetCore.Hosting.Tests; @@ -25,9 +26,9 @@ public void MultipleRequests() var httpContext = new DefaultHttpContext(); var meter = meterFactory.Meters.Single(); - using var requestDurationRecorder = new InstrumentRecorder(meterFactory, HostingMetrics.MeterName, "http-server-request-duration"); - using var currentRequestsRecorder = new InstrumentRecorder(meterFactory, HostingMetrics.MeterName, "http-server-current-requests"); - using var unhandledRequestsRecorder = new InstrumentRecorder(meterFactory, HostingMetrics.MeterName, "http-server-unhandled-requests"); + using var requestDurationCollector = new MetricCollector(meterFactory, HostingMetrics.MeterName, "http-server-request-duration"); + using var currentRequestsCollector = new MetricCollector(meterFactory, HostingMetrics.MeterName, "http-server-current-requests"); + using var unhandledRequestsCollector = new MetricCollector(meterFactory, HostingMetrics.MeterName, "http-server-unhandled-requests"); // Act/Assert Assert.Equal(HostingMetrics.MeterName, meter.Name); @@ -39,10 +40,10 @@ public void MultipleRequests() context1.HttpContext.Response.StatusCode = StatusCodes.Status200OK; hostingApplication.DisposeContext(context1, null); - Assert.Collection(currentRequestsRecorder.GetMeasurements(), + Assert.Collection(currentRequestsCollector.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - Assert.Collection(requestDurationRecorder.GetMeasurements(), + Assert.Collection(requestDurationCollector.GetMeasurementSnapshot(), m => AssertRequestDuration(m, HttpProtocol.Http11, StatusCodes.Status200OK)); // Request 2 (after failure) @@ -51,12 +52,12 @@ public void MultipleRequests() context2.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; hostingApplication.DisposeContext(context2, new InvalidOperationException("Test error")); - Assert.Collection(currentRequestsRecorder.GetMeasurements(), + Assert.Collection(currentRequestsCollector.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - Assert.Collection(requestDurationRecorder.GetMeasurements(), + Assert.Collection(requestDurationCollector.GetMeasurementSnapshot(), m => AssertRequestDuration(m, HttpProtocol.Http11, StatusCodes.Status200OK), m => AssertRequestDuration(m, HttpProtocol.Http2, StatusCodes.Status500InternalServerError, exceptionName: "System.InvalidOperationException")); @@ -66,37 +67,37 @@ public void MultipleRequests() context3.HttpContext.Items["__RequestUnhandled"] = true; context3.HttpContext.Response.StatusCode = StatusCodes.Status404NotFound; - Assert.Collection(currentRequestsRecorder.GetMeasurements(), + Assert.Collection(currentRequestsCollector.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value), m => Assert.Equal(1, m.Value)); - Assert.Collection(requestDurationRecorder.GetMeasurements(), + Assert.Collection(requestDurationCollector.GetMeasurementSnapshot(), m => AssertRequestDuration(m, HttpProtocol.Http11, StatusCodes.Status200OK), m => AssertRequestDuration(m, HttpProtocol.Http2, StatusCodes.Status500InternalServerError, exceptionName: "System.InvalidOperationException")); hostingApplication.DisposeContext(context3, null); - Assert.Collection(currentRequestsRecorder.GetMeasurements(), + Assert.Collection(currentRequestsCollector.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - Assert.Collection(requestDurationRecorder.GetMeasurements(), + Assert.Collection(requestDurationCollector.GetMeasurementSnapshot(), m => AssertRequestDuration(m, HttpProtocol.Http11, StatusCodes.Status200OK), m => AssertRequestDuration(m, HttpProtocol.Http2, StatusCodes.Status500InternalServerError, exceptionName: "System.InvalidOperationException"), m => AssertRequestDuration(m, HttpProtocol.Http3, StatusCodes.Status404NotFound)); - Assert.Collection(unhandledRequestsRecorder.GetMeasurements(), + Assert.Collection(unhandledRequestsCollector.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value)); - static void AssertRequestDuration(Measurement measurement, string protocol, int statusCode, string exceptionName = null) + static void AssertRequestDuration(CollectedMeasurement measurement, string protocol, int statusCode, string exceptionName = null) { Assert.True(measurement.Value > 0); - Assert.Equal(protocol, (string)measurement.Tags.ToArray().Single(t => t.Key == "protocol").Value); - Assert.Equal(statusCode, (int)measurement.Tags.ToArray().Single(t => t.Key == "status-code").Value); + Assert.Equal(protocol, (string)measurement.Tags["protocol"]); + Assert.Equal(statusCode, (int)measurement.Tags["status-code"]); if (exceptionName == null) { Assert.DoesNotContain(measurement.Tags.ToArray(), t => t.Key == "exception-name"); @@ -132,8 +133,8 @@ public async Task StartListeningDuringRequest_NotMeasured() await syncPoint.WaitForSyncPoint().DefaultTimeout(); - using var requestDurationRecorder = new InstrumentRecorder(meterFactory, HostingMetrics.MeterName, "http-server-request-duration"); - using var currentRequestsRecorder = new InstrumentRecorder(meterFactory, HostingMetrics.MeterName, "http-server-current-requests"); + using var requestDurationCollector = new MetricCollector(meterFactory, HostingMetrics.MeterName, "http-server-request-duration"); + using var currentRequestsCollector = new MetricCollector(meterFactory, HostingMetrics.MeterName, "http-server-current-requests"); context1.HttpContext.Response.StatusCode = StatusCodes.Status200OK; syncPoint.Continue(); @@ -141,8 +142,8 @@ public async Task StartListeningDuringRequest_NotMeasured() hostingApplication.DisposeContext(context1, null); - Assert.Empty(currentRequestsRecorder.GetMeasurements()); - Assert.Empty(requestDurationRecorder.GetMeasurements()); + Assert.Empty(currentRequestsCollector.GetMeasurementSnapshot()); + Assert.Empty(requestDurationCollector.GetMeasurementSnapshot()); } [Fact] @@ -154,8 +155,8 @@ public void IHttpMetricsTagsFeatureNotUsedFromFeatureCollection() var httpContext = new DefaultHttpContext(); var meter = meterFactory.Meters.Single(); - using var requestDurationRecorder = new InstrumentRecorder(meterFactory, HostingMetrics.MeterName, "http-server-request-duration"); - using var currentRequestsRecorder = new InstrumentRecorder(meterFactory, HostingMetrics.MeterName, "http-server-current-requests"); + using var requestDurationCollector = new MetricCollector(meterFactory, HostingMetrics.MeterName, "http-server-request-duration"); + using var currentRequestsCollector = new MetricCollector(meterFactory, HostingMetrics.MeterName, "http-server-current-requests"); // Act/Assert Assert.Equal(HostingMetrics.MeterName, meter.Name); diff --git a/src/Hosting/Hosting/test/Microsoft.AspNetCore.Hosting.Tests.csproj b/src/Hosting/Hosting/test/Microsoft.AspNetCore.Hosting.Tests.csproj index 0fc87b4ba5c3..19285bbe7ec3 100644 --- a/src/Hosting/Hosting/test/Microsoft.AspNetCore.Hosting.Tests.csproj +++ b/src/Hosting/Hosting/test/Microsoft.AspNetCore.Hosting.Tests.csproj @@ -22,6 +22,7 @@ + diff --git a/src/Http/Routing/test/UnitTests/Microsoft.AspNetCore.Routing.Tests.csproj b/src/Http/Routing/test/UnitTests/Microsoft.AspNetCore.Routing.Tests.csproj index 39ae97878631..15a5702547b1 100644 --- a/src/Http/Routing/test/UnitTests/Microsoft.AspNetCore.Routing.Tests.csproj +++ b/src/Http/Routing/test/UnitTests/Microsoft.AspNetCore.Routing.Tests.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Http/Routing/test/UnitTests/RoutingMetricsTests.cs b/src/Http/Routing/test/UnitTests/RoutingMetricsTests.cs index 6998afd595a0..e8338013317b 100644 --- a/src/Http/Routing/test/UnitTests/RoutingMetricsTests.cs +++ b/src/Http/Routing/test/UnitTests/RoutingMetricsTests.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Telemetry.Testing.Metering; using Moq; namespace Microsoft.AspNetCore.Routing; @@ -34,8 +35,8 @@ public async Task Match_Success() var httpContext = new DefaultHttpContext(); var meter = meterFactory.Meters.Single(); - using var routingMatchSuccessRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); - using var routingMatchFailureRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); + using var routingMatchSuccessCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); + using var routingMatchFailureCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); // Act await middleware.Invoke(httpContext); @@ -44,9 +45,9 @@ public async Task Match_Success() Assert.Equal(RoutingMetrics.MeterName, meter.Name); Assert.Null(meter.Version); - Assert.Collection(routingMatchSuccessRecorder.GetMeasurements(), + Assert.Collection(routingMatchSuccessCollector.GetMeasurementSnapshot(), m => AssertSuccess(m, "/{hi}", fallback: false)); - Assert.Empty(routingMatchFailureRecorder.GetMeasurements()); + Assert.Empty(routingMatchFailureCollector.GetMeasurementSnapshot()); } [Theory] @@ -70,8 +71,8 @@ public async Task Match_SuccessFallback_SetTagIfPresent(bool hasFallbackMetadata var httpContext = new DefaultHttpContext(); var meter = meterFactory.Meters.Single(); - using var routingMatchSuccessRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); - using var routingMatchFailureRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); + using var routingMatchSuccessCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); + using var routingMatchFailureCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); // Act await middleware.Invoke(httpContext); @@ -80,9 +81,9 @@ public async Task Match_SuccessFallback_SetTagIfPresent(bool hasFallbackMetadata Assert.Equal(RoutingMetrics.MeterName, meter.Name); Assert.Null(meter.Version); - Assert.Collection(routingMatchSuccessRecorder.GetMeasurements(), + Assert.Collection(routingMatchSuccessCollector.GetMeasurementSnapshot(), m => AssertSuccess(m, "/{hi}", fallback: hasFallbackMetadata)); - Assert.Empty(routingMatchFailureRecorder.GetMeasurements()); + Assert.Empty(routingMatchFailureCollector.GetMeasurementSnapshot()); } [Fact] @@ -99,8 +100,8 @@ public async Task Match_Success_MissingRoute() var httpContext = new DefaultHttpContext(); var meter = meterFactory.Meters.Single(); - using var routingMatchSuccessRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); - using var routingMatchFailureRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); + using var routingMatchSuccessCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); + using var routingMatchFailureCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); // Act await middleware.Invoke(httpContext); @@ -109,9 +110,9 @@ public async Task Match_Success_MissingRoute() Assert.Equal(RoutingMetrics.MeterName, meter.Name); Assert.Null(meter.Version); - Assert.Collection(routingMatchSuccessRecorder.GetMeasurements(), + Assert.Collection(routingMatchSuccessCollector.GetMeasurementSnapshot(), m => AssertSuccess(m, "(missing)", fallback: false)); - Assert.Empty(routingMatchFailureRecorder.GetMeasurements()); + Assert.Empty(routingMatchFailureCollector.GetMeasurementSnapshot()); } [Fact] @@ -125,8 +126,8 @@ public async Task Match_Failure() var httpContext = new DefaultHttpContext(); var meter = meterFactory.Meters.Single(); - using var routingMatchSuccessRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); - using var routingMatchFailureRecorder = new InstrumentRecorder(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); + using var routingMatchSuccessCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-success"); + using var routingMatchFailureCollector = new MetricCollector(meterFactory, RoutingMetrics.MeterName, "routing-match-failure"); // Act await middleware.Invoke(httpContext); @@ -135,16 +136,16 @@ public async Task Match_Failure() Assert.Equal(RoutingMetrics.MeterName, meter.Name); Assert.Null(meter.Version); - Assert.Empty(routingMatchSuccessRecorder.GetMeasurements()); - Assert.Collection(routingMatchFailureRecorder.GetMeasurements(), + Assert.Empty(routingMatchSuccessCollector.GetMeasurementSnapshot()); + Assert.Collection(routingMatchFailureCollector.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value)); } - private void AssertSuccess(Measurement measurement, string route, bool fallback) + private void AssertSuccess(CollectedMeasurement measurement, string route, bool fallback) { Assert.Equal(1, measurement.Value); - Assert.Equal(route, (string)measurement.Tags.ToArray().Single(t => t.Key == "route").Value); - Assert.Equal(fallback, (bool)measurement.Tags.ToArray().Single(t => t.Key == "fallback").Value); + Assert.Equal(route, (string)measurement.Tags["route"]); + Assert.Equal(fallback, (bool)measurement.Tags["fallback"]); } private EndpointRoutingMiddleware CreateMiddleware( diff --git a/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs b/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs index 435ccb394be2..5df91e9de41a 100644 --- a/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs +++ b/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs @@ -17,6 +17,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Telemetry.Testing.Metering; namespace Microsoft.AspNetCore.Diagnostics; @@ -538,16 +539,9 @@ public async Task NullInfoInCompilationException_ShouldNotThrowExceptionGenerati public async Task UnhandledError_ExceptionNameTagAdded() { // Arrange - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var meterFactory = new TestMeterFactory(); - using var requestDurationRecorder = new InstrumentRecorder(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration"); - using var requestExceptionRecorder = new InstrumentRecorder(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); - using var measurementReporter = new MeasurementReporter(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration"); - measurementReporter.Register(m => - { - tcs.SetResult(); - }); + using var requestDurationCollector = new MetricCollector(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration"); + using var requestExceptionCollector = new MetricCollector(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); using var host = new HostBuilder() .ConfigureServices(s => @@ -577,33 +571,33 @@ public async Task UnhandledError_ExceptionNameTagAdded() var response = await server.CreateClient().GetAsync("/path"); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); - await tcs.Task.DefaultTimeout(); + await requestDurationCollector.WaitForMeasurementsAsync(numMeasurements: 1).DefaultTimeout(); // Assert Assert.Collection( - requestDurationRecorder.GetMeasurements(), + requestDurationCollector.GetMeasurementSnapshot(), m => { Assert.True(m.Value > 0); Assert.Equal(500, (int)m.Tags.ToArray().Single(t => t.Key == "status-code").Value); Assert.Equal("System.Exception", (string)m.Tags.ToArray().Single(t => t.Key == "exception-name").Value); }); - Assert.Collection(requestExceptionRecorder.GetMeasurements(), + Assert.Collection(requestExceptionCollector.GetMeasurementSnapshot(), m => AssertRequestException(m, "System.Exception", "Unhandled")); } - private static void AssertRequestException(Measurement measurement, string exceptionName, string result, string handler = null) + private static void AssertRequestException(CollectedMeasurement measurement, string exceptionName, string result, string handler = null) { Assert.Equal(1, measurement.Value); - Assert.Equal(exceptionName, (string)measurement.Tags.ToArray().Single(t => t.Key == "exception-name").Value); - Assert.Equal(result, measurement.Tags.ToArray().Single(t => t.Key == "result").Value.ToString()); + Assert.Equal(exceptionName, (string)measurement.Tags["exception-name"]); + Assert.Equal(result, measurement.Tags["result"].ToString()); if (handler == null) { - Assert.DoesNotContain(measurement.Tags.ToArray(), t => t.Key == "handler"); + Assert.False(measurement.Tags.ContainsKey("handler")); } else { - Assert.Equal(handler, (string)measurement.Tags.ToArray().Single(t => t.Key == "handler").Value); + Assert.Equal(handler, (string)measurement.Tags["handler"]); } } diff --git a/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerMiddlewareTest.cs b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerMiddlewareTest.cs index e6df4b587243..0a0b0fcf9c29 100644 --- a/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerMiddlewareTest.cs +++ b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerMiddlewareTest.cs @@ -19,6 +19,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Telemetry.Testing.Metering; using Moq; namespace Microsoft.AspNetCore.Diagnostics; @@ -210,7 +211,7 @@ public async Task Metrics_NoExceptionThrown() var middleware = CreateMiddleware(_ => Task.CompletedTask, optionsAccessor, exceptionHandlers, meterFactory); var meter = meterFactory.Meters.Single(); - using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); + using var diagnosticsRequestExceptionCollector = new MetricCollector(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); // Act await middleware.Invoke(httpContext); @@ -219,7 +220,7 @@ public async Task Metrics_NoExceptionThrown() Assert.Equal(DiagnosticsMetrics.MeterName, meter.Name); Assert.Null(meter.Version); - Assert.Empty(diagnosticsRequestExceptionRecorder.GetMeasurements()); + Assert.Empty(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot()); } [Fact] @@ -233,13 +234,13 @@ public async Task Metrics_ExceptionThrown_Handled_Reported() var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, exceptionHandlers, meterFactory); var meter = meterFactory.Meters.Single(); - using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); + using var diagnosticsRequestExceptionCollector = new MetricCollector(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); // Act await middleware.Invoke(httpContext); // Assert - Assert.Collection(diagnosticsRequestExceptionRecorder.GetMeasurements(), + Assert.Collection(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot(), m => AssertRequestException(m, "System.InvalidOperationException", "Handled", typeof(TestExceptionHandler).FullName)); } @@ -255,13 +256,13 @@ public async Task Metrics_ExceptionThrown_ResponseStarted_Skipped() var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, exceptionHandlers, meterFactory); var meter = meterFactory.Meters.Single(); - using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); + using var diagnosticsRequestExceptionCollector = new MetricCollector(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); // Act await Assert.ThrowsAsync(() => middleware.Invoke(httpContext)); // Assert - Assert.Collection(diagnosticsRequestExceptionRecorder.GetMeasurements(), + Assert.Collection(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot(), m => AssertRequestException(m, "System.InvalidOperationException", "Skipped")); } @@ -280,13 +281,13 @@ public async Task Metrics_ExceptionThrown_DefaultSettings_Handled_Reported() var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, meterFactory: meterFactory); var meter = meterFactory.Meters.Single(); - using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); + using var diagnosticsRequestExceptionCollector = new MetricCollector(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); // Act await middleware.Invoke(httpContext); // Assert - Assert.Collection(diagnosticsRequestExceptionRecorder.GetMeasurements(), + Assert.Collection(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot(), m => AssertRequestException(m, "System.InvalidOperationException", "Handled", null)); } @@ -304,28 +305,28 @@ public async Task Metrics_ExceptionThrown_Unhandled_Reported() var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, meterFactory: meterFactory); var meter = meterFactory.Meters.Single(); - using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); + using var diagnosticsRequestExceptionCollector = new MetricCollector(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception"); // Act await Assert.ThrowsAsync(() => middleware.Invoke(httpContext)); // Assert - Assert.Collection(diagnosticsRequestExceptionRecorder.GetMeasurements(), + Assert.Collection(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot(), m => AssertRequestException(m, "System.InvalidOperationException", "Unhandled")); } - private static void AssertRequestException(Measurement measurement, string exceptionName, string result, string handler = null) + private static void AssertRequestException(CollectedMeasurement measurement, string exceptionName, string result, string handler = null) { Assert.Equal(1, measurement.Value); - Assert.Equal(exceptionName, (string)measurement.Tags.ToArray().Single(t => t.Key == "exception-name").Value); - Assert.Equal(result, measurement.Tags.ToArray().Single(t => t.Key == "result").Value.ToString()); + Assert.Equal(exceptionName, (string)measurement.Tags["exception-name"]); + Assert.Equal(result, measurement.Tags["result"].ToString()); if (handler == null) { - Assert.DoesNotContain(measurement.Tags.ToArray(), t => t.Key == "handler"); + Assert.False(measurement.Tags.ContainsKey("handler")); } else { - Assert.Equal(handler, (string)measurement.Tags.ToArray().Single(t => t.Key == "handler").Value); + Assert.Equal(handler, (string)measurement.Tags["handler"]); } } diff --git a/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs index 13ec51b1903a..0d64d800dc1e 100644 --- a/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs +++ b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; +using Microsoft.Extensions.Telemetry.Testing.Metering; namespace Microsoft.AspNetCore.Diagnostics; @@ -915,15 +916,8 @@ public async Task ExceptionHandlerWithExceptionHandlerNotReplacedWithGlobalRoute public async Task UnhandledError_ExceptionNameTagAdded() { // Arrange - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var meterFactory = new TestMeterFactory(); - using var instrumentRecorder = new InstrumentRecorder(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration"); - using var measurementReporter = new MeasurementReporter(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration"); - measurementReporter.Register(m => - { - tcs.SetResult(); - }); + using var instrumentCollector = new MetricCollector(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration"); using var host = new HostBuilder() .ConfigureServices(s => @@ -959,11 +953,11 @@ public async Task UnhandledError_ExceptionNameTagAdded() var response = await server.CreateClient().GetAsync("/path"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - await tcs.Task.DefaultTimeout(); + await instrumentCollector.WaitForMeasurementsAsync(numMeasurements: 1).DefaultTimeout(); // Assert Assert.Collection( - instrumentRecorder.GetMeasurements(), + instrumentCollector.GetMeasurementSnapshot(), m => { Assert.True(m.Value > 0); diff --git a/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj b/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj index d01bdacc7058..6f2620a2e6ce 100644 --- a/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj +++ b/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Middleware/RateLimiting/test/Microsoft.AspNetCore.RateLimiting.Tests.csproj b/src/Middleware/RateLimiting/test/Microsoft.AspNetCore.RateLimiting.Tests.csproj index 7a307f93db64..56d83fee3271 100644 --- a/src/Middleware/RateLimiting/test/Microsoft.AspNetCore.RateLimiting.Tests.csproj +++ b/src/Middleware/RateLimiting/test/Microsoft.AspNetCore.RateLimiting.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Middleware/RateLimiting/test/RateLimitingMetricsTests.cs b/src/Middleware/RateLimiting/test/RateLimitingMetricsTests.cs index 6ffd16e92a47..972f325afc11 100644 --- a/src/Middleware/RateLimiting/test/RateLimitingMetricsTests.cs +++ b/src/Middleware/RateLimiting/test/RateLimitingMetricsTests.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Telemetry.Testing.Metering; using Moq; namespace Microsoft.AspNetCore.RateLimiting; @@ -34,11 +35,11 @@ public async Task Metrics_Rejected() var context = new DefaultHttpContext(); - using var leaseRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); - using var currentLeaseRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); - using var currentRequestsQueuedRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); - using var queuedRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); - using var leaseFailedRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); + using var leaseRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); + using var currentLeaseRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); + using var currentRequestsQueuedCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); + using var queuedRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); + using var leaseFailedRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); // Act await middleware.Invoke(context).DefaultTimeout(); @@ -46,11 +47,11 @@ public async Task Metrics_Rejected() // Assert Assert.Equal(StatusCodes.Status503ServiceUnavailable, context.Response.StatusCode); - Assert.Empty(currentLeaseRequestsRecorder.GetMeasurements()); - Assert.Empty(leaseRequestDurationRecorder.GetMeasurements()); - Assert.Empty(currentRequestsQueuedRecorder.GetMeasurements()); - Assert.Empty(queuedRequestDurationRecorder.GetMeasurements()); - Assert.Collection(leaseFailedRequestsRecorder.GetMeasurements(), + Assert.Empty(currentLeaseRequestsCollector.GetMeasurementSnapshot()); + Assert.Empty(leaseRequestDurationCollector.GetMeasurementSnapshot()); + Assert.Empty(currentRequestsQueuedCollector.GetMeasurementSnapshot()); + Assert.Empty(queuedRequestDurationCollector.GetMeasurementSnapshot()); + Assert.Collection(leaseFailedRequestsCollector.GetMeasurementSnapshot(), m => { Assert.Equal(1, m.Value); @@ -81,20 +82,20 @@ public async Task Metrics_Success() var context = new DefaultHttpContext(); context.Request.Method = "GET"; - using var leaseRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); - using var currentLeaseRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); - using var currentRequestsQueuedRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); - using var queuedRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); - using var leaseFailedRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); + using var leaseRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); + using var currentLeaseRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); + using var currentRequestsQueuedCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); + using var queuedRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); + using var leaseFailedRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); // Act var middlewareTask = middleware.Invoke(context); await syncPoint.WaitForSyncPoint().DefaultTimeout(); - Assert.Collection(currentLeaseRequestsRecorder.GetMeasurements(), + Assert.Collection(currentLeaseRequestsCollector.GetMeasurementSnapshot(), m => AssertCounter(m, 1, null)); - Assert.Empty(leaseRequestDurationRecorder.GetMeasurements()); + Assert.Empty(leaseRequestDurationCollector.GetMeasurementSnapshot()); syncPoint.Continue(); @@ -103,14 +104,14 @@ public async Task Metrics_Success() // Assert Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); - Assert.Collection(currentLeaseRequestsRecorder.GetMeasurements(), + Assert.Collection(currentLeaseRequestsCollector.GetMeasurementSnapshot(), m => AssertCounter(m, 1, null), m => AssertCounter(m, -1, null)); - Assert.Collection(leaseRequestDurationRecorder.GetMeasurements(), + Assert.Collection(leaseRequestDurationCollector.GetMeasurementSnapshot(), m => AssertDuration(m, null)); - Assert.Empty(currentRequestsQueuedRecorder.GetMeasurements()); - Assert.Empty(queuedRequestDurationRecorder.GetMeasurements()); - Assert.Empty(leaseFailedRequestsRecorder.GetMeasurements()); + Assert.Empty(currentRequestsQueuedCollector.GetMeasurementSnapshot()); + Assert.Empty(queuedRequestDurationCollector.GetMeasurementSnapshot()); + Assert.Empty(leaseFailedRequestsCollector.GetMeasurementSnapshot()); } [Fact] @@ -141,11 +142,11 @@ public async Task Metrics_ListenInMiddleOfRequest_CurrentLeasesNotDecreased() await syncPoint.WaitForSyncPoint().DefaultTimeout(); - using var leaseRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); - using var currentLeaseRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); - using var currentRequestsQueuedRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); - using var queuedRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); - using var leaseFailedRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); + using var leaseRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); + using var currentLeaseRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); + using var currentRequestsQueuedCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); + using var queuedRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); + using var leaseFailedRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); syncPoint.Continue(); @@ -154,8 +155,8 @@ public async Task Metrics_ListenInMiddleOfRequest_CurrentLeasesNotDecreased() // Assert Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); - Assert.Empty(currentLeaseRequestsRecorder.GetMeasurements()); - Assert.Collection(leaseRequestDurationRecorder.GetMeasurements(), + Assert.Empty(currentLeaseRequestsCollector.GetMeasurementSnapshot()); + Assert.Collection(leaseRequestDurationCollector.GetMeasurementSnapshot(), m => AssertDuration(m, null)); } @@ -192,11 +193,11 @@ public async Task Metrics_Queued() routeEndpointBuilder.Metadata.Add(new EnableRateLimitingAttribute("concurrencyPolicy")); var endpoint = routeEndpointBuilder.Build(); - using var leaseRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); - using var currentLeaseRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); - using var currentRequestsQueuedRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); - using var queuedRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); - using var leaseFailedRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); + using var leaseRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); + using var currentLeaseRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); + using var currentRequestsQueuedCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); + using var queuedRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); + using var leaseFailedRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); // Act var context1 = new DefaultHttpContext(); @@ -213,9 +214,9 @@ public async Task Metrics_Queued() var middlewareTask2 = middleware.Invoke(context1); // Assert second request is queued. - Assert.Collection(currentRequestsQueuedRecorder.GetMeasurements(), + Assert.Collection(currentRequestsQueuedCollector.GetMeasurementSnapshot(), m => AssertCounter(m, 1, "concurrencyPolicy")); - Assert.Empty(queuedRequestDurationRecorder.GetMeasurements()); + Assert.Empty(queuedRequestDurationCollector.GetMeasurementSnapshot()); // Allow both requests to finish. syncPoint.Continue(); @@ -223,10 +224,10 @@ public async Task Metrics_Queued() await middlewareTask1.DefaultTimeout(); await middlewareTask2.DefaultTimeout(); - Assert.Collection(currentRequestsQueuedRecorder.GetMeasurements(), + Assert.Collection(currentRequestsQueuedCollector.GetMeasurementSnapshot(), m => AssertCounter(m, 1, "concurrencyPolicy"), m => AssertCounter(m, -1, "concurrencyPolicy")); - Assert.Collection(queuedRequestDurationRecorder.GetMeasurements(), + Assert.Collection(queuedRequestDurationCollector.GetMeasurementSnapshot(), m => AssertDuration(m, "concurrencyPolicy")); } @@ -279,14 +280,14 @@ public async Task Metrics_ListenInMiddleOfQueued_CurrentQueueNotDecreased() // Start listening while the second request is queued. - using var leaseRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); - using var currentLeaseRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); - using var currentRequestsQueuedRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); - using var queuedRequestDurationRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); - using var leaseFailedRequestsRecorder = new InstrumentRecorder(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); + using var leaseRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration"); + using var currentLeaseRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests"); + using var currentRequestsQueuedCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests"); + using var queuedRequestDurationCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration"); + using var leaseFailedRequestsCollector = new MetricCollector(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests"); - Assert.Empty(currentRequestsQueuedRecorder.GetMeasurements()); - Assert.Empty(queuedRequestDurationRecorder.GetMeasurements()); + Assert.Empty(currentRequestsQueuedCollector.GetMeasurementSnapshot()); + Assert.Empty(queuedRequestDurationCollector.GetMeasurementSnapshot()); // Allow both requests to finish. syncPoint.Continue(); @@ -294,24 +295,24 @@ public async Task Metrics_ListenInMiddleOfQueued_CurrentQueueNotDecreased() await middlewareTask1.DefaultTimeout(); await middlewareTask2.DefaultTimeout(); - Assert.Empty(currentRequestsQueuedRecorder.GetMeasurements()); - Assert.Collection(queuedRequestDurationRecorder.GetMeasurements(), + Assert.Empty(currentRequestsQueuedCollector.GetMeasurementSnapshot()); + Assert.Collection(queuedRequestDurationCollector.GetMeasurementSnapshot(), m => AssertDuration(m, "concurrencyPolicy")); } - private static void AssertCounter(Measurement measurement, long value, string policy) + private static void AssertCounter(CollectedMeasurement measurement, long value, string policy) { Assert.Equal(value, measurement.Value); AssertTag(measurement.Tags, "policy", policy); } - private static void AssertDuration(Measurement measurement, string policy) + private static void AssertDuration(CollectedMeasurement measurement, string policy) { Assert.True(measurement.Value > 0); AssertTag(measurement.Tags, "policy", policy); } - private static void AssertTag(ReadOnlySpan> tags, string tagName, T expected) + private static void AssertTag(IReadOnlyDictionary tags, string tagName, T expected) { if (expected == null) { diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/ConnectionLimitTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/ConnectionLimitTests.cs index 3eb165e19a7b..7f30315d4f66 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/ConnectionLimitTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/ConnectionLimitTests.cs @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Tests; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Diagnostics.Metrics; +using Microsoft.Extensions.Telemetry.Testing.Metering; using Xunit; namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests; @@ -102,7 +103,7 @@ public async Task UpgradedConnectionsCountsAgainstDifferentLimit() public async Task RejectsConnectionsWhenLimitReached() { var testMeterFactory = new TestMeterFactory(); - using var rejectedConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-rejected-connections"); + using var rejectedConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-rejected-connections"); const int max = 10; var requestTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -141,14 +142,14 @@ public async Task RejectsConnectionsWhenLimitReached() } var actions = Enumerable.Repeat(AssertCounter, i + 1).ToArray(); - Assert.Collection(rejectedConnections.GetMeasurements(), actions); + Assert.Collection(rejectedConnections.GetMeasurementSnapshot(), actions); } requestTcs.TrySetResult(); } } - static void AssertCounter(Measurement measurement) => Assert.Equal(1, measurement.Value); + static void AssertCounter(CollectedMeasurement measurement) => Assert.Equal(1, measurement.Value); } [Fact] diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj index 7f5ed843519d..e688c0c79bc8 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/InMemory.FunctionalTests.csproj @@ -36,6 +36,7 @@ + diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/KestrelMetricsTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/KestrelMetricsTests.cs index 9945aa8be9ec..fafeb436fa38 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/KestrelMetricsTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/KestrelMetricsTests.cs @@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Diagnostics.Metrics; +using Microsoft.Extensions.Telemetry.Testing.Metering; namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests; @@ -46,9 +47,9 @@ public async Task Http1Connection() }); var testMeterFactory = new TestMeterFactory(); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); - using var queuedConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); + using var connectionDuration = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); + using var queuedConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory)); @@ -63,8 +64,8 @@ public async Task Http1Connection() // Wait for connection to start on the server. await sync.WaitForSyncPoint(); - Assert.Empty(connectionDuration.GetMeasurements()); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0")); + Assert.Empty(connectionDuration.GetMeasurementSnapshot()); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0")); // Signal that connection can continue. sync.Continue(); @@ -79,13 +80,13 @@ await connection.ReceiveEnd( await connection.WaitForConnectionClose(); } - Assert.Collection(connectionDuration.GetMeasurements(), m => + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => { AssertDuration(m, "127.0.0.1:0", "HTTP/1.1"); - Assert.Equal("value!", (string)m.Tags.ToArray().Single(t => t.Key == "custom").Value); + Assert.Equal("value!", (string)m.Tags["custom"]); }); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); - Assert.Collection(queuedConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(queuedConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); } [Fact] @@ -122,9 +123,9 @@ public async Task Http1Connection_BeginListeningAfterConnectionStarted() // Wait for connection to start on the server. await sync.WaitForSyncPoint(); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); - using var queuedConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); + using var connectionDuration = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); + using var queuedConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); // Signal that connection can continue. sync.Continue(); @@ -138,9 +139,9 @@ await connection.ReceiveEnd( await connection.WaitForConnectionClose(); - Assert.Empty(connectionDuration.GetMeasurements()); - Assert.Empty(currentConnections.GetMeasurements()); - Assert.Empty(queuedConnections.GetMeasurements()); + Assert.Empty(connectionDuration.GetMeasurementSnapshot()); + Assert.Empty(currentConnections.GetMeasurementSnapshot()); + Assert.Empty(queuedConnections.GetMeasurementSnapshot()); Assert.False(hasConnectionMetricsTagsFeature); } @@ -169,9 +170,9 @@ public async Task Http1Connection_IHttpConnectionTagsFeatureIgnoreFeatureSetOnTr }); var testMeterFactory = new TestMeterFactory(); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); - using var queuedConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); + using var connectionDuration = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); + using var queuedConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory)); @@ -195,8 +196,8 @@ public async Task Http1Connection_IHttpConnectionTagsFeatureIgnoreFeatureSetOnTr Assert.NotEqual(overridenFeature, currentConnectionContext.Features.Get()); - Assert.Empty(connectionDuration.GetMeasurements()); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0")); + Assert.Empty(connectionDuration.GetMeasurementSnapshot()); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0")); // Signal that connection can continue. sync.Continue(); @@ -211,14 +212,14 @@ await connection.ReceiveEnd( await connection.WaitForConnectionClose(); } - Assert.Collection(connectionDuration.GetMeasurements(), m => + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => { AssertDuration(m, "127.0.0.1:0", "HTTP/1.1"); - Assert.Equal("value!", (string)m.Tags.ToArray().Single(t => t.Key == "custom").Value); - Assert.Empty(m.Tags.ToArray().Where(t => t.Key == "test")); + Assert.Equal("value!", (string)m.Tags["custom"]); + Assert.False(m.Tags.ContainsKey("test")); }); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); - Assert.Collection(queuedConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(queuedConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); } private sealed class TestConnectionMetricsTagsFeature : IConnectionMetricsTagsFeature @@ -244,9 +245,9 @@ public async Task Http1Connection_Error() }); var testMeterFactory = new TestMeterFactory(); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); - using var queuedConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); + using var connectionDuration = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); + using var queuedConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory)); @@ -261,8 +262,8 @@ public async Task Http1Connection_Error() // Wait for connection to start on the server. await sync.WaitForSyncPoint(); - Assert.Empty(connectionDuration.GetMeasurements()); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0")); + Assert.Empty(connectionDuration.GetMeasurementSnapshot()); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0")); // Signal that connection can continue. sync.Continue(); @@ -272,13 +273,13 @@ public async Task Http1Connection_Error() await connection.WaitForConnectionClose(); } - Assert.Collection(connectionDuration.GetMeasurements(), m => + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => { AssertDuration(m, "127.0.0.1:0", httpProtocol: null); - Assert.Equal("System.InvalidOperationException", (string)m.Tags.ToArray().Single(t => t.Key == "exception-name").Value); + Assert.Equal("System.InvalidOperationException", (string)m.Tags["exception-name"]); }); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); - Assert.Collection(queuedConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(queuedConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); } [Fact] @@ -287,9 +288,9 @@ public async Task Http1Connection_Upgrade() var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); var testMeterFactory = new TestMeterFactory(); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); - using var currentUpgradedRequests = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-upgraded-connections"); + using var connectionDuration = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); + using var currentUpgradedRequests = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-upgraded-connections"); var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory)); @@ -305,9 +306,9 @@ await connection.ReceiveEnd("HTTP/1.1 101 Switching Protocols", ""); } - Assert.Collection(connectionDuration.GetMeasurements(), m => AssertDuration(m, "127.0.0.1:0", "HTTP/1.1")); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); - Assert.Collection(currentUpgradedRequests.GetMeasurements(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => AssertDuration(m, "127.0.0.1:0", "HTTP/1.1")); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(currentUpgradedRequests.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); static async Task UpgradeApp(HttpContext context) { @@ -331,12 +332,12 @@ public async Task Http2Connection() var requestsReceived = 0; var testMeterFactory = new TestMeterFactory(); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); - using var queuedConnections = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); - using var queuedRequests = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-requests"); - using var tlsHandshakeDuration = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-tls-handshake-duration"); - using var currentTlsHandshakes = new InstrumentRecorder(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-tls-handshakes"); + using var connectionDuration = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections"); + using var queuedConnections = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections"); + using var queuedRequests = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-requests"); + using var tlsHandshakeDuration = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-tls-handshake-duration"); + using var currentTlsHandshakes = new MetricCollector(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-tls-handshakes"); await using (var server = new TestServer(context => { @@ -393,27 +394,27 @@ public async Task Http2Connection() Assert.NotNull(connectionId); Assert.Equal(2, requestsReceived); - Assert.Collection(connectionDuration.GetMeasurements(), m => AssertDuration(m, "127.0.0.1:0", "HTTP/2")); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); - Assert.Collection(queuedConnections.GetMeasurements(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => AssertDuration(m, "127.0.0.1:0", "HTTP/2")); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); + Assert.Collection(queuedConnections.GetMeasurementSnapshot(), m => AssertCount(m, 1, "127.0.0.1:0"), m => AssertCount(m, -1, "127.0.0.1:0")); - Assert.Collection(queuedRequests.GetMeasurements(), + Assert.Collection(queuedRequests.GetMeasurementSnapshot(), m => AssertRequestCount(m, 1, "HTTP/2"), m => AssertRequestCount(m, -1, "HTTP/2"), m => AssertRequestCount(m, 1, "HTTP/2"), m => AssertRequestCount(m, -1, "HTTP/2")); - Assert.Collection(tlsHandshakeDuration.GetMeasurements(), m => + Assert.Collection(tlsHandshakeDuration.GetMeasurementSnapshot(), m => { Assert.True(m.Value > 0); - Assert.Equal("Tls12", (string)m.Tags.ToArray().Single(t => t.Key == "protocol").Value); + Assert.Equal("Tls12", (string)m.Tags["protocol"]); }); - Assert.Collection(currentTlsHandshakes.GetMeasurements(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); + Assert.Collection(currentTlsHandshakes.GetMeasurementSnapshot(), m => Assert.Equal(1, m.Value), m => Assert.Equal(-1, m.Value)); - static void AssertRequestCount(Measurement measurement, long expectedValue, string httpVersion) + static void AssertRequestCount(CollectedMeasurement measurement, long expectedValue, string httpVersion) { Assert.Equal(expectedValue, measurement.Value); - Assert.Equal(httpVersion, (string)measurement.Tags.ToArray().Single(t => t.Key == "version").Value); + Assert.Equal(httpVersion, (string)measurement.Tags["version"]); } } @@ -430,23 +431,23 @@ private static async Task EchoApp(HttpContext httpContext) } } - private static void AssertDuration(Measurement measurement, string localEndpoint, string httpProtocol) + private static void AssertDuration(CollectedMeasurement measurement, string localEndpoint, string httpProtocol) { Assert.True(measurement.Value > 0); - Assert.Equal(localEndpoint, (string)measurement.Tags.ToArray().Single(t => t.Key == "endpoint").Value); + Assert.Equal(localEndpoint, (string)measurement.Tags["endpoint"]); if (httpProtocol is not null) { - Assert.Equal(httpProtocol, (string)measurement.Tags.ToArray().Single(t => t.Key == "http-protocol").Value); + Assert.Equal(httpProtocol, (string)measurement.Tags["http-protocol"]); } else { - Assert.DoesNotContain(measurement.Tags.ToArray(), t => t.Key == "http-protocol"); + Assert.False(measurement.Tags.ContainsKey("http-protocol")); } } - private static void AssertCount(Measurement measurement, long expectedValue, string localEndpoint) + private static void AssertCount(CollectedMeasurement measurement, long expectedValue, string localEndpoint) { Assert.Equal(expectedValue, measurement.Value); - Assert.Equal(localEndpoint, (string)measurement.Tags.ToArray().Single(t => t.Key == "endpoint").Value); + Assert.Equal(localEndpoint, (string)measurement.Tags["endpoint"]); } } diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http2/Http2RequestTests.cs b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http2/Http2RequestTests.cs index 74fefad7a6ca..a2b9564bec8d 100644 --- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http2/Http2RequestTests.cs +++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http2/Http2RequestTests.cs @@ -16,6 +16,7 @@ using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Telemetry.Testing.Metering; namespace Interop.FunctionalTests.Http2; @@ -37,13 +38,7 @@ public async Task GET_Metrics_HttpProtocolAndTlsSet() { var meterFactory = host.Services.GetRequiredService(); - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - using var connectionDuration = new InstrumentRecorder(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var measurementReporter = new MeasurementReporter(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - measurementReporter.Register(m => - { - tcs.SetResult(); - }); + using var connectionDuration = new MetricCollector(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); await host.StartAsync(); var client = HttpHelpers.CreateClient(); @@ -61,16 +56,16 @@ public async Task GET_Metrics_HttpProtocolAndTlsSet() // Dispose the client to end the connection. client.Dispose(); // Wait for measurement to be available. - await tcs.Task.DefaultTimeout(); + await connectionDuration.WaitForMeasurementsAsync(numMeasurements: 1).DefaultTimeout(); // Assert - Assert.Collection(connectionDuration.GetMeasurements(), + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => { Assert.True(m.Value > 0); - Assert.Equal(protocol.ToString(), m.Tags.ToArray().Single(t => t.Key == "tls-protocol").Value); - Assert.Equal("HTTP/2", m.Tags.ToArray().Single(t => t.Key == "http-protocol").Value); - Assert.Equal($"127.0.0.1:{host.GetPort()}", m.Tags.ToArray().Single(t => t.Key == "endpoint").Value); + Assert.Equal(protocol.ToString(), m.Tags["tls-protocol"]); + Assert.Equal("HTTP/2", m.Tags["http-protocol"]); + Assert.Equal($"127.0.0.1:{host.GetPort()}", m.Tags["endpoint"]); }); await host.StopAsync(); diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs index 327e0a1cc19c..a81496c5dd43 100644 --- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs +++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs @@ -24,6 +24,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Primitives; +using Microsoft.Extensions.Telemetry.Testing.Metering; using Xunit; namespace Interop.FunctionalTests.Http3; @@ -81,13 +82,7 @@ public async Task GET_Metrics_HttpProtocolAndTlsSet() { var meterFactory = host.Services.GetRequiredService(); - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - using var connectionDuration = new InstrumentRecorder(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - using var measurementReporter = new MeasurementReporter(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); - measurementReporter.Register(m => - { - tcs.SetResult(); - }); + using var connectionDuration = new MetricCollector(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration"); await host.StartAsync(); var client = HttpHelpers.CreateClient(); @@ -103,16 +98,16 @@ public async Task GET_Metrics_HttpProtocolAndTlsSet() // Dispose the client to end the connection. client.Dispose(); // Wait for measurement to be available. - await tcs.Task.DefaultTimeout(); + await connectionDuration.WaitForMeasurementsAsync(numMeasurements: 1).DefaultTimeout(); // Assert - Assert.Collection(connectionDuration.GetMeasurements(), + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => { Assert.True(m.Value > 0); - Assert.Equal("Tls13", m.Tags.ToArray().Single(t => t.Key == "tls-protocol").Value); - Assert.Equal("HTTP/3", m.Tags.ToArray().Single(t => t.Key == "http-protocol").Value); - Assert.Equal($"127.0.0.1:{host.GetPort()}", m.Tags.ToArray().Single(t => t.Key == "endpoint").Value); + Assert.Equal("Tls13", m.Tags["tls-protocol"]); + Assert.Equal("HTTP/3", m.Tags["http-protocol"]); + Assert.Equal($"127.0.0.1:{host.GetPort()}", m.Tags["endpoint"]); }); await host.StopAsync(); diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj b/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj index 5b092bee3d01..a6455ef0564f 100644 --- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj @@ -32,6 +32,7 @@ + diff --git a/src/Shared/Metrics/TestMeterFactory.cs b/src/Shared/Metrics/TestMeterFactory.cs index 9553f06dce04..20d92510f36a 100644 --- a/src/Shared/Metrics/TestMeterFactory.cs +++ b/src/Shared/Metrics/TestMeterFactory.cs @@ -27,47 +27,3 @@ public void Dispose() Meters.Clear(); } } - -internal sealed class MeasurementReporter : IDisposable where T : struct -{ - private readonly string _meterName; - private readonly string _instrumentName; - private readonly List>> _callbacks; - private readonly MeterListener _meterListener; - - public MeasurementReporter(IMeterFactory factory, string meterName, string instrumentName, object state = null) - { - _meterName = meterName; - _instrumentName = instrumentName; - _callbacks = new List>>(); - _meterListener = new MeterListener(); - _meterListener.InstrumentPublished = (instrument, listener) => - { - if (instrument.Meter.Name == _meterName && instrument.Meter.Scope == factory && instrument.Name == _instrumentName) - { - listener.EnableMeasurementEvents(instrument, state); - } - }; - _meterListener.SetMeasurementEventCallback(OnMeasurementRecorded); - _meterListener.Start(); - } - - private void OnMeasurementRecorded(Instrument instrument, T measurement, ReadOnlySpan> tags, object state) - { - var m = new Measurement(measurement, tags); - foreach (var callback in _callbacks) - { - callback(m); - } - } - - public void Register(Action> callback) - { - _callbacks.Add(callback); - } - - public void Dispose() - { - _meterListener.Dispose(); - } -} diff --git a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs index b810476a85fe..e295f7181f21 100644 --- a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs +++ b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs @@ -38,6 +38,7 @@ using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; +using Microsoft.Extensions.Telemetry.Testing.Metering; using Microsoft.IdentityModel.Tokens; using Moq; using Newtonsoft.Json; @@ -1090,8 +1091,8 @@ public async Task Metrics() using (StartVerifiableLog()) { var testMeterFactory = new TestMeterFactory(); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections"); + using var connectionDuration = new MetricCollector(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections"); var metrics = new HttpConnectionsMetrics(testMeterFactory); var manager = CreateConnectionManager(LoggerFactory, metrics); @@ -1118,8 +1119,8 @@ public async Task Metrics() var exists = manager.TryGetConnection(connection.ConnectionId, out _); Assert.False(exists); - Assert.Collection(connectionDuration.GetMeasurements(), m => AssertDuration(m, HttpConnectionStopStatus.NormalClosure, HttpTransportType.LongPolling)); - Assert.Collection(currentConnections.GetMeasurements(), m => AssertTransport(m, 1, HttpTransportType.LongPolling), m => AssertTransport(m, -1, HttpTransportType.LongPolling)); + Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m => AssertDuration(m, HttpConnectionStopStatus.NormalClosure, HttpTransportType.LongPolling)); + Assert.Collection(currentConnections.GetMeasurementSnapshot(), m => AssertTransport(m, 1, HttpTransportType.LongPolling), m => AssertTransport(m, -1, HttpTransportType.LongPolling)); } } @@ -1146,8 +1147,8 @@ public async Task Metrics_ListenStartAfterConnection_Empty() await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app); Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode); - using var connectionDuration = new InstrumentRecorder(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration"); - using var currentConnections = new InstrumentRecorder(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections"); + using var connectionDuration = new MetricCollector(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration"); + using var currentConnections = new MetricCollector(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections"); await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app); @@ -1157,22 +1158,22 @@ public async Task Metrics_ListenStartAfterConnection_Empty() var exists = manager.TryGetConnection(connection.ConnectionId, out _); Assert.False(exists); - Assert.Empty(currentConnections.GetMeasurements()); - Assert.Empty(connectionDuration.GetMeasurements()); + Assert.Empty(currentConnections.GetMeasurementSnapshot()); + Assert.Empty(connectionDuration.GetMeasurementSnapshot()); } } - private static void AssertTransport(Measurement measurement, long expected, HttpTransportType transportType) + private static void AssertTransport(CollectedMeasurement measurement, long expected, HttpTransportType transportType) { Assert.Equal(expected, measurement.Value); - Assert.Equal(transportType.ToString(), (string)measurement.Tags.ToArray().Single(t => t.Key == "transport").Value); + Assert.Equal(transportType.ToString(), (string)measurement.Tags["transport"]); } - private static void AssertDuration(Measurement measurement, HttpConnectionStopStatus status, HttpTransportType transportType) + private static void AssertDuration(CollectedMeasurement measurement, HttpConnectionStopStatus status, HttpTransportType transportType) { Assert.True(measurement.Value > 0); - Assert.Equal(status.ToString(), (string)measurement.Tags.ToArray().Single(t => t.Key == "status").Value); - Assert.Equal(transportType.ToString(), (string)measurement.Tags.ToArray().Single(t => t.Key == "transport").Value); + Assert.Equal(status.ToString(), (string)measurement.Tags["status"]); + Assert.Equal(transportType.ToString(), (string)measurement.Tags["transport"]); } [Fact] diff --git a/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj b/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj index 28671b344a4f..a9b50cd8e516 100644 --- a/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj +++ b/src/SignalR/common/Http.Connections/test/Microsoft.AspNetCore.Http.Connections.Tests.csproj @@ -24,6 +24,7 @@ +