Replace InstrumentRecorder with MetricCollector (#48931)

This commit is contained in:
James Newton-King 2023-06-22 11:19:39 +08:00 коммит произвёл GitHub
Родитель 0356ba0fc4
Коммит 03edc26ae2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
23 изменённых файлов: 245 добавлений и 290 удалений

Просмотреть файл

@ -62,6 +62,7 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.Extensions.Options.DataAnnotations" />
<LatestPackageReference Include="Microsoft.Extensions.Options" />
<LatestPackageReference Include="Microsoft.Extensions.Primitives" />
<LatestPackageReference Include="Microsoft.Extensions.Telemetry.Testing" />
<LatestPackageReference Include="Microsoft.Win32.Registry" />
<LatestPackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" />
<LatestPackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" />

Просмотреть файл

@ -385,6 +385,10 @@
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>e2334b2be36919347923d0ec872a46acddb1e385</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Telemetry.Testing" Version="8.0.0-preview.6.23320.3">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>a0e9c8794e3e0ba27033a9f54a545385228d0876</Sha>
</Dependency>
<Dependency Name="NuGet.Frameworks" Version="6.2.4">
<Uri>https://github.com/nuget/nuget.client</Uri>
<Sha>8fef55f5a55a3b4f2c96cd1a9b5ddc51d4b927f8</Sha>

Просмотреть файл

@ -134,6 +134,8 @@
<SystemDiagnosticsPerformanceCounterVersion>8.0.0-preview.6.23318.9</SystemDiagnosticsPerformanceCounterVersion>
<SystemIOHashingVersion>8.0.0-preview.6.23318.9</SystemIOHashingVersion>
<SystemRuntimeCachingVersion>8.0.0-preview.6.23318.9</SystemRuntimeCachingVersion>
<!-- Packages from dotnet/extensions -->
<MicrosoftExtensionsTelemetryTestingVersion>8.0.0-preview.6.23320.3</MicrosoftExtensionsTelemetryTestingVersion>
<!-- Packages from dotnet/efcore -->
<dotnetefVersion>8.0.0-preview.6.23319.5</dotnetefVersion>
<MicrosoftEntityFrameworkCoreInMemoryVersion>8.0.0-preview.6.23319.5</MicrosoftEntityFrameworkCoreInMemoryVersion>

Просмотреть файл

@ -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.AspNetCore.Testing;
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 class HostingApplicationDiagnosticsTests
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<long>(testMeterFactory1, HostingMetrics.MeterName, "http-server-current-requests");
using var currentRequestsRecorder2 = new InstrumentRecorder<long>(testMeterFactory2, HostingMetrics.MeterName, "http-server-current-requests");
using var requestDurationRecorder1 = new InstrumentRecorder<double>(testMeterFactory1, HostingMetrics.MeterName, "http-server-request-duration");
using var requestDurationRecorder2 = new InstrumentRecorder<double>(testMeterFactory2, HostingMetrics.MeterName, "http-server-request-duration");
using var currentRequestsRecorder1 = new MetricCollector<long>(testMeterFactory1, HostingMetrics.MeterName, "http-server-current-requests");
using var currentRequestsRecorder2 = new MetricCollector<long>(testMeterFactory2, HostingMetrics.MeterName, "http-server-current-requests");
using var requestDurationRecorder1 = new MetricCollector<double>(testMeterFactory1, HostingMetrics.MeterName, "http-server-request-duration");
using var requestDurationRecorder2 = new MetricCollector<double>(testMeterFactory2, HostingMetrics.MeterName, "http-server-request-duration");
// Act/Assert 1
var context1 = hostingApplication1.CreateContext(features1);
@ -74,15 +74,15 @@ public class HostingApplicationDiagnosticsTests
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 class HostingApplicationDiagnosticsTests
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));
}

Просмотреть файл

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Testing;
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 class HostingMetricsTests
var httpContext = new DefaultHttpContext();
var meter = meterFactory.Meters.Single();
using var requestDurationRecorder = new InstrumentRecorder<double>(meterFactory, HostingMetrics.MeterName, "http-server-request-duration");
using var currentRequestsRecorder = new InstrumentRecorder<long>(meterFactory, HostingMetrics.MeterName, "http-server-current-requests");
using var unhandledRequestsRecorder = new InstrumentRecorder<long>(meterFactory, HostingMetrics.MeterName, "http-server-unhandled-requests");
using var requestDurationCollector = new MetricCollector<double>(meterFactory, HostingMetrics.MeterName, "http-server-request-duration");
using var currentRequestsCollector = new MetricCollector<long>(meterFactory, HostingMetrics.MeterName, "http-server-current-requests");
using var unhandledRequestsCollector = new MetricCollector<long>(meterFactory, HostingMetrics.MeterName, "http-server-unhandled-requests");
// Act/Assert
Assert.Equal(HostingMetrics.MeterName, meter.Name);
@ -39,10 +40,10 @@ public class HostingMetricsTests
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 class HostingMetricsTests
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 class HostingMetricsTests
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<double> measurement, string protocol, int statusCode, string exceptionName = null)
static void AssertRequestDuration(CollectedMeasurement<double> 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 class HostingMetricsTests
await syncPoint.WaitForSyncPoint().DefaultTimeout();
using var requestDurationRecorder = new InstrumentRecorder<double>(meterFactory, HostingMetrics.MeterName, "http-server-request-duration");
using var currentRequestsRecorder = new InstrumentRecorder<long>(meterFactory, HostingMetrics.MeterName, "http-server-current-requests");
using var requestDurationCollector = new MetricCollector<double>(meterFactory, HostingMetrics.MeterName, "http-server-request-duration");
using var currentRequestsCollector = new MetricCollector<long>(meterFactory, HostingMetrics.MeterName, "http-server-current-requests");
context1.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
syncPoint.Continue();
@ -141,8 +142,8 @@ public class HostingMetricsTests
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 class HostingMetricsTests
var httpContext = new DefaultHttpContext();
var meter = meterFactory.Meters.Single();
using var requestDurationRecorder = new InstrumentRecorder<double>(meterFactory, HostingMetrics.MeterName, "http-server-request-duration");
using var currentRequestsRecorder = new InstrumentRecorder<long>(meterFactory, HostingMetrics.MeterName, "http-server-current-requests");
using var requestDurationCollector = new MetricCollector<double>(meterFactory, HostingMetrics.MeterName, "http-server-request-duration");
using var currentRequestsCollector = new MetricCollector<long>(meterFactory, HostingMetrics.MeterName, "http-server-current-requests");
// Act/Assert
Assert.Equal(HostingMetrics.MeterName, meter.Name);

Просмотреть файл

@ -22,6 +22,7 @@
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.Extensions.Options" />
<Reference Include="Microsoft.Extensions.Telemetry.Testing" />
<Reference Include="System.Threading.Channels" />
</ItemGroup>

Просмотреть файл

@ -15,6 +15,7 @@
<Reference Include="Microsoft.Extensions.Diagnostics" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Telemetry.Testing" />
<Reference Include="Microsoft.Extensions.WebEncoders" />
<Compile Include="$(SharedSourceRoot)Metrics\TestMeterFactory.cs" LinkBase="shared" />

Просмотреть файл

@ -13,6 +13,7 @@ using Microsoft.Extensions.Diagnostics.Metrics;
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 class RoutingMetricsTests
var httpContext = new DefaultHttpContext();
var meter = meterFactory.Meters.Single();
using var routingMatchSuccessRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
using var routingMatchSuccessCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
// Act
await middleware.Invoke(httpContext);
@ -44,9 +45,9 @@ public class RoutingMetricsTests
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 class RoutingMetricsTests
var httpContext = new DefaultHttpContext();
var meter = meterFactory.Meters.Single();
using var routingMatchSuccessRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
using var routingMatchSuccessCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
// Act
await middleware.Invoke(httpContext);
@ -80,9 +81,9 @@ public class RoutingMetricsTests
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 class RoutingMetricsTests
var httpContext = new DefaultHttpContext();
var meter = meterFactory.Meters.Single();
using var routingMatchSuccessRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
using var routingMatchSuccessCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
// Act
await middleware.Invoke(httpContext);
@ -109,9 +110,9 @@ public class RoutingMetricsTests
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 class RoutingMetricsTests
var httpContext = new DefaultHttpContext();
var meter = meterFactory.Meters.Single();
using var routingMatchSuccessRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureRecorder = new InstrumentRecorder<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
using var routingMatchSuccessCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-success");
using var routingMatchFailureCollector = new MetricCollector<long>(meterFactory, RoutingMetrics.MeterName, "routing-match-failure");
// Act
await middleware.Invoke(httpContext);
@ -135,16 +136,16 @@ public class RoutingMetricsTests
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<long> measurement, string route, bool fallback)
private void AssertSuccess(CollectedMeasurement<long> 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(

Просмотреть файл

@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Testing;
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 class DeveloperExceptionPageMiddlewareTest : LoggedTest
public async Task UnhandledError_ExceptionNameTagAdded()
{
// Arrange
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var meterFactory = new TestMeterFactory();
using var requestDurationRecorder = new InstrumentRecorder<double>(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration");
using var requestExceptionRecorder = new InstrumentRecorder<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
using var measurementReporter = new MeasurementReporter<double>(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration");
measurementReporter.Register(m =>
{
tcs.SetResult();
});
using var requestDurationCollector = new MetricCollector<double>(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration");
using var requestExceptionCollector = new MetricCollector<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
using var host = new HostBuilder()
.ConfigureServices(s =>
@ -577,33 +571,33 @@ public class DeveloperExceptionPageMiddlewareTest : LoggedTest
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<long> measurement, string exceptionName, string result, string handler = null)
private static void AssertRequestException(CollectedMeasurement<long> 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"]);
}
}

Просмотреть файл

@ -19,6 +19,7 @@ using Microsoft.Extensions.Diagnostics.Metrics;
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 class ExceptionHandlerMiddlewareTest
var middleware = CreateMiddleware(_ => Task.CompletedTask, optionsAccessor, exceptionHandlers, meterFactory);
var meter = meterFactory.Meters.Single();
using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
using var diagnosticsRequestExceptionCollector = new MetricCollector<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
// Act
await middleware.Invoke(httpContext);
@ -219,7 +220,7 @@ public class ExceptionHandlerMiddlewareTest
Assert.Equal(DiagnosticsMetrics.MeterName, meter.Name);
Assert.Null(meter.Version);
Assert.Empty(diagnosticsRequestExceptionRecorder.GetMeasurements());
Assert.Empty(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot());
}
[Fact]
@ -233,13 +234,13 @@ public class ExceptionHandlerMiddlewareTest
var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, exceptionHandlers, meterFactory);
var meter = meterFactory.Meters.Single();
using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
using var diagnosticsRequestExceptionCollector = new MetricCollector<long>(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 class ExceptionHandlerMiddlewareTest
var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, exceptionHandlers, meterFactory);
var meter = meterFactory.Meters.Single();
using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
using var diagnosticsRequestExceptionCollector = new MetricCollector<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
// Act
await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext));
// Assert
Assert.Collection(diagnosticsRequestExceptionRecorder.GetMeasurements(),
Assert.Collection(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot(),
m => AssertRequestException(m, "System.InvalidOperationException", "Skipped"));
}
@ -280,13 +281,13 @@ public class ExceptionHandlerMiddlewareTest
var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, meterFactory: meterFactory);
var meter = meterFactory.Meters.Single();
using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
using var diagnosticsRequestExceptionCollector = new MetricCollector<long>(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 class ExceptionHandlerMiddlewareTest
var middleware = CreateMiddleware(_ => throw new InvalidOperationException(), optionsAccessor, meterFactory: meterFactory);
var meter = meterFactory.Meters.Single();
using var diagnosticsRequestExceptionRecorder = new InstrumentRecorder<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
using var diagnosticsRequestExceptionCollector = new MetricCollector<long>(meterFactory, DiagnosticsMetrics.MeterName, "diagnostics-handler-exception");
// Act
await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext));
// Assert
Assert.Collection(diagnosticsRequestExceptionRecorder.GetMeasurements(),
Assert.Collection(diagnosticsRequestExceptionCollector.GetMeasurementSnapshot(),
m => AssertRequestException(m, "System.InvalidOperationException", "Unhandled"));
}
private static void AssertRequestException(Measurement<long> measurement, string exceptionName, string result, string handler = null)
private static void AssertRequestException(CollectedMeasurement<long> 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"]);
}
}

Просмотреть файл

@ -14,6 +14,7 @@ using Microsoft.Extensions.Diagnostics.Metrics;
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 class ExceptionHandlerTest
public async Task UnhandledError_ExceptionNameTagAdded()
{
// Arrange
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var meterFactory = new TestMeterFactory();
using var instrumentRecorder = new InstrumentRecorder<double>(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration");
using var measurementReporter = new MeasurementReporter<double>(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration");
measurementReporter.Register(m =>
{
tcs.SetResult();
});
using var instrumentCollector = new MetricCollector<double>(meterFactory, "Microsoft.AspNetCore.Hosting", "http-server-request-duration");
using var host = new HostBuilder()
.ConfigureServices(s =>
@ -959,11 +953,11 @@ public class ExceptionHandlerTest
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);

Просмотреть файл

@ -21,6 +21,7 @@
<Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Extensions.DiagnosticAdapter" />
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
<Reference Include="Microsoft.Extensions.Telemetry.Testing" />
<Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" />
</ItemGroup>

Просмотреть файл

@ -8,6 +8,7 @@
<Reference Include="Microsoft.AspNetCore.Http" />
<Reference Include="Microsoft.AspNetCore.RateLimiting" />
<Reference Include="Microsoft.AspNetCore.Routing" />
<Reference Include="Microsoft.Extensions.Telemetry.Testing" />
<Compile Include="$(SharedSourceRoot)Metrics\TestMeterFactory.cs" LinkBase="shared" />
<Compile Include="$(SharedSourceRoot)SyncPoint\SyncPoint.cs" LinkBase="shared" />

Просмотреть файл

@ -14,6 +14,7 @@ using Microsoft.Extensions.Diagnostics.Metrics;
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 class RateLimitingMetricsTests
var context = new DefaultHttpContext();
using var leaseRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
using var leaseRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
// Act
await middleware.Invoke(context).DefaultTimeout();
@ -46,11 +47,11 @@ public class RateLimitingMetricsTests
// 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 class RateLimitingMetricsTests
var context = new DefaultHttpContext();
context.Request.Method = "GET";
using var leaseRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
using var leaseRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsCollector = new MetricCollector<long>(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 class RateLimitingMetricsTests
// 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 class RateLimitingMetricsTests
await syncPoint.WaitForSyncPoint().DefaultTimeout();
using var leaseRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
using var leaseRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
syncPoint.Continue();
@ -154,8 +155,8 @@ public class RateLimitingMetricsTests
// 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 class RateLimitingMetricsTests
routeEndpointBuilder.Metadata.Add(new EnableRateLimitingAttribute("concurrencyPolicy"));
var endpoint = routeEndpointBuilder.Build();
using var leaseRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
using var leaseRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
// Act
var context1 = new DefaultHttpContext();
@ -213,9 +214,9 @@ public class RateLimitingMetricsTests
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 class RateLimitingMetricsTests
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 class RateLimitingMetricsTests
// Start listening while the second request is queued.
using var leaseRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationRecorder = new InstrumentRecorder<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsRecorder = new InstrumentRecorder<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-lease-failed-requests");
using var leaseRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-leased-request-duration");
using var currentLeaseRequestsCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-leased-requests");
using var currentRequestsQueuedCollector = new MetricCollector<long>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-current-queued-requests");
using var queuedRequestDurationCollector = new MetricCollector<double>(meterFactory, RateLimitingMetrics.MeterName, "rate-limiting-queued-request-duration");
using var leaseFailedRequestsCollector = new MetricCollector<long>(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 class RateLimitingMetricsTests
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<long> measurement, long value, string policy)
private static void AssertCounter(CollectedMeasurement<long> measurement, long value, string policy)
{
Assert.Equal(value, measurement.Value);
AssertTag(measurement.Tags, "policy", policy);
}
private static void AssertDuration(Measurement<double> measurement, string policy)
private static void AssertDuration(CollectedMeasurement<double> measurement, string policy)
{
Assert.True(measurement.Value > 0);
AssertTag(measurement.Tags, "policy", policy);
}
private static void AssertTag<T>(ReadOnlySpan<KeyValuePair<string, object>> tags, string tagName, T expected)
private static void AssertTag<T>(IReadOnlyDictionary<string, object> tags, string tagName, T expected)
{
if (expected == null)
{

Просмотреть файл

@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport
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 class ConnectionLimitTests : LoggedTest
public async Task RejectsConnectionsWhenLimitReached()
{
var testMeterFactory = new TestMeterFactory();
using var rejectedConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-rejected-connections");
using var rejectedConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-rejected-connections");
const int max = 10;
var requestTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
@ -141,14 +142,14 @@ public class ConnectionLimitTests : LoggedTest
}
var actions = Enumerable.Repeat(AssertCounter, i + 1).ToArray();
Assert.Collection(rejectedConnections.GetMeasurements(), actions);
Assert.Collection(rejectedConnections.GetMeasurementSnapshot(), actions);
}
requestTcs.TrySetResult();
}
}
static void AssertCounter(Measurement<long> measurement) => Assert.Equal(1, measurement.Value);
static void AssertCounter(CollectedMeasurement<long> measurement) => Assert.Equal(1, measurement.Value);
}
[Fact]

Просмотреть файл

@ -36,6 +36,7 @@
<Reference Include="System.Threading.Channels" />
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.Extensions.Telemetry.Testing" />
<Reference Include="Newtonsoft.Json" />
</ItemGroup>

Просмотреть файл

@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
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 class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
});
var testMeterFactory = new TestMeterFactory();
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
@ -63,8 +64,8 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
// 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 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
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 class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
// Wait for connection to start on the server.
await sync.WaitForSyncPoint();
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
// Signal that connection can continue.
sync.Continue();
@ -138,9 +139,9 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
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 class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
});
var testMeterFactory = new TestMeterFactory();
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
@ -195,8 +196,8 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
Assert.NotEqual(overridenFeature, currentConnectionContext.Features.Get<IConnectionMetricsTagsFeature>());
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 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
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 class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
});
var testMeterFactory = new TestMeterFactory();
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
@ -261,8 +262,8 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
// 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 class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
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 class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0));
var testMeterFactory = new TestMeterFactory();
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var currentUpgradedRequests = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-upgraded-connections");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var currentUpgradedRequests = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-upgraded-connections");
var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
@ -305,9 +306,9 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
"");
}
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 class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
var requestsReceived = 0;
var testMeterFactory = new TestMeterFactory();
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
using var queuedRequests = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-requests");
using var tlsHandshakeDuration = new InstrumentRecorder<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-tls-handshake-duration");
using var currentTlsHandshakes = new InstrumentRecorder<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-tls-handshakes");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-connections");
using var queuedConnections = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-connections");
using var queuedRequests = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-queued-requests");
using var tlsHandshakeDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-tls-handshake-duration");
using var currentTlsHandshakes = new MetricCollector<long>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-current-tls-handshakes");
await using (var server = new TestServer(context =>
{
@ -393,27 +394,27 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
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<long> measurement, long expectedValue, string httpVersion)
static void AssertRequestCount(CollectedMeasurement<long> 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 @@ public class KestrelMetricsTests : TestApplicationErrorLoggerLoggedTest
}
}
private static void AssertDuration(Measurement<double> measurement, string localEndpoint, string httpProtocol)
private static void AssertDuration(CollectedMeasurement<double> 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<long> measurement, long expectedValue, string localEndpoint)
private static void AssertCount(CollectedMeasurement<long> 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"]);
}
}

Просмотреть файл

@ -16,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection;
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 class Http2RequestTests : LoggedTest
{
var meterFactory = host.Services.GetRequiredService<IMeterFactory>();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
using var connectionDuration = new InstrumentRecorder<double>(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var measurementReporter = new MeasurementReporter<double>(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
measurementReporter.Register(m =>
{
tcs.SetResult();
});
using var connectionDuration = new MetricCollector<double>(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
await host.StartAsync();
var client = HttpHelpers.CreateClient();
@ -61,16 +56,16 @@ public class Http2RequestTests : LoggedTest
// 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();

Просмотреть файл

@ -24,6 +24,7 @@ using Microsoft.Extensions.Hosting;
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 class Http3RequestTests : LoggedTest
{
var meterFactory = host.Services.GetRequiredService<IMeterFactory>();
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
using var connectionDuration = new InstrumentRecorder<double>(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
using var measurementReporter = new MeasurementReporter<double>(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
measurementReporter.Register(m =>
{
tcs.SetResult();
});
using var connectionDuration = new MetricCollector<double>(meterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel-connection-duration");
await host.StartAsync();
var client = HttpHelpers.CreateClient();
@ -103,16 +98,16 @@ public class Http3RequestTests : LoggedTest
// 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();

Просмотреть файл

@ -32,6 +32,7 @@
<Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Quic" />
<Reference Include="Microsoft.AspNetCore.WebSockets" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.Extensions.Telemetry.Testing" />
<Reference Include="Microsoft.Internal.AspNetCore.H2Spec.All" />
</ItemGroup>

Просмотреть файл

@ -27,47 +27,3 @@ internal sealed class TestMeterFactory : IMeterFactory
Meters.Clear();
}
}
internal sealed class MeasurementReporter<T> : IDisposable where T : struct
{
private readonly string _meterName;
private readonly string _instrumentName;
private readonly List<Action<Measurement<T>>> _callbacks;
private readonly MeterListener _meterListener;
public MeasurementReporter(IMeterFactory factory, string meterName, string instrumentName, object state = null)
{
_meterName = meterName;
_instrumentName = instrumentName;
_callbacks = new List<Action<Measurement<T>>>();
_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<T>(OnMeasurementRecorded);
_meterListener.Start();
}
private void OnMeasurementRecorded(Instrument instrument, T measurement, ReadOnlySpan<KeyValuePair<string, object>> tags, object state)
{
var m = new Measurement<T>(measurement, tags);
foreach (var callback in _callbacks)
{
callback(m);
}
}
public void Register(Action<Measurement<T>> callback)
{
_callbacks.Add(callback);
}
public void Dispose()
{
_meterListener.Dispose();
}
}

Просмотреть файл

@ -39,6 +39,7 @@ using Microsoft.Extensions.Logging;
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;
@ -1091,8 +1092,8 @@ public class HttpConnectionDispatcherTests : VerifiableLoggedTest
using (StartVerifiableLog())
{
var testMeterFactory = new TestMeterFactory();
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections");
var metrics = new HttpConnectionsMetrics(testMeterFactory);
var manager = CreateConnectionManager(LoggerFactory, metrics);
@ -1119,8 +1120,8 @@ public class HttpConnectionDispatcherTests : VerifiableLoggedTest
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));
}
}
@ -1147,8 +1148,8 @@ public class HttpConnectionDispatcherTests : VerifiableLoggedTest
await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app);
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
using var connectionDuration = new InstrumentRecorder<double>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration");
using var currentConnections = new InstrumentRecorder<long>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections");
using var connectionDuration = new MetricCollector<double>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-connection-duration");
using var currentConnections = new MetricCollector<long>(testMeterFactory, HttpConnectionsMetrics.MeterName, "signalr-http-transport-current-connections");
await dispatcher.ExecuteAsync(context, new HttpConnectionDispatcherOptions(), app);
@ -1158,22 +1159,22 @@ public class HttpConnectionDispatcherTests : VerifiableLoggedTest
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<long> measurement, long expected, HttpTransportType transportType)
private static void AssertTransport(CollectedMeasurement<long> 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<double> measurement, HttpConnectionStopStatus status, HttpTransportType transportType)
private static void AssertDuration(CollectedMeasurement<double> 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]

Просмотреть файл

@ -24,6 +24,7 @@
<Reference Include="Microsoft.AspNetCore.Http.Connections" />
<Reference Include="Microsoft.AspNetCore.Http.Connections.Client" />
<Reference Include="Microsoft.AspNetCore.Http" />
<Reference Include="Microsoft.Extensions.Telemetry.Testing" />
<Reference Include="Newtonsoft.Json" />
</ItemGroup>