From 0ff0ebe2f427a3233df80e016b72905ac9c6840d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Mon, 9 Oct 2023 11:01:44 -0700 Subject: [PATCH 01/28] Remove project names (#4532) * Remove project names * Revert changes made in other PR --- scripts/SlngenReferencing.ps1 | 5 ++--- .../FakeSslCertificateFactory.cs | 2 +- .../Http/TelemetryConstants.cs | 2 +- ...icrosoft.Extensions.Diagnostics.ExtraAbstractions.json | 2 +- .../Microsoft.Extensions.Telemetry.Abstractions.json | 2 +- .../Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs | 8 ++++---- .../FakeCertificateFactoryTests.cs | 2 +- .../Http/AbstractionTests.cs | 2 +- .../Linux/AcceptanceTest.cs | 2 +- .../ResourceMonitoringServiceTests.cs | 2 +- .../Logging/HttpClientLoggerTest.cs | 2 +- .../Microsoft.Extensions.Http.Resilience.Tests.csproj | 2 +- .../Microsoft.Extensions.Resilience.Tests.csproj | 2 +- 13 files changed, 17 insertions(+), 18 deletions(-) diff --git a/scripts/SlngenReferencing.ps1 b/scripts/SlngenReferencing.ps1 index 2f2296a44d6..1bee1e39959 100644 --- a/scripts/SlngenReferencing.ps1 +++ b/scripts/SlngenReferencing.ps1 @@ -2,10 +2,9 @@ <# .SYNOPSIS -Create solution file that contains all packages referencing mentioned keywords. +Creates a solution file that contains all the packages referencing specific keywords. .DESCRIPTION -This script will help you when changing public API of the package and would like to adjust all internal R9 usages. -It will generate a solution file with all packages that references a project matching given keywords. +This script will generate a solution file with all the packages that reference a project matching given keywords. .EXAMPLE PS> .\SlngenReferencing.ps1 AsyncState #> diff --git a/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs b/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs index 4e2d838942c..2473fee5dc9 100644 --- a/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs +++ b/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs @@ -23,7 +23,7 @@ internal static class FakeSslCertificateFactory public static X509Certificate2 CreateSslCertificate() { var request = new CertificateRequest( - new X500DistinguishedName("CN=r9-self-signed-unit-test-certificate"), + new X500DistinguishedName("CN=dotnet-extensions-self-signed-unit-test-certificate"), _rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Http/TelemetryConstants.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Http/TelemetryConstants.cs index 58b6ea8a0be..5e600f29ed4 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Http/TelemetryConstants.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Http/TelemetryConstants.cs @@ -11,7 +11,7 @@ public static class TelemetryConstants /// /// Request metadata key that is used when storing request metadata object. /// - public const string RequestMetadataKey = "R9-RequestMetadata"; + public const string RequestMetadataKey = "Extensions-RequestMetadata"; /// /// Represents the placeholder text for an unknown request name or dependency name in telemetry. diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json index 3d7bd4beb33..44dd7b3edd0 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json @@ -893,7 +893,7 @@ { "Member": "const string Microsoft.Extensions.Http.Diagnostics.TelemetryConstants.RequestMetadataKey", "Stage": "Stable", - "Value": "R9-RequestMetadata" + "Value": "Extensions-RequestMetadata" }, { "Member": "const string Microsoft.Extensions.Http.Diagnostics.TelemetryConstants.ServerApplicationNameHeader", diff --git a/test/Generators/Microsoft.Gen.MetricsReports/GoldenReports/Microsoft.Extensions.Telemetry.Abstractions.json b/test/Generators/Microsoft.Gen.MetricsReports/GoldenReports/Microsoft.Extensions.Telemetry.Abstractions.json index e4ea7bcd360..046247fa87e 100644 --- a/test/Generators/Microsoft.Gen.MetricsReports/GoldenReports/Microsoft.Extensions.Telemetry.Abstractions.json +++ b/test/Generators/Microsoft.Gen.MetricsReports/GoldenReports/Microsoft.Extensions.Telemetry.Abstractions.json @@ -867,7 +867,7 @@ { "Member": "const string Microsoft.Extensions.Http.Diagnostics.TelemetryConstants.RequestMetadataKey", "Stage": "Stable", - "Value": "R9-RequestMetadata" + "Value": "Extensions-RequestMetadata" }, { "Member": "const string Microsoft.Extensions.Http.Diagnostics.TelemetryConstants.ServerApplicationNameHeader", diff --git a/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs b/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs index 98af82ebec4..d6df799b261 100644 --- a/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs +++ b/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs @@ -65,13 +65,13 @@ public class EmitterTests }, new ReportedMetricMethod { - MetricName = "R9\\Test\\MemoryUsage", + MetricName = "Test\\MemoryUsage", Summary = "MemoryUsage summary.", Kind = InstrumentKind.Gauge, Dimensions = new() { "Path"}, DimensionsDescriptions = new Dictionary { - { "Path", "R9\\Test\\Description\\Path" } + { "Path", "Test\\Description\\Path" } }, } } @@ -166,11 +166,11 @@ public void EmitterShouldOutputInJSONFormat() newLine + " \"InstrumentName\": \"Counter\"" + newLine + " }," + newLine + " {" + - newLine + " \"MetricName\": \"R9\\\\Test\\\\MemoryUsage\"," + + newLine + " \"MetricName\": \"Test\\\\MemoryUsage\"," + newLine + " \"MetricDescription\": \"MemoryUsage summary.\"," + newLine + " \"InstrumentName\": \"Gauge\"," + newLine + " \"Dimensions\": {" + - newLine + " \"Path\": \"R9\\\\Test\\\\Description\\\\Path\"" + + newLine + " \"Path\": \"Test\\\\Description\\\\Path\"" + newLine + " }" + newLine + " }" + newLine + " ]" + diff --git a/test/Libraries/Microsoft.AspNetCore.Testing.Tests/FakeCertificateFactoryTests.cs b/test/Libraries/Microsoft.AspNetCore.Testing.Tests/FakeCertificateFactoryTests.cs index 5feeb5459e6..1da6dc74ddc 100644 --- a/test/Libraries/Microsoft.AspNetCore.Testing.Tests/FakeCertificateFactoryTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Testing.Tests/FakeCertificateFactoryTests.cs @@ -16,7 +16,7 @@ public void Create_CreatesCertificate() { using var certificate = FakeSslCertificateFactory.CreateSslCertificate(); - Assert.Equal("CN=r9-self-signed-unit-test-certificate", certificate.SubjectName.Name); + Assert.Equal("CN=dotnet-extensions-self-signed-unit-test-certificate", certificate.SubjectName.Name); Assert.Equal("localhost", certificate.GetNameInfo(X509NameType.DnsFromAlternativeName, false)); Assert.True(DateTime.Now > certificate.NotBefore + TimeSpan.FromHours(1)); Assert.True(DateTime.Now < certificate.NotAfter - TimeSpan.FromHours(1)); diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Http/AbstractionTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Http/AbstractionTests.cs index d05259dd38c..7f383cf1c7c 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Http/AbstractionTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Http/AbstractionTests.cs @@ -35,7 +35,7 @@ public void RequestMetadata_ParameterizedConsrtuctor_HasProvidedValues() [Fact] public void Ensure_TelemetryConstantValuesAreNotChanged() { - Assert.Equal("R9-RequestMetadata", TelemetryConstants.RequestMetadataKey); + Assert.Equal("Extensions-RequestMetadata", TelemetryConstants.RequestMetadataKey); Assert.Equal("unknown", TelemetryConstants.Unknown); Assert.Equal("REDACTED", TelemetryConstants.Redacted); } diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs index d0acbad4074..ce49066f3f5 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/AcceptanceTest.cs @@ -130,7 +130,7 @@ public void Adding_Linux_Resource_Utilization_With_Section_Registers_SnapshotPro Assert.Equal(104_767_488UL, provider.Resources.MaximumMemoryInBytes); // meminfo * 1024 } - [ConditionalFact(Skip = "Flaky test, see https://github.com/dotnet/r9/issues/406")] + [ConditionalFact(Skip = "Flaky test, see https://github.com/dotnet/extensions/issues/3997")] [OSSkipCondition(OperatingSystems.Windows | OperatingSystems.MacOSX, SkipReason = "Linux specific package.")] public Task ResourceUtilizationTracker_Reports_The_Same_Values_As_One_Can_Observe_From_Gauges() { diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringServiceTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringServiceTests.cs index 6a1d768642d..fdfdf41da2d 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringServiceTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/ResourceMonitoringServiceTests.cs @@ -344,7 +344,7 @@ public async Task ResourceUtilizationTracker_InitializedProperly_InvokesPublishe Assert.True(publisherCalled); } - [Fact(Skip = "Broken test, see https://github.com/dotnet/r9/issues/404")] + [Fact(Skip = "Broken test, see https://github.com/dotnet/extensions/issues/4529")] public async Task ResourceUtilizationTracker_WhenInitializedWithZeroSnapshots_ReportsHighCpuSpikesThenConvergeInFewCycles() { // This test shows that initializing the internal buffer of the tracker with snapshots diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs index 0f932569999..0d589f46603 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs @@ -454,7 +454,7 @@ public async Task HttpLoggingHandler_AllOptionsSendAsyncFailed_LogsRequestInform Assert.DoesNotContain(logRecordState, kvp => kvp.Key.StartsWith(HttpClientLoggingTagNames.ResponseHeaderPrefix)); } - [Fact(Skip = "Flaky test, see https://github.com/dotnet/r9/issues/372")] + [Fact(Skip = "Flaky test, see https://github.com/dotnet/extensions/issues/4530")] public async Task HttpLoggingHandler_ReadResponseThrows_LogsException() { var requestContent = _fixture.Create(); diff --git a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Microsoft.Extensions.Http.Resilience.Tests.csproj b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Microsoft.Extensions.Http.Resilience.Tests.csproj index 5edac760ac0..583bee2681b 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Microsoft.Extensions.Http.Resilience.Tests.csproj +++ b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Microsoft.Extensions.Http.Resilience.Tests.csproj @@ -5,7 +5,7 @@ - + true diff --git a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Microsoft.Extensions.Resilience.Tests.csproj b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Microsoft.Extensions.Resilience.Tests.csproj index 0fdfe0445ba..67c25dd1e90 100644 --- a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Microsoft.Extensions.Resilience.Tests.csproj +++ b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Microsoft.Extensions.Resilience.Tests.csproj @@ -9,7 +9,7 @@ - + true From a959ae4190e1f90bf9b86aec4f99f863860005f0 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Mon, 9 Oct 2023 13:02:42 -0700 Subject: [PATCH 02/28] Layer enrichment & redaction on top of HttpLogging (#4330) * Layer enrichment & redaction on top of HttpLogging * Cleanup * Extract enrichment, cleanup * Remove duration * Remove duration * API updates * Experimental * Target net80, remove old code, fix up tests * PR feedback, tests * Test cleanup * CombineLogs, fix tests * Cleanup, reduce diff * Spacing * React to duration changes * Update SDK to 8.0.100-rc.2.23451.1 * Experimental * Use HttpContext * Config binding * Cleanup * Duplicate options? * Remove LogMethodHelper * Revert "Duplicate options?" This reverts commit 582a508a32ec1c815dab6b46e76d222845ce8658. # Conflicts: # src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingServiceCollectionExtensions.cs * Don't combine options * Consolidate enrichers * Cleanup metadata * Remove LogMethodHelper tests * More LogMethodHelper * Warnings * More LogMethodHelper * Fixup baseline * Test coverage * Update src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricherOptions.cs * Delete src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricherOptionsValidator.cs * Update RequestHeadersEnricherServiceCollectionExtensions.cs * Delete more * Delete more * Options, docs, binding * Cleanup headers log enricher * Less experimental * Warnings * Fix dimensions --- Directory.Build.targets | 2 +- docs/list-of-diagnostics.md | 1 + .../Log.cs | 13 + .../Logging/HeaderReader.cs | 16 +- .../Logging/HttpLogPropertiesProvider.cs | 72 --- ...HttpLoggingApplicationBuilderExtensions.cs | 27 - .../Logging/HttpLoggingMiddleware.cs | 459 ---------------- .../HttpLoggingRedactionInterceptor.cs | 203 ++++++++ .../HttpLoggingServiceCollectionExtensions.cs | 91 ++-- .../Logging/HttpLoggingTagNames.cs | 18 +- .../Logging/HttpRequestBodyReader.cs | 58 --- .../Logging/IHttpLogEnricher.cs | 11 +- .../Logging/IncomingRequestLogRecord.cs | 71 --- .../Logging/Log.cs | 162 ------ .../Logging/LoggingOptions.cs | 216 -------- .../Logging/LoggingRedactionOptions.cs | 105 ++++ ...cs => LoggingRedactionOptionsValidator.cs} | 5 +- .../Logging/MediaTypeSetExtensions.cs | 28 - .../Logging/PipeReaderExtensions.cs | 74 --- ...dersEnricherServiceCollectionExtensions.cs | 50 +- .../RequestHeadersLogEnricherOptions.cs | 5 +- .../Logging/ResponseInterceptingStream.cs | 224 -------- .../Logging/ResponseInterceptingStreamPool.cs | 47 -- ...oft.AspNetCore.Diagnostics.Middleware.json | 134 +---- .../Enrichment/ApplicationEnricherTags.cs | 8 +- .../Logging/LogMethodHelper.cs | 180 ------- .../Logging/LoggerMessageState.cs | 6 +- ...ensions.Diagnostics.ExtraAbstractions.json | 50 +- src/Shared/DiagnosticIds/Experiments.cs | 1 + .../Generated/LogPropertiesTests.cs | 2 +- .../Logging/AcceptanceTests.Mvc.cs | 15 +- .../Logging/AcceptanceTests.Routing.cs | 9 +- .../Logging/AcceptanceTests.cs | 493 ++++++------------ .../Logging/ApiRoutingController.cs | 6 +- .../Logging/AttributeRoutingController.cs | 3 + .../Logging/ConventionalRoutingController.cs | 3 + .../Logging/CustomHttpLogEnricher.cs | 5 +- .../Logging/HeaderReaderTests.cs | 15 +- .../HttpLoggingServiceExtensionsTests.cs | 48 +- .../Logging/HttpRequestBodyReaderTests.cs | 50 -- .../Logging/IncomingHttpDimensionsTests.cs | 3 + .../Logging/IncomingRequestLogRecordTests.cs | 19 - .../Logging/IncomingRequestStructTests.cs | 32 -- .../Logging/InfiniteStream.cs | 51 -- .../Logging/LoggingMiddlewareTests.cs | 51 -- .../Logging/LoggingOptionsValidationTests.cs | 57 +- .../Logging/MediaTypeSetExtensionsTests.cs | 42 -- .../Logging/MixedRoutingController.cs | 3 + .../Logging/PipeReaderExtensionsTests.cs | 61 --- .../Logging/RequestBodyErrorPipeFeature.cs | 27 - .../Logging/RequestBodyInfinitePipeFeature.cs | 45 -- .../RequestBodyMultiSegmentPipeFeature.cs | 59 --- .../RequestHeadersEnricherExtensionsTests.cs | 50 +- .../Logging/RequestHeadersEnricherTests.cs | 18 +- .../ResponseInterceptingStreamTests.cs | 204 -------- .../Logging/TestBodyPipeFeatureMiddleware.cs | 35 -- .../Logging/TestHttpLogEnricher.cs | 5 +- .../Logging/ExtendedLoggerTests.cs | 100 +--- .../Logging/SerialExtendedLoggerTests.cs | 7 +- .../Logging/LogMethodHelperTests.cs | 120 ----- .../Logging/LoggerMessageHelperTests.cs | 9 - .../Logging/HttpRequestReaderTest.cs | 8 - 62 files changed, 690 insertions(+), 3302 deletions(-) create mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Log.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLogPropertiesProvider.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingApplicationBuilderExtensions.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs create mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpRequestBodyReader.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IncomingRequestLogRecord.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/Log.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingOptions.cs create mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptions.cs rename src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/{LoggingOptionsValidator.cs => LoggingRedactionOptionsValidator.cs} (64%) delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/MediaTypeSetExtensions.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/PipeReaderExtensions.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStream.cs delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStreamPool.cs delete mode 100644 src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LogMethodHelper.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpRequestBodyReaderTests.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestLogRecordTests.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestStructTests.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/InfiniteStream.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingMiddlewareTests.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MediaTypeSetExtensionsTests.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/PipeReaderExtensionsTests.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyErrorPipeFeature.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyInfinitePipeFeature.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyMultiSegmentPipeFeature.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ResponseInterceptingStreamTests.cs delete mode 100644 test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestBodyPipeFeatureMiddleware.cs delete mode 100644 test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LogMethodHelperTests.cs diff --git a/Directory.Build.targets b/Directory.Build.targets index 7954433dfb6..3adec0a4c9b 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -26,7 +26,7 @@ $(NoWarn);AD0001 - $(NoWarn);EXTEXP0001;EXTEXP0002;EXTEXP0003;EXTEXP0004;EXTEXP0005;EXTEXP0006;EXTEXP0007;EXTEXP0008;EXTEXP0009;EXTEXP0010;EXTEXP0011;EXTEXP0012 + $(NoWarn);EXTEXP0001;EXTEXP0002;EXTEXP0003;EXTEXP0004;EXTEXP0005;EXTEXP0006;EXTEXP0007;EXTEXP0008;EXTEXP0009;EXTEXP0010;EXTEXP0011;EXTEXP0012;EXTEXP0013 $(NoWarn);NU5104 diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md index 3e2f6cd3653..5033c0be651 100644 --- a/docs/list-of-diagnostics.md +++ b/docs/list-of-diagnostics.md @@ -27,3 +27,4 @@ if desired. | `EXTEXP0010` | Object pool experiments | | `EXTEXP0011` | Document database experiments | | `EXTEXP0012` | Auto-activation experiments | +| `EXTEXP0013` | HttpLogging middleware experiments | diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Log.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Log.cs new file mode 100644 index 00000000000..9ce4db19905 --- /dev/null +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Log.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Diagnostics; + +internal static partial class Log +{ + [LoggerMessage(0, LogLevel.Warning, "Enricher failed: {Enricher}.")] + internal static partial void EnricherFailed(this ILogger logger, Exception exception, string enricher); +} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs index 973b224de2b..eb5792dad40 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HeaderReader.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; using System.Collections.Generic; using System.Linq; @@ -19,17 +21,10 @@ public HeaderReader(IDictionary headersToLog, IRedac { _redactorProvider = redactorProvider; - _headers = headersToLog.Count == 0 - ? Array.Empty>() - : headersToLog.ToArray(); + _headers = headersToLog.Count == 0 ? [] : headersToLog.ToArray(); } - /// - /// Reads headers and applies filtering (if required). - /// - /// A collection of headers to be read from. - /// A list to be filled with headers. - public void Read(IHeaderDictionary headers, List> listToFill) + public void Read(IHeaderDictionary headers, IList> logContext, string prefix) { if (headers.Count == 0) { @@ -42,8 +37,9 @@ public void Read(IHeaderDictionary headers, List> l { var provider = _redactorProvider.GetRedactor(header.Value); var redacted = provider.Redact(headerValue.ToString()); - listToFill.Add(new(header.Key, redacted)); + logContext.Add(new(prefix + header.Key, redacted)); } } } } +#endif diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLogPropertiesProvider.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLogPropertiesProvider.cs deleted file mode 100644 index 93ec3d3fc98..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLogPropertiesProvider.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Concurrent; -using Microsoft.AspNetCore.Diagnostics.Logging; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal static class HttpLogPropertiesProvider -{ - private static readonly ConcurrentDictionary _requestPrefixedNamesCache = new(); - private static readonly ConcurrentDictionary _responsePrefixedNamesCache = new(); - - public static void GetTags(LogMethodHelper helper, IncomingRequestLogRecord logRecord) - { - helper.Add(HttpLoggingTagNames.Method, logRecord.Method); - helper.Add(HttpLoggingTagNames.Host, logRecord.Host); - helper.Add(HttpLoggingTagNames.Path, logRecord.Path); - - if (logRecord.Duration.HasValue) - { - helper.Add(HttpLoggingTagNames.Duration, logRecord.Duration.Value); - } - - if (logRecord.StatusCode.HasValue) - { - helper.Add(HttpLoggingTagNames.StatusCode, logRecord.StatusCode.Value); - } - - if (logRecord.PathParameters is not null) - { - for (int i = 0; i < logRecord.PathParametersCount; i++) - { - var p = logRecord.PathParameters[i]; - helper.Add(p.Name, p.Value); - } - } - - if (logRecord.RequestBody is not null) - { - helper.Add(HttpLoggingTagNames.RequestBody, logRecord.RequestBody); - } - - if (logRecord.ResponseBody is not null) - { - helper.Add(HttpLoggingTagNames.ResponseBody, logRecord.ResponseBody); - } - - if (logRecord.RequestHeaders is not null) - { - var count = logRecord.RequestHeaders.Count; - for (int i = 0; i < count; i++) - { - var header = logRecord.RequestHeaders[i]; - var prefixedName = _requestPrefixedNamesCache.GetOrAdd(header.Key, static x => HttpLoggingTagNames.RequestHeaderPrefix + x); - helper.Add(prefixedName, header.Value); - } - } - - if (logRecord.ResponseHeaders is not null) - { - var count = logRecord.ResponseHeaders.Count; - for (int i = 0; i < count; i++) - { - var header = logRecord.ResponseHeaders[i]; - var prefixedName = _responsePrefixedNamesCache.GetOrAdd(header.Key, static x => HttpLoggingTagNames.ResponseHeaderPrefix + x); - helper.Add(prefixedName, header.Value); - } - } - } -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingApplicationBuilderExtensions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingApplicationBuilderExtensions.cs deleted file mode 100644 index 82728d1a63d..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingApplicationBuilderExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.AspNetCore.Diagnostics.Logging; -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.AspNetCore.Builder; - -/// -/// Extensions to attach the HTTP logging middleware. -/// -public static class HttpLoggingApplicationBuilderExtensions -{ - /// - /// Registers incoming HTTP request logging middleware into . - /// - /// - /// Request logging middleware should be placed after call. - /// - /// An application's request pipeline builder. - /// The value of . - /// is . - public static IApplicationBuilder UseHttpLoggingMiddleware(this IApplicationBuilder builder) - => Throw.IfNull(builder) - .UseMiddleware([]); -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs deleted file mode 100644 index e36851d6a57..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs +++ /dev/null @@ -1,459 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.Collections.Frozen; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.Logging; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Mvc.Formatters; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Compliance.Classification; -using Microsoft.Extensions.Compliance.Redaction; -using Microsoft.Extensions.Http.Diagnostics; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.ObjectPool; -using Microsoft.Extensions.Options; -using Microsoft.Shared.Diagnostics; -using Microsoft.Shared.Pools; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal sealed class HttpLoggingMiddleware : IMiddleware -{ - // These three fields are "internal" solely for testing purposes: - internal int BodyReadSizeLimit; - internal TimeProvider TimeProvider = TimeProvider.System; - internal Func> GetResponseBodyInterceptedData = static stream => stream.GetInterceptedSequence(); - internal TimeSpan RequestBodyReadTimeout; - - private readonly bool _logRequestStart; - private readonly bool _logRequestBody; - private readonly bool _logResponseBody; - private readonly bool _logRequestHeaders; - private readonly bool _logResponseHeaders; - - private readonly IncomingPathLoggingMode _requestPathLogMode; - private readonly HttpRouteParameterRedactionMode _parameterRedactionMode; - private readonly ILogger _logger; - private readonly IHttpRouteParser _httpRouteParser; - private readonly IHttpRouteFormatter _httpRouteFormatter; - private readonly IIncomingHttpRouteUtility _httpRouteUtility; - private readonly HeaderReader _requestHeadersReader; - private readonly HeaderReader _responseHeadersReader; - private readonly string[] _excludePathStartsWith; - private readonly IHttpLogEnricher[] _enrichers; - private readonly MediaType[] _requestMediaTypes; - private readonly MediaType[] _responseMediaTypes; - private readonly FrozenDictionary _parametersToRedactMap; - - private readonly ObjectPool _logRecordPool = - PoolFactory.CreatePool(); - - private readonly ObjectPool>> _headersPool = - PoolFactory.CreateListPool>(); - - [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Technical debt accepted.")] - public HttpLoggingMiddleware( - IOptions options, - ILogger logger, - IEnumerable httpLogEnrichers, - IHttpRouteParser httpRouteParser, - IHttpRouteFormatter httpRouteFormatter, - IRedactorProvider redactorProvider, - IIncomingHttpRouteUtility httpRouteUtility, - IDebuggerState? debugger = null) - { - var optionsValue = options.Value; - _logger = logger; - _httpRouteParser = httpRouteParser; - _httpRouteFormatter = httpRouteFormatter; - _httpRouteUtility = httpRouteUtility; - _logRequestStart = optionsValue.LogRequestStart; - if (optionsValue.LogBody) - { - _logRequestBody = optionsValue.RequestBodyContentTypes.Count > 0; - _logResponseBody = optionsValue.ResponseBodyContentTypes.Count > 0; - } - - _parametersToRedactMap = optionsValue.RouteParameterDataClasses.ToFrozenDictionary(StringComparer.Ordinal); - - _requestPathLogMode = EnsureRequestPathLoggingModeIsValid(optionsValue.RequestPathLoggingMode); - _parameterRedactionMode = optionsValue.RequestPathParameterRedactionMode; - - BodyReadSizeLimit = optionsValue.BodySizeLimit; - - debugger ??= DebuggerState.System; - RequestBodyReadTimeout = debugger.IsAttached - ? Timeout.InfiniteTimeSpan - : optionsValue.RequestBodyReadTimeout; - - _requestMediaTypes = optionsValue.RequestBodyContentTypes - .Select(static x => new MediaType(x)) - .ToArray(); - - _responseMediaTypes = optionsValue.ResponseBodyContentTypes - .Select(static x => new MediaType(x)) - .ToArray(); - - _logRequestHeaders = optionsValue.RequestHeadersDataClasses.Count > 0; - _logResponseHeaders = optionsValue.ResponseHeadersDataClasses.Count > 0; - _requestHeadersReader = new(optionsValue.RequestHeadersDataClasses, redactorProvider); - _responseHeadersReader = new(optionsValue.ResponseHeadersDataClasses, redactorProvider); - - _excludePathStartsWith = optionsValue.ExcludePathStartsWith.ToArray(); - - _enrichers = httpLogEnrichers.ToArray(); - - // There's no need to use this middleware, - // so log a warning and hope that "LogLevel.Warning" is enabled: - if (!_logger.IsEnabled(Log.DefaultLogLevel)) - { - _logger.MiddlewareIsMisused(Log.DefaultLogLevel, nameof(HttpLoggingApplicationBuilderExtensions.UseHttpLoggingMiddleware)); - } - } - - public Task InvokeAsync(HttpContext context, RequestDelegate next) - { - if (ShouldExcludePath(context.Request.Path)) - { - return next(context); - } - else - { - return InvokeAsyncWithPathAsync(context, next); - } - } - - private static IncomingPathLoggingMode EnsureRequestPathLoggingModeIsValid(IncomingPathLoggingMode mode) - => mode switch - { - IncomingPathLoggingMode.Structured or IncomingPathLoggingMode.Formatted => mode, - _ => throw new InvalidOperationException($"Unsupported value '{mode}' for enum type '{nameof(IncomingPathLoggingMode)}'"), - }; - - private async Task InvokeAsyncWithPathAsync(HttpContext context, RequestDelegate next) - { - ResponseInterceptingStream? bufferingResponseStream = null; - string? requestBody = null; - var timestamp = TimeProvider.GetTimestamp(); - try - { - if (_logResponseBody) - { - // Swapping response stream: - var oldFeature = context.Features.Get()!; - bufferingResponseStream = ResponseInterceptingStreamPool.Get(oldFeature, BodyReadSizeLimit); - context.Features.Set(bufferingResponseStream); - } - - requestBody = _logRequestBody - ? await GetRequestBodyAsync(context.Request, context.RequestAborted).ConfigureAwait(false) - : null; - - if (_logRequestStart) - { - LogRequest(context, timestamp, isRequestStart: true, requestBody, responseBody: null); - } - - await next(context).ConfigureAwait(false); - - var responseBody = _logResponseBody - ? GetResponseBody(context.Response, bufferingResponseStream!) - : null; - - context.Response.OnCompleted(() => - { - LogRequest(context, timestamp, isRequestStart: false, requestBody, responseBody); - - return Task.CompletedTask; - }); - } - catch (Exception ex) - { - // Even if the response body has been already read, we can re-read it safely: - var responseBody = _logResponseBody - ? GetResponseBody(context.Response, bufferingResponseStream!) - : null; - - LogRequest(context, timestamp, isRequestStart: false, requestBody, responseBody, ex); - - throw; - } - finally - { - if (bufferingResponseStream is not null) - { - context.Features.Set(bufferingResponseStream.InnerBodyFeature); - ResponseInterceptingStreamPool.Return(bufferingResponseStream); - } - } - } - - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Intentional")] - private string? GetResponseBody(HttpResponse response, ResponseInterceptingStream stream) - { - if (!MediaTypeSetExtensions.Covers(_responseMediaTypes, response.ContentType)) - { - return null; - } - - try - { - var sequenceSpan = GetResponseBodyInterceptedData(stream).Span; - - return Encoding.UTF8.GetString(sequenceSpan); - } - catch (Exception ex) - { - // We are intentionally catching and logging any exceptions which may happen. - _logger.ErrorReadingResponseBody(ex); - return null; - } - } - - private void LogRequest( - HttpContext context, - long timestamp, - bool isRequestStart, - string? requestBody, - string? responseBody, - Exception? exception = null) - { - const int StatusCodeOnException = 0; - const int LowestUnsuccessfulStatusCode = 400; - - // Don't get a tag collector for "RequestStart" log record: - var collector = isRequestStart || _enrichers.Length == 0 - ? null - : LogMethodHelper.GetHelper(); - - var requestHeaders = _logRequestHeaders - ? _headersPool.Get() - : null; - - // Checking response headers, since we can possibly don't have a response: - var responseHeaders = _logResponseHeaders && context.Response.Headers.Count > 0 - ? _headersPool.Get() - : null; - - var logRecord = _logRecordPool.Get(); - try - { - logRecord.RequestBody = requestBody; - logRecord.ResponseBody = responseBody; - logRecord.RequestHeaders = requestHeaders; - logRecord.ResponseHeaders = responseHeaders; - - if (requestHeaders != null) - { - _requestHeadersReader.Read(context.Request.Headers, requestHeaders); - } - - if (responseHeaders != null) - { - _responseHeadersReader.Read(context.Response.Headers, responseHeaders); - } - - FillLogRecord(logRecord, context, collector); - - if (isRequestStart) - { - // Don't emit both status code and duration tags on request start: - logRecord.Duration = null; - logRecord.StatusCode = null; - } - else - { - // Catching duration at the end: - logRecord.Duration = (long)TimeProvider.GetElapsedTime(timestamp, TimeProvider.GetTimestamp()).TotalMilliseconds; - } - - if (exception == null) - { - _logger.IncomingRequest(logRecord); - } - else - { - // Logging status code == 0 when exception occurs and no middleware has set a meaningful status code: - if (logRecord.StatusCode < LowestUnsuccessfulStatusCode) - { - logRecord.StatusCode = StatusCodeOnException; - } - - _logger.RequestProcessingError(exception, logRecord); - } - } - finally - { - if (logRecord.PathParameters != null) - { - ArrayPool.Shared.Return(logRecord.PathParameters); - logRecord.PathParameters = null; - } - - if (collector != null) - { - LogMethodHelper.ReturnHelper(collector); - logRecord.EnrichmentPropertyBag = null; - } - - if (requestHeaders != null) - { - _headersPool.Return(requestHeaders); - logRecord.RequestHeaders = null; - } - - if (responseHeaders != null) - { - _headersPool.Return(responseHeaders); - logRecord.ResponseHeaders = null; - } - - // Return log record at the end: - _logRecordPool.Return(logRecord); - } - } - - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Intentional")] - private async Task GetRequestBodyAsync(HttpRequest request, CancellationToken token) - { - if (!MediaTypeSetExtensions.Covers(_requestMediaTypes, request.ContentType)) - { - return null; - } - - try - { - var sequence = - await request.ReadBodyAsync(RequestBodyReadTimeout, BodyReadSizeLimit, token) - .ConfigureAwait(false); - -#if NET5_0_OR_GREATER - var stringifiedBody = Encoding.UTF8.GetString(in sequence); -#else - string stringifiedBody; - if (sequence.IsSingleSegment) - { - stringifiedBody = Encoding.UTF8.GetString(sequence.FirstSpan); - } - else - { - var buffer = ArrayPool.Shared.Rent((int)sequence.Length); - try - { - sequence.CopyTo(buffer); - stringifiedBody = Encoding.UTF8.GetString(buffer.AsSpan(0, (int)sequence.Length)); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } -#endif - - return stringifiedBody; - } - catch (OperationCanceledException) - { - // Rethrow cancellation exceptions. - throw; - } - catch (Exception ex) - { - // We are intentionally catching and logging any exceptions which may happen. - _logger.ErrorReadingRequestBody(ex); - return null; - } - } - - private void FillLogRecord( - IncomingRequestLogRecord logRecord, - HttpContext context, - LogMethodHelper? collector) - { - var request = context.Request; - var response = context.Response; - - string path = TelemetryConstants.Unknown; - var pathParamsCount = 0; - - if (_parameterRedactionMode != HttpRouteParameterRedactionMode.None) - { - var endpoint = context.GetEndpoint() as RouteEndpoint; - - if (endpoint?.RoutePattern.RawText != null) - { - var httpRoute = endpoint.RoutePattern.RawText; - var paramsToRedact = _httpRouteUtility.GetSensitiveParameters(httpRoute, request, _parametersToRedactMap); - - var routeSegments = _httpRouteParser.ParseRoute(httpRoute); - - if (_requestPathLogMode == IncomingPathLoggingMode.Formatted) - { - path = _httpRouteFormatter.Format(in routeSegments, request.Path, _parameterRedactionMode, paramsToRedact); - logRecord.PathParameters = null; - } - else - { - // Case when logging mode is IncomingPathLoggingMode.Structured - path = httpRoute; - var routeParams = ArrayPool.Shared.Rent(routeSegments.ParameterCount); - - // Setting this value right away to be able to return it back to pool in a callee's "finally" block: - logRecord.PathParameters = routeParams; - if (_httpRouteParser.TryExtractParameters(request.Path, in routeSegments, _parameterRedactionMode, paramsToRedact, ref routeParams)) - { - pathParamsCount = routeSegments.ParameterCount; - } - } - } - else - { - logRecord.PathParameters = null; - } - } - else if (request.Path.HasValue) - { - path = request.Path.Value!; - } - - // We need to set all the values (logRecord was taken from the pool): - logRecord.Path = path; - logRecord.PathParametersCount = pathParamsCount; - logRecord.Method = request.Method; - logRecord.StatusCode = response.StatusCode; - logRecord.Host = request.Host.Value; - - if (collector != null) - { - foreach (var enricher in _enrichers) - { - enricher.Enrich(collector, request, response); - } - } - - logRecord.EnrichmentPropertyBag = collector; - } - - private bool ShouldExcludePath(string path) - { - foreach (var excludedPath in _excludePathStartsWith) - { - if (path.StartsWith(excludedPath, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs new file mode 100644 index 00000000000..330c7d0c376 --- /dev/null +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if NET8_0_OR_GREATER + +using System; +using System.Buffers; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpLogging; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Compliance.Classification; +using Microsoft.Extensions.Compliance.Redaction; +using Microsoft.Extensions.Http.Diagnostics; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Diagnostics.Logging; + +internal sealed class HttpLoggingRedactionInterceptor : IHttpLoggingInterceptor +{ + private readonly IHttpLogEnricher[] _enrichers; + private readonly IncomingPathLoggingMode _requestPathLogMode; + private readonly HttpRouteParameterRedactionMode _parameterRedactionMode; + private readonly ILogger _logger; + private readonly IHttpRouteParser _httpRouteParser; + private readonly IHttpRouteFormatter _httpRouteFormatter; + private readonly IIncomingHttpRouteUtility _httpRouteUtility; + private readonly HeaderReader _requestHeadersReader; + private readonly HeaderReader _responseHeadersReader; + private readonly string[] _excludePathStartsWith; + private readonly FrozenDictionary _parametersToRedactMap; + + public HttpLoggingRedactionInterceptor( + IOptions options, + ILogger logger, + IEnumerable httpLogEnrichers, + IHttpRouteParser httpRouteParser, + IHttpRouteFormatter httpRouteFormatter, + IRedactorProvider redactorProvider, + IIncomingHttpRouteUtility httpRouteUtility) + { + var optionsValue = options.Value; + _logger = logger; + _enrichers = httpLogEnrichers.ToArray(); + _httpRouteParser = httpRouteParser; + _httpRouteFormatter = httpRouteFormatter; + _httpRouteUtility = httpRouteUtility; + + _parametersToRedactMap = optionsValue.RouteParameterDataClasses.ToFrozenDictionary(StringComparer.Ordinal); + + _requestPathLogMode = EnsureRequestPathLoggingModeIsValid(optionsValue.RequestPathLoggingMode); + _parameterRedactionMode = optionsValue.RequestPathParameterRedactionMode; + + _requestHeadersReader = new(optionsValue.RequestHeadersDataClasses, redactorProvider); + _responseHeadersReader = new(optionsValue.ResponseHeadersDataClasses, redactorProvider); + + _excludePathStartsWith = optionsValue.ExcludePathStartsWith.ToArray(); + } + + public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext) + { + var context = logContext.HttpContext; + var request = context.Request; + if (_excludePathStartsWith.Length != 0 && ShouldExcludePath(context.Request.Path.Value!)) + { + logContext.LoggingFields = HttpLoggingFields.None; + return default; + } + + // Don't redact if we're not going to log any part of the request + if (!logContext.IsAnyEnabled(HttpLoggingFields.RequestPropertiesAndHeaders)) + { + return default; + } + + // Always included, redaction will filter it out of the headers by default. + logContext.AddParameter(HttpLoggingTagNames.Host, context.Request.Host.Value); + + if (logContext.TryDisable(HttpLoggingFields.RequestPath)) + { + string path = TelemetryConstants.Unknown; + + if (_parameterRedactionMode != HttpRouteParameterRedactionMode.None) + { + var endpoint = context.GetEndpoint() as RouteEndpoint; + + if (endpoint?.RoutePattern.RawText != null) + { + var httpRoute = endpoint.RoutePattern.RawText; + var paramsToRedact = _httpRouteUtility.GetSensitiveParameters(httpRoute, request, _parametersToRedactMap); + + var routeSegments = _httpRouteParser.ParseRoute(httpRoute); + + if (_requestPathLogMode == IncomingPathLoggingMode.Formatted) + { + path = _httpRouteFormatter.Format(in routeSegments, request.Path, _parameterRedactionMode, paramsToRedact); + } + else + { + // Case when logging mode is IncomingPathLoggingMode.Structured + path = httpRoute; + var routeParams = ArrayPool.Shared.Rent(routeSegments.ParameterCount); + + // Setting this value right away to be able to return it back to pool in a callee's "finally" block: + if (_httpRouteParser.TryExtractParameters(request.Path, in routeSegments, _parameterRedactionMode, paramsToRedact, ref routeParams)) + { + for (var i = 0; i < routeSegments.ParameterCount; i++) + { + logContext.AddParameter(routeParams[i].Name, routeParams[i].Value); + } + } + } + } + } + else if (request.Path.HasValue) + { + path = request.Path.Value!; + } + + logContext.AddParameter(nameof(request.Path), path); + } + + if (logContext.TryDisable(HttpLoggingFields.RequestHeaders)) + { + _requestHeadersReader.Read(context.Request.Headers, logContext.Parameters, HttpLoggingTagNames.RequestHeaderPrefix); + } + + return default; + } + + public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext) + { + var context = logContext.HttpContext; + + if (logContext.TryDisable(HttpLoggingFields.ResponseHeaders)) + { + _responseHeadersReader.Read(context.Response.Headers, logContext.Parameters, HttpLoggingTagNames.ResponseHeaderPrefix); + } + + // Don't enrich if we're not going to log any part of the response + if (_enrichers.Length == 0 + || (!logContext.IsAnyEnabled(HttpLoggingFields.Response) && logContext.Parameters.Count == 0)) + { + return default; + } + + var loggerMessageState = LoggerMessageHelper.ThreadLocalState; + + try + { + foreach (var enricher in _enrichers) + { +#pragma warning disable CA1031 // Do not catch general exception types + try + { + enricher.Enrich(loggerMessageState, context); + } + catch (Exception ex) + { + _logger.EnricherFailed(ex, enricher.GetType().Name); + } +#pragma warning restore CA1031 // Do not catch general exception types + } + + foreach (var pair in loggerMessageState) + { + logContext.Parameters.Add(pair); + } + } + finally + { + loggerMessageState.Clear(); + } + + return default; + } + + private static IncomingPathLoggingMode EnsureRequestPathLoggingModeIsValid(IncomingPathLoggingMode mode) + => mode switch + { + IncomingPathLoggingMode.Structured or IncomingPathLoggingMode.Formatted => mode, + _ => throw new InvalidOperationException($"Unsupported value '{mode}' for enum type '{nameof(IncomingPathLoggingMode)}'"), + }; + + private bool ShouldExcludePath(string path) + { + foreach (var excludedPath in _excludePathStartsWith) + { + if (path.StartsWith(excludedPath, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } +} + +#endif diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingServiceCollectionExtensions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingServiceCollectionExtensions.cs index 83829c4767d..1ac60bb5598 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingServiceCollectionExtensions.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingServiceCollectionExtensions.cs @@ -1,14 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Diagnostics.Logging; +using Microsoft.AspNetCore.HttpLogging; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Http.Diagnostics; -using Microsoft.Extensions.Options; -using Microsoft.IO; +using Microsoft.Shared.DiagnosticIds; using Microsoft.Shared.Diagnostics; namespace Microsoft.Extensions.DependencyInjection; @@ -16,57 +18,49 @@ namespace Microsoft.Extensions.DependencyInjection; /// /// Extension methods to register the HTTP logging feature within the service. /// +[Experimental(diagnosticId: Experiments.HttpLogging, UrlFormat = Experiments.UrlFormat)] public static class HttpLoggingServiceCollectionExtensions { /// - /// Adds components for incoming HTTP requests logging into . + /// Enables enrichment and redaction of HTTP request logging output. /// - /// The to add the service to. - /// The value of . + /// + /// This will enable and by default. + /// + /// The service collection. + /// Configures the redaction options. + /// The value of . /// is . - public static IServiceCollection AddHttpLogging(this IServiceCollection services) - => Throw.IfNull(services) - .AddHttpLoggingInternal(); - - /// - /// Adds components for incoming HTTP requests logging into . - /// - /// The to add the service to. - /// - /// An to configure the . - /// - /// The value of . - /// - /// Either or is . - /// - public static IServiceCollection AddHttpLogging(this IServiceCollection services, Action configure) + public static IServiceCollection AddHttpLoggingRedaction(this IServiceCollection services, Action? configure = null) { _ = Throw.IfNull(services); - _ = Throw.IfNull(configure); - return AddHttpLoggingInternal(services, builder => builder.Configure(configure)); + _ = services.AddOptionsWithValidateOnStart() + .Configure(configure ?? (static _ => { })); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + + return services.AddHttpLogging(o => + { + o.CombineLogs = true; + o.LoggingFields |= HttpLoggingFields.Duration; + }) + + // Internal stuff for route processing: + .AddHttpRouteProcessor() + .AddHttpRouteUtilities(); } /// - /// Adds components for incoming HTTP requests logging into . + /// Enables enrichment and redaction of HTTP request logging output. /// - /// The to add the service to. - /// The configuration section to bind to. - /// The value of . - /// - /// Either or is . - /// - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(LoggingOptions))] - [UnconditionalSuppressMessage( - "Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = "Addressed with [DynamicDependency]")] - public static IServiceCollection AddHttpLogging(this IServiceCollection services, IConfigurationSection section) + /// The service collection. + /// The configuration section with the redaction settings. + /// The value of . + public static IServiceCollection AddHttpLoggingRedaction(this IServiceCollection services, IConfigurationSection section) { - _ = Throw.IfNull(services); _ = Throw.IfNull(section); - return AddHttpLoggingInternal(services, builder => builder.Bind(section)); + return services.AddHttpLoggingRedaction(section.Bind); } /// @@ -78,23 +72,10 @@ public static IServiceCollection AddHttpLogging(this IServiceCollection services /// is . public static IServiceCollection AddHttpLogEnricher(this IServiceCollection services) where T : class, IHttpLogEnricher - => Throw.IfNull(services) - .AddActivatedSingleton(); - - private static IServiceCollection AddHttpLoggingInternal( - this IServiceCollection services, - Action>? configureOptionsBuilder = null) { - var builder = services - .AddOptionsWithValidateOnStart(); - - configureOptionsBuilder?.Invoke(builder); - - services.TryAddSingleton(); - services.TryAddActivatedSingleton(); - - return services - .AddHttpRouteProcessor() - .AddHttpRouteUtilities(); + _ = Throw.IfNull(services); + return services.AddHttpLoggingRedaction() + .AddActivatedSingleton(); } } +#endif diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs index a91e687c462..548c82babfc 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingTagNames.cs @@ -14,47 +14,47 @@ public static class HttpLoggingTagNames /// /// HTTP Request duration in milliseconds. /// - public const string Duration = "duration"; + public const string Duration = "Duration"; /// /// HTTP Host. /// - public const string Host = "httpHost"; + public const string Host = "Host"; /// /// HTTP Method. /// - public const string Method = "httpMethod"; + public const string Method = "Method"; /// /// HTTP Path. /// - public const string Path = "httpPath"; + public const string Path = "Path"; /// /// HTTP Request Headers prefix. /// - public const string RequestHeaderPrefix = "httpRequestHeader_"; + public const string RequestHeaderPrefix = "RequestHeader."; /// /// HTTP Response Headers prefix. /// - public const string ResponseHeaderPrefix = "httpResponseHeader_"; + public const string ResponseHeaderPrefix = "ResponseHeader."; /// /// HTTP Request Body. /// - public const string RequestBody = "httpRequestBody"; + public const string RequestBody = "RequestBody"; /// /// HTTP Response Body. /// - public const string ResponseBody = "httpResponseBody"; + public const string ResponseBody = "ResponseBody"; /// /// HTTP Status Code. /// - public const string StatusCode = "httpStatusCode"; + public const string StatusCode = "StatusCode"; /// /// Gets a list of all dimension names. diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpRequestBodyReader.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpRequestBodyReader.cs deleted file mode 100644 index 5307745c34e..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpRequestBodyReader.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal static class HttpRequestBodyReader -{ - internal const string ReadCancelled = "[read-cancelled]"; - - private static readonly ReadOnlySequence _readCancelled = new(Encoding.UTF8.GetBytes(ReadCancelled)); - - public static async ValueTask> ReadBodyAsync( - this HttpRequest request, - TimeSpan readTimeout, - int readSizeLimit, - CancellationToken token) - { - using var joinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token); - joinedTokenSource.CancelAfter(readTimeout); - - try - { - /* - * We enable buffering with max threshold and max limit. - * - Max threshold practically means we don't start writing into a file. - * - Max limit practically means we won't get a capacity exception. - */ - request.EnableBuffering(int.MaxValue, long.MaxValue); - - return await request.BodyReader.ReadAsync(readSizeLimit, joinedTokenSource.Token).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Token source hides triggering token (https://github.com/dotnet/runtime/issues/22172) - if (!token.IsCancellationRequested) - { - return _readCancelled; - } - - throw; - } - finally - { - if (request.Body.CanSeek) - { - _ = request.Body.Seek(0, SeekOrigin.Begin); - } - } - } -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IHttpLogEnricher.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IHttpLogEnricher.cs index 42a712f9f2a..f559415ff94 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IHttpLogEnricher.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IHttpLogEnricher.cs @@ -1,21 +1,26 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Diagnostics.Enrichment; +using Microsoft.Shared.DiagnosticIds; namespace Microsoft.AspNetCore.Diagnostics.Logging; /// /// Interface for implementing log enrichers for incoming HTTP requests. /// +[Experimental(diagnosticId: Experiments.HttpLogging, UrlFormat = Experiments.UrlFormat)] public interface IHttpLogEnricher { /// /// Enrich logs. /// /// Tag collector to add tags to. - /// object associated with the incoming HTTP request. - /// object associated with the response to an incoming HTTP request. - void Enrich(IEnrichmentTagCollector collector, HttpRequest request, HttpResponse response); + /// object associated with the incoming HTTP request. + void Enrich(IEnrichmentTagCollector collector, HttpContext httpContext); } +#endif diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IncomingRequestLogRecord.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IncomingRequestLogRecord.cs deleted file mode 100644 index c862a4684d6..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/IncomingRequestLogRecord.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using Microsoft.Extensions.Http.Diagnostics; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal sealed class IncomingRequestLogRecord -{ - /// - /// Gets or sets a request host. - /// - public string Host { get; set; } = string.Empty; - - /// - /// Gets or sets a request method. - /// - public string Method { get; set; } = string.Empty; - - /// - /// Gets or sets a request path. - /// - public string Path { get; set; } = string.Empty; - - /// - /// Gets or sets request path parameters. - /// - public HttpRouteParameter[]? PathParameters { get; set; } - - /// - /// Gets or sets request path parameters count for . - /// - public int PathParametersCount { get; set; } - - /// - /// Gets or sets a request's duration in milliseconds. - /// - public long? Duration { get; set; } - - /// - /// Gets or sets response status code. - /// - public int? StatusCode { get; set; } - - /// - /// Gets or sets a list of request headers. - /// - public List>? RequestHeaders { get; set; } - - /// - /// Gets or sets a list of response headers. - /// - public List>? ResponseHeaders { get; set; } - - /// - /// Gets or sets enrichment properties. - /// - public LogMethodHelper? EnrichmentPropertyBag { get; set; } - - /// - /// Gets or sets parsed request body. - /// - public string? RequestBody { get; set; } - - /// - /// Gets or sets parsed response body. - /// - public string? ResponseBody { get; set; } -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/Log.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/Log.cs deleted file mode 100644 index 388bc8f7791..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/Log.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Diagnostics.Logging; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -#pragma warning disable S109 - -internal static partial class Log -{ - internal const LogLevel DefaultLogLevel = LogLevel.Information; - internal const LogLevel ErrorLogLevel = LogLevel.Error; - internal const string OriginalFormat = "{OriginalFormat}"; - internal const string OriginalFormatValue = "{httpMethod} {httpHost}/{httpPath}"; - internal const string ReadingRequestBodyError = "Error on reading HTTP request body."; - internal const string ReadingResponseBodyError = "Error on reading HTTP response body."; - - private const int IncomingRequestEventId = 1; - private const int RequestProcessingErrorEventId = 2; - -#pragma warning disable S3257 // Declarations and initializations should be as concise as possible - private static readonly Func _originalFormatValueFMTFunc = new(OriginalFormatValueFMT); -#pragma warning restore S3257 // Declarations and initializations should be as concise as possible - - #region Non-generated logging - - public static void IncomingRequest(this ILogger logger, IncomingRequestLogRecord req) - { - if (logger.IsEnabled(DefaultLogLevel)) - { - var collector = req.EnrichmentPropertyBag ?? LogMethodHelper.GetHelper(); - - try - { - collector.ParameterName = string.Empty; - HttpLogPropertiesProvider.GetTags(collector, req); - - collector.Add(OriginalFormat, OriginalFormatValue); - logger.Log( - DefaultLogLevel, - new(IncomingRequestEventId, nameof(IncomingRequest)), - new IncomingRequestStruct(collector), - null, - _originalFormatValueFMTFunc); - } - finally - { - // Stryker disable once all - if (collector != req.EnrichmentPropertyBag) - { - LogMethodHelper.ReturnHelper(collector); - } - } - } - } - - public static void RequestProcessingError(this ILogger logger, Exception ex, IncomingRequestLogRecord req) - { - if (logger.IsEnabled(ErrorLogLevel)) - { - var collector = req.EnrichmentPropertyBag ?? LogMethodHelper.GetHelper(); - - try - { - collector.ParameterName = string.Empty; - HttpLogPropertiesProvider.GetTags(collector, req); - - collector.Add(OriginalFormat, OriginalFormatValue); - logger.Log( - ErrorLogLevel, - new(RequestProcessingErrorEventId, nameof(RequestProcessingError)), - new IncomingRequestStruct(collector), - ex, - _originalFormatValueFMTFunc); - } - finally - { - // Stryker disable once all - if (collector != req.EnrichmentPropertyBag) - { - LogMethodHelper.ReturnHelper(collector); - } - } - } - } - - internal readonly struct IncomingRequestStruct : IReadOnlyList> - { - private readonly LogMethodHelper _collector; - - public IncomingRequestStruct(LogMethodHelper collector) - { - _collector = collector; - } - - public int Count - => _collector.Count; - - public KeyValuePair this[int index] - => _collector[index]; - - public IEnumerator> GetEnumerator() - { - for (int i = 0; i < Count; i++) - { - yield return this[i]; - } - } - - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); - } - - #endregion - - [LoggerMessage(3, LogLevel.Error, ReadingRequestBodyError)] - public static partial void ErrorReadingRequestBody(this ILogger logger, Exception ex); - - [LoggerMessage(4, LogLevel.Error, ReadingResponseBodyError)] - public static partial void ErrorReadingResponseBody(this ILogger logger, Exception ex); - -#pragma warning disable LOGGEN000 - [LoggerMessage(5, LogLevel.Warning, - $"HttpLogging middleware is injected into application pipeline, but {nameof(LogLevel)} '{{logLevel}}' is disabled in logger. " + - "Remove {methodName}() call from pipeline configuration in that case.")] - public static partial void MiddlewareIsMisused(this ILogger logger, LogLevel logLevel, string methodName); -#pragma warning restore LOGGEN000 - - private static string OriginalFormatValueFMT(IncomingRequestStruct request, Exception? _) - { - int startIndex = FindStartIndex(request); - var httpMethod = request[startIndex].Value; - var httpHost = request[startIndex + 1].Value; - var httpPath = request[startIndex + 2].Value; - return FormattableString.Invariant($"{httpMethod} {httpHost}/{httpPath}"); - } - - [ExcludeFromCodeCoverage] - private static int FindStartIndex(in IncomingRequestStruct request) - { - int startIndex = 0; - - foreach (var kvp in request) - { - if (kvp.Key == HttpLoggingTagNames.Method) - { - break; - } - - startIndex++; - } - - return startIndex; - } -} - diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingOptions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingOptions.cs deleted file mode 100644 index 0b46a450461..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingOptions.cs +++ /dev/null @@ -1,216 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Diagnostics.Logging; -using Microsoft.Extensions.Compliance.Classification; -using Microsoft.Extensions.Http.Diagnostics; -using Microsoft.Shared.Data.Validation; -using Microsoft.Shared.DiagnosticIds; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -/// -/// Top-level model for formatting incoming HTTP requests and their corresponding responses. -/// -public class LoggingOptions -{ - private const int Millisecond = 1; - private const int Minute = 60_000; - private const int MaxBodyReadSize = 1_572_864; // 1.5 MB - private const int DefaultBodyReadSizeLimit = 32 * 1024; // ≈ 32K - private const IncomingPathLoggingMode DefaultRequestPathLoggingMode = IncomingPathLoggingMode.Formatted; - private const HttpRouteParameterRedactionMode DefaultPathParameterRedactionMode = HttpRouteParameterRedactionMode.Strict; - - /// - /// Gets or sets a value indicating whether the request is logged additionally before any further processing. - /// - /// - /// The default value is . - /// - /// - /// When enabled, two entries will be logged for each incoming request. Note, that the first log record won't be enriched. - /// When disabled, only one entry will be logged for each incoming request (with corresponding response's data). - /// - public bool LogRequestStart { get; set; } - - /// - /// Gets or sets a value indicating whether HTTP request and response body will be logged. - /// - /// - /// The default value is . - /// - /// - /// Avoid enabling this option in a production environment as it might lead to leaking privacy information. - /// - public bool LogBody { get; set; } - - /// - /// Gets or sets a strategy how request path should be logged. - /// - /// - /// The default value is . - /// - /// - /// Make sure you add redactors to ensure that sensitive information doesn't find its way into your log records. - /// This option only applies when the - /// option is not set to . - /// - public IncomingPathLoggingMode RequestPathLoggingMode { get; set; } = DefaultRequestPathLoggingMode; - - /// - /// Gets or sets a value indicating how request path parameter should be redacted. - /// - /// - /// The default value is . - /// - [Experimental(diagnosticId: Experiments.Telemetry, UrlFormat = Experiments.UrlFormat)] - public HttpRouteParameterRedactionMode RequestPathParameterRedactionMode { get; set; } = DefaultPathParameterRedactionMode; - - /// - /// Gets or sets a maximum amount of time to wait for the request body to be read. - /// - /// - /// The default value is 1 second. - /// - /// - /// The value should be in the range of 1 millisecond to 1 minute. - /// - [TimeSpan(Millisecond, Minute)] - public TimeSpan RequestBodyReadTimeout { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Gets or sets the maximum number of bytes of the request/response body to be read. - /// - /// - /// The default is ≈ 32K. - /// - /// - /// The number should ideally be below 85000 bytes to not be allocated on the large object heap. - /// - [Range(1, MaxBodyReadSize)] - public int BodySizeLimit { get; set; } = DefaultBodyReadSizeLimit; - - /// - /// Gets or sets a map between HTTP path parameters and their data classification. - /// - /// - /// The default value is an empty dictionary. - /// - /// - /// If a parameter within a controller's action is not annotated with a data classification attribute and - /// it's not found in this map, it will be redacted as if it was . - /// If you don't want a parameter to be redacted, mark it as . - /// - [Required] - [SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Options pattern.")] - public IDictionary RouteParameterDataClasses { get; set; } = new Dictionary(); - - /// - /// Gets or sets a map between request headers to be logged and their data classification. - /// - /// - /// The default value is an empty dictionary, which means that no request header is logged by default. - /// - [Required] - [SuppressMessage("Usage", "CA2227:Collection properties should be read only", - Justification = "Options pattern.")] - public IDictionary RequestHeadersDataClasses { get; set; } = new Dictionary(); - - /// - /// Gets or sets the set of request body content types which are considered text and thus possible to log. - /// - /// - /// The default value is an empty , which means that the request's body isn't logged. - /// - /// - /// Don't enable body logging in a production environment, as it might impact - /// performance and leak sensitive data. - /// If you need to log body in production, go through compliance and security. - /// - /// - /// A typical set of known text content-types like json, xml or text would be: - /// - /// RequestBodyContentTypesToLog = new HashSet<string> - /// { - /// "application/*+json", - /// "application/*+xml", - /// "application/json", - /// "application/xml", - /// "text/*" - /// }; - /// - /// - [Required] - [SuppressMessage("Usage", "CA2227:Collection properties should be read only", - Justification = "Options pattern.")] - public ISet RequestBodyContentTypes { get; set; } = new HashSet(); - - /// - /// Gets or sets a map between response headers to be logged and their data classification. - /// - /// - /// The default value is an empty dictionary, which means that no response header is logged by default. - /// - [Required] - [SuppressMessage("Usage", "CA2227:Collection properties should be read only", - Justification = "Options pattern.")] - public IDictionary ResponseHeadersDataClasses { get; set; } = new Dictionary(); - - /// - /// Gets or sets the set of response body content types which are considered text and thus possible to log. - /// - /// - /// The default value is an empty , which means that the response's body isn't logged. - /// - /// - /// Don't enable body logging in a production environment, as it might impact performance and leak sensitive data. - /// If you need to log body in production, go through compliance and security. - /// - /// - /// A typical set of known text content-types like json, xml or text would be: - /// - /// ResponseBodyContentTypesToLog = new HashSet<string> - /// { - /// "application/*+json", - /// "application/*+xml", - /// "application/json", - /// "application/xml", - /// "text/*" - /// }; - /// - /// - [Required] - [SuppressMessage("Usage", "CA2227:Collection properties should be read only", - Justification = "Options pattern.")] - public ISet ResponseBodyContentTypes { get; set; } = new HashSet(); - - /// - /// Gets or sets the set of HTTP paths that should be excluded from logging. - /// - /// - /// The default value is an empty . - /// - /// - /// Any path added to the set will not be logged. - /// Paths are case insensitive. - /// - /// - /// A typical set of HTTP paths would be: - /// - /// ExcludePathStartsWith = new HashSet<string> - /// { - /// "/probe/live", - /// "/probe/ready" - /// }; - /// - /// - [Experimental(diagnosticId: Experiments.Telemetry, UrlFormat = Experiments.UrlFormat)] - [Required] - [SuppressMessage("Usage", "CA2227:Collection properties should be read only", - Justification = "Options pattern.")] - public ISet ExcludePathStartsWith { get; set; } = new HashSet(); -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptions.cs new file mode 100644 index 00000000000..69d46ea4735 --- /dev/null +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptions.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if NET8_0_OR_GREATER + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Compliance.Classification; +using Microsoft.Extensions.Http.Diagnostics; +using Microsoft.Shared.DiagnosticIds; + +namespace Microsoft.AspNetCore.Diagnostics.Logging; + +/// +/// Top-level model for redacting incoming HTTP requests and their corresponding responses. +/// +[Experimental(diagnosticId: Experiments.HttpLogging, UrlFormat = Experiments.UrlFormat)] +public class LoggingRedactionOptions +{ + private const IncomingPathLoggingMode DefaultRequestPathLoggingMode = IncomingPathLoggingMode.Formatted; + private const HttpRouteParameterRedactionMode DefaultPathParameterRedactionMode = HttpRouteParameterRedactionMode.Strict; + + /// + /// Gets or sets a strategy how request path should be logged. + /// + /// + /// The default value is . + /// + /// + /// Make sure you add redactors to ensure that sensitive information doesn't find its way into your log records. + /// This option only applies when the + /// option is not set to . + /// + public IncomingPathLoggingMode RequestPathLoggingMode { get; set; } = DefaultRequestPathLoggingMode; + + /// + /// Gets or sets a value indicating how request path parameter should be redacted. + /// + /// + /// The default value is . + /// + public HttpRouteParameterRedactionMode RequestPathParameterRedactionMode { get; set; } = DefaultPathParameterRedactionMode; + + /// + /// Gets or sets a map between HTTP path parameters and their data classification. + /// + /// + /// The default value is an empty dictionary. + /// + /// + /// If a parameter within a controller's action is not annotated with a data classification attribute and + /// it's not found in this map, it will be redacted as if it was . + /// If you don't want a parameter to be redacted, mark it as . + /// + [Required] +#pragma warning disable CA2227 // Collection properties should be read only + public IDictionary RouteParameterDataClasses { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); +#pragma warning restore CA2227 // Collection properties should be read only + + /// + /// Gets or sets a map between request headers to be logged and their data classification. + /// + /// + /// The default value is an empty dictionary, which means that no request header is logged by default. + /// + [Required] +#pragma warning disable CA2227 // Collection properties should be read only + public IDictionary RequestHeadersDataClasses { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); +#pragma warning restore CA2227 // Collection properties should be read only + + /// + /// Gets or sets a map between response headers to be logged and their data classification. + /// + /// + /// The default value is an empty dictionary, which means that no response header is logged by default. + /// + [Required] +#pragma warning disable CA2227 // Collection properties should be read only + public IDictionary ResponseHeadersDataClasses { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); +#pragma warning restore CA2227 // Collection properties should be read only + + /// + /// Gets or sets the set of HTTP paths that should be excluded from logging. + /// + /// + /// The default value is an empty . + /// + /// + /// Any path added to the set will not be logged. + /// Paths are case insensitive. + /// + /// + /// A typical set of HTTP paths would be: + /// - "/probe/live". + /// - "/probe/ready". + /// + [Required] +#pragma warning disable CA2227 // Collection properties should be read only + public ISet ExcludePathStartsWith { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); +#pragma warning restore CA2227 // Collection properties should be read only +} + +#endif diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingOptionsValidator.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptionsValidator.cs similarity index 64% rename from src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingOptionsValidator.cs rename to src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptionsValidator.cs index eb7cc16e1fa..088ff6658e3 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingOptionsValidator.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptionsValidator.cs @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Diagnostics.Logging; [OptionsValidator] -internal sealed partial class LoggingOptionsValidator : IValidateOptions +internal sealed partial class LoggingRedactionOptionsValidator : IValidateOptions { } +#endif diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/MediaTypeSetExtensions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/MediaTypeSetExtensions.cs deleted file mode 100644 index 56490c14dca..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/MediaTypeSetExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Mvc.Formatters; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal static class MediaTypeSetExtensions -{ - public static bool Covers(this MediaType[] supportedMediaTypes, string? mediaTypeToCheck) - { - if (string.IsNullOrEmpty(mediaTypeToCheck)) - { - return false; - } - - var sampleContentType = new MediaType(mediaTypeToCheck); - foreach (var supportedMediaType in supportedMediaTypes) - { - if (sampleContentType.IsSubsetOf(supportedMediaType)) - { - return true; - } - } - - return false; - } -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/PipeReaderExtensions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/PipeReaderExtensions.cs deleted file mode 100644 index 91edaf58beb..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/PipeReaderExtensions.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.Diagnostics.CodeAnalysis; -using System.IO.Pipelines; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal static class PipeReaderExtensions -{ - [SuppressMessage("Major Code Smell", "S125:Sections commented out", Justification = "Diagram")] - public static async Task> ReadAsync(this PipeReader pipeReader, int numBytes, - CancellationToken token) - { - long pointer = 0L; - while (true) - { - ReadResult result; - try - { - result = await pipeReader.ReadAsync(token).ConfigureAwait(false); - } - catch (TaskCanceledException ex) - { - throw new OperationCanceledException(ex.Message, ex, ex.CancellationToken); - } - - /* - * Move pointer to (N*buffer) lower than numBytes. - * +---------+ +---------+ +---------+ - * ||||||||||| ||||||||||| | | - * +---------+ +---------+ +-+-------+ - * ^ ^ ^ - * pointer ------ + | | - * num bytes -----------+ | - * advanced to -------------------+ - * - */ - - if (!result.IsCompleted && result.Buffer.Length < numBytes) - { - var bufferStart = result.Buffer.Start; - var bufferEnd = result.Buffer.End; - - pipeReader.AdvanceTo(bufferStart, bufferEnd); - pointer += bufferEnd.GetInteger() - bufferStart.GetInteger(); - - continue; - } - - /* - * Move pointer by bytes remaining after (N*buffer). - * +---------+ +---------+ +---------+ - * ||||||||||| ||||||||||| ||| | - * +---------+ +---------+ +-+-------+ - * ^ ^ - * pointer -----------+ | - * num bytes -----------+ | - * advanced to -------------------+ - * - */ - if (!result.IsCompleted && pointer < numBytes) - { - pointer = numBytes; - } - - return result.Buffer.Slice(0L, pointer); - } - } -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersEnricherServiceCollectionExtensions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersEnricherServiceCollectionExtensions.cs index 227f5681b4d..c001868321e 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersEnricherServiceCollectionExtensions.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersEnricherServiceCollectionExtensions.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Diagnostics.Logging; -using Microsoft.Extensions.Configuration; using Microsoft.Shared.Diagnostics; namespace Microsoft.Extensions.DependencyInjection; @@ -22,59 +19,28 @@ public static class RequestHeadersEnricherServiceCollectionExtensions /// The value of . /// is . public static IServiceCollection AddRequestHeadersLogEnricher(this IServiceCollection services) - => Throw.IfNull(services) - .AddLogEnricherOptions(_ => { }) - .RegisterRequestHeadersEnricher(); - - /// - /// Adds an instance of Request Headers Log Enricher to the . - /// - /// The to add the Request Headers Log Enricher to. - /// The configuration delegate. - /// The value of . - /// is . - public static IServiceCollection AddRequestHeadersLogEnricher(this IServiceCollection services, Action configure) { _ = Throw.IfNull(services); - _ = Throw.IfNull(configure); - + _ = services.AddOptionsWithValidateOnStart(); return services - .AddLogEnricherOptions(configure) - .RegisterRequestHeadersEnricher(); + .AddHttpContextAccessor() + .AddLogEnricher(); } /// /// Adds an instance of Request Headers Log Enricher to the . /// /// The to add the Request Headers Log Enricher to. - /// The to use for configuring - /// in the Request Headers Log Enricher. + /// The configuration delegate. /// The value of . /// is . - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(RequestHeadersLogEnricherOptions))] - [UnconditionalSuppressMessage( - "Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = "Addressed with [DynamicDependency]")] - public static IServiceCollection AddRequestHeadersLogEnricher(this IServiceCollection services, IConfigurationSection section) + public static IServiceCollection AddRequestHeadersLogEnricher(this IServiceCollection services, Action configure) { _ = Throw.IfNull(services); - _ = Throw.IfNull(section); + _ = Throw.IfNull(configure); return services - .Configure(section) - .AddLogEnricherOptions(_ => { }) - .RegisterRequestHeadersEnricher(); + .Configure(configure) + .AddRequestHeadersLogEnricher(); } - - private static IServiceCollection RegisterRequestHeadersEnricher(this IServiceCollection services) - => services - .AddHttpContextAccessor() - .AddLogEnricher(); - - private static IServiceCollection AddLogEnricherOptions(this IServiceCollection services, Action configure) - => services - .AddOptionsWithValidateOnStart() - .Configure(configure) - .Services; } diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricherOptions.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricherOptions.cs index ccfd5010fbf..bf4e3456fd0 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricherOptions.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricherOptions.cs @@ -1,13 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Compliance.Classification; using Microsoft.Shared.DiagnosticIds; -namespace Microsoft.AspNetCore.Diagnostics; +namespace Microsoft.AspNetCore.Diagnostics.Logging; /// /// Options for the Request Headers enricher. @@ -23,6 +24,6 @@ public class RequestHeadersLogEnricherOptions [Required] [Experimental(diagnosticId: Experiments.Telemetry, UrlFormat = Experiments.UrlFormat)] #pragma warning disable CA2227 // Collection properties should be read only - public IDictionary HeadersDataClasses { get; set; } = new Dictionary(); + public IDictionary HeadersDataClasses { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); #pragma warning restore CA2227 // Collection properties should be read only } diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStream.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStream.cs deleted file mode 100644 index 5994bf10abb..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStream.cs +++ /dev/null @@ -1,224 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.IO; -using System.IO.Pipelines; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Shared.Pools; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -/// -/// Intercepts data being written to stream and writes its copy to another data structure. -/// -/// -/// Copied from ASP .NET and adjusted for current needs. -/// -internal sealed class ResponseInterceptingStream : Stream, IHttpResponseBodyFeature -{ - private const bool LeavePipeWriterOpened = true; - private static readonly StreamPipeWriterOptions _pipeWriterOptions - = new(leaveOpen: LeavePipeWriterOpened); - - private PipeWriter? _pipeAdapter; - - public PipeWriter Writer => _pipeAdapter ??= PipeWriter.Create(this, _pipeWriterOptions); - - public Stream Stream => this; - - public override bool CanSeek => InterceptedStream.CanSeek; - - public override bool CanRead => InterceptedStream.CanRead; - - public override bool CanWrite => InterceptedStream.CanWrite; - - public override long Length => InterceptedStream.Length; - - public override long Position - { - get => InterceptedStream.Position; - set => InterceptedStream.Position = value; - } - - public override int WriteTimeout - { - get => InterceptedStream.WriteTimeout; - set => InterceptedStream.WriteTimeout = value; - } - - /// - /// Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - /// Justification: I need parameterless ctor to use pooling on this object. I don't want to declare it as nullable because - /// using current pattern it is never null after initialization. - /// -#pragma warning disable CS8618 - public ResponseInterceptingStream() - { - } -#pragma warning restore CS8618 - - public ResponseInterceptingStream( - Stream interceptedStream, - IHttpResponseBodyFeature responseBodyFeature, - BufferWriter bufferWriter, - int interceptedValueWriteLimit) - { - InterceptedStream = interceptedStream; - InnerBodyFeature = responseBodyFeature; - InterceptedValueBuffer = bufferWriter; - InterceptedValueWriteLimit = interceptedValueWriteLimit; - } - - internal Stream InterceptedStream { get; set; } - - internal IHttpResponseBodyFeature InnerBodyFeature { get; set; } - - internal int InterceptedValueWriteLimit { get; set; } - - internal BufferWriter InterceptedValueBuffer { get; set; } - - public override void Flush() - { - InterceptedStream.Flush(); - } - - public override Task FlushAsync(CancellationToken cancellationToken) - { - return InterceptedStream.FlushAsync(cancellationToken); - } - - public override long Seek(long offset, SeekOrigin origin) - { - return InterceptedStream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - InterceptedStream.SetLength(value); - } - -#if NET5_0_OR_GREATER - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - return InterceptedStream.BeginWrite(buffer, offset, count, callback, state); - } -#else - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object? state) - { - return InterceptedStream.BeginWrite(buffer, offset, count, callback, state); - } -#endif - - public override void EndWrite(IAsyncResult asyncResult) - { - InterceptedStream.EndWrite(asyncResult); - } - - public override int Read(Span buffer) - { - return InterceptedStream.Read(buffer); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return InterceptedStream.Read(buffer, offset, count); - } - - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - return InterceptedStream.ReadAsync(buffer, cancellationToken); - } - - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return InterceptedStream.ReadAsync(buffer, offset, count, cancellationToken); - } - -#if NET5_0_OR_GREATER - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - return InterceptedStream.BeginRead(buffer, offset, count, callback, state); - } -#else - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object? state) - { - return InterceptedStream.BeginRead(buffer, offset, count, callback, state); - } -#endif - - public override int EndRead(IAsyncResult asyncResult) - { - return InterceptedStream.EndRead(asyncResult); - } - - public override void CopyTo(Stream destination, int bufferSize) - { - InterceptedStream.CopyTo(destination, bufferSize); - } - - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - return InterceptedStream.CopyToAsync(destination, bufferSize, cancellationToken); - } - - public override async ValueTask DisposeAsync() - { - await base.DisposeAsync().ConfigureAwait(false); - await InterceptedStream.DisposeAsync().ConfigureAwait(false); - } - - public Task StartAsync(CancellationToken cancellationToken = default) - { - return InnerBodyFeature.StartAsync(cancellationToken); - } - - public override void Write(byte[] buffer, int offset, int count) - { - Write(buffer.AsSpan(offset, count)); - } - - public override void Write(ReadOnlySpan buffer) - { - var valueToWriteUntilLimit = InterceptedValueWriteLimit - InterceptedValueBuffer.WrittenCount; - var innerCount = Math.Min(valueToWriteUntilLimit, buffer.Length); - - InterceptedValueBuffer.Write(buffer.Slice(0, innerCount)); - InterceptedStream.Write(buffer); - } - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return WriteAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); - } - - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - var valueToWriteUntilLimit = InterceptedValueWriteLimit - InterceptedValueBuffer.WrittenCount; - var innerCount = Math.Min(valueToWriteUntilLimit, buffer.Length); - - InterceptedValueBuffer.Write(buffer.Span.Slice(0, innerCount)); - - return InterceptedStream.WriteAsync(buffer, cancellationToken); - } - - public void DisableBuffering() - { - InnerBodyFeature.DisableBuffering(); - } - - public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellationToken = default) - { - return InnerBodyFeature.SendFileAsync(path, offset, count, cancellationToken); - } - - public Task CompleteAsync() - { - return InnerBodyFeature.CompleteAsync(); - } - - internal ReadOnlyMemory GetInterceptedSequence() => InterceptedValueBuffer.WrittenMemory; -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStreamPool.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStreamPool.cs deleted file mode 100644 index b8221ceea68..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/ResponseInterceptingStreamPool.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.ObjectPool; -using Microsoft.Shared.Pools; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal static class ResponseInterceptingStreamPool -{ - private static readonly StreamResponseBodyFeature _dummyBodyFeature = new(Stream.Null); - private static readonly Stream _dummyStream = Stream.Null; - private static readonly BufferWriter _dummyBufferWriter = new(); - - private static ObjectPool StreamPool { get; } - = PoolFactory.CreatePool(); - - private static ObjectPool> BufferWriterPool { get; } = Microsoft.Shared.Pools.BufferWriterPool.SharedBufferWriterPool; - - public static ResponseInterceptingStream Get(IHttpResponseBodyFeature innerBodyFeature, int limit) - { - var instance = StreamPool.Get(); - var bufferWriter = BufferWriterPool.Get(); - - instance.InnerBodyFeature = innerBodyFeature; - instance.InterceptedStream = innerBodyFeature.Stream; - instance.InterceptedValueWriteLimit = limit; - instance.InterceptedValueBuffer = bufferWriter; - - return instance; - } - - public static void Return(ResponseInterceptingStream stream) - { - stream.InnerBodyFeature = _dummyBodyFeature; - stream.InterceptedStream = _dummyStream; - stream.InterceptedValueWriteLimit = 0; - - BufferWriterPool.Return(stream.InterceptedValueBuffer); - stream.InterceptedValueBuffer = _dummyBufferWriter; - - StreamPool.Return(stream); - } -} diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json index db6369a355b..777e30c8db5 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Microsoft.AspNetCore.Diagnostics.Middleware.json @@ -1,38 +1,6 @@ { "Name": "Microsoft.AspNetCore.Diagnostics.Middleware, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "Types": [ - { - "Type": "static class Microsoft.AspNetCore.Builder.HttpLoggingApplicationBuilderExtensions", - "Stage": "Stable", - "Methods": [ - { - "Member": "static Microsoft.AspNetCore.Builder.IApplicationBuilder Microsoft.AspNetCore.Builder.HttpLoggingApplicationBuilderExtensions.UseHttpLoggingMiddleware(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder);", - "Stage": "Stable" - } - ] - }, - { - "Type": "static class Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions", - "Stage": "Stable", - "Methods": [ - { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions.AddHttpLogEnricher(this Microsoft.Extensions.DependencyInjection.IServiceCollection services);", - "Stage": "Stable" - }, - { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions.AddHttpLogging(this Microsoft.Extensions.DependencyInjection.IServiceCollection services);", - "Stage": "Stable" - }, - { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions.AddHttpLogging(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure);", - "Stage": "Stable" - }, - { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions.AddHttpLogging(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.Configuration.IConfigurationSection section);", - "Stage": "Stable" - } - ] - }, { "Type": "static class Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames", "Stage": "Stable", @@ -40,47 +8,47 @@ { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.Duration", "Stage": "Stable", - "Value": "duration" + "Value": "Duration" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.Host", "Stage": "Stable", - "Value": "httpHost" + "Value": "Host" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.Method", "Stage": "Stable", - "Value": "httpMethod" + "Value": "Method" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.Path", "Stage": "Stable", - "Value": "httpPath" + "Value": "Path" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.RequestBody", "Stage": "Stable", - "Value": "httpRequestBody" + "Value": "RequestBody" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.RequestHeaderPrefix", "Stage": "Stable", - "Value": "httpRequestHeader_" + "Value": "RequestHeader_" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.ResponseBody", "Stage": "Stable", - "Value": "httpResponseBody" + "Value": "ResponseBody" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.ResponseHeaderPrefix", "Stage": "Stable", - "Value": "httpResponseHeader_" + "Value": "ResponseHeader_" }, { "Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.StatusCode", "Stage": "Stable", - "Value": "httpStatusCode" + "Value": "StatusCode" } ], "Properties": [ @@ -90,16 +58,6 @@ } ] }, - { - "Type": "interface Microsoft.AspNetCore.Diagnostics.Logging.IHttpLogEnricher", - "Stage": "Stable", - "Methods": [ - { - "Member": "void Microsoft.AspNetCore.Diagnostics.Logging.IHttpLogEnricher.Enrich(Microsoft.Extensions.Diagnostics.Enrichment.IEnrichmentTagCollector collector, Microsoft.AspNetCore.Http.HttpRequest request, Microsoft.AspNetCore.Http.HttpResponse response);", - "Stage": "Stable" - } - ] - }, { "Type": "enum Microsoft.AspNetCore.Diagnostics.Logging.IncomingPathLoggingMode", "Stage": "Stable", @@ -122,66 +80,6 @@ } ] }, - { - "Type": "class Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions", - "Stage": "Stable", - "Methods": [ - { - "Member": "Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.LoggingOptions();", - "Stage": "Stable" - } - ], - "Properties": [ - { - "Member": "int Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.BodySizeLimit { get; set; }", - "Stage": "Stable" - }, - { - "Member": "System.Collections.Generic.ISet Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.ExcludePathStartsWith { get; set; }", - "Stage": "Experimental" - }, - { - "Member": "bool Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.LogBody { get; set; }", - "Stage": "Stable" - }, - { - "Member": "bool Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.LogRequestStart { get; set; }", - "Stage": "Stable" - }, - { - "Member": "System.Collections.Generic.ISet Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.RequestBodyContentTypes { get; set; }", - "Stage": "Stable" - }, - { - "Member": "System.TimeSpan Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.RequestBodyReadTimeout { get; set; }", - "Stage": "Stable" - }, - { - "Member": "System.Collections.Generic.IDictionary Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.RequestHeadersDataClasses { get; set; }", - "Stage": "Stable" - }, - { - "Member": "Microsoft.AspNetCore.Diagnostics.Logging.IncomingPathLoggingMode Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.RequestPathLoggingMode { get; set; }", - "Stage": "Stable" - }, - { - "Member": "Microsoft.Extensions.Http.Diagnostics.HttpRouteParameterRedactionMode Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.RequestPathParameterRedactionMode { get; set; }", - "Stage": "Experimental" - }, - { - "Member": "System.Collections.Generic.ISet Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.ResponseBodyContentTypes { get; set; }", - "Stage": "Stable" - }, - { - "Member": "System.Collections.Generic.IDictionary Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.ResponseHeadersDataClasses { get; set; }", - "Stage": "Stable" - }, - { - "Member": "System.Collections.Generic.IDictionary Microsoft.AspNetCore.Diagnostics.Logging.LoggingOptions.RouteParameterDataClasses { get; set; }", - "Stage": "Stable" - } - ] - }, { "Type": "static class Microsoft.AspNetCore.Diagnostics.Latency.RequestCheckpointConstants", "Stage": "Stable", @@ -222,27 +120,23 @@ "Stage": "Stable" }, { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.RequestHeadersEnricherServiceCollectionExtensions.AddRequestHeadersLogEnricher(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure);", - "Stage": "Stable" - }, - { - "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.RequestHeadersEnricherServiceCollectionExtensions.AddRequestHeadersLogEnricher(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.Configuration.IConfigurationSection section);", + "Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.RequestHeadersEnricherServiceCollectionExtensions.AddRequestHeadersLogEnricher(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure);", "Stage": "Stable" } ] }, { - "Type": "class Microsoft.AspNetCore.Diagnostics.RequestHeadersLogEnricherOptions", + "Type": "class Microsoft.AspNetCore.Diagnostics.Logging.RequestHeadersLogEnricherOptions", "Stage": "Stable", "Methods": [ { - "Member": "Microsoft.AspNetCore.Diagnostics.RequestHeadersLogEnricherOptions.RequestHeadersLogEnricherOptions();", + "Member": "Microsoft.AspNetCore.Diagnostics.Logging.RequestHeadersLogEnricherOptions.RequestHeadersLogEnricherOptions();", "Stage": "Stable" } ], "Properties": [ { - "Member": "System.Collections.Generic.IDictionary Microsoft.AspNetCore.Diagnostics.RequestHeadersLogEnricherOptions.HeadersDataClasses { get; set; }", + "Member": "System.Collections.Generic.IDictionary Microsoft.AspNetCore.Diagnostics.RequestHeadersLogEnricherOptions.Logging.HeadersDataClasses { get; set; }", "Stage": "Experimental" } ] @@ -300,4 +194,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs index c8adcb3755f..ea0807b0d44 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Enrichment/ApplicationEnricherTags.cs @@ -14,22 +14,22 @@ public static class ApplicationEnricherTags /// /// Application name. /// - public const string ApplicationName = "env_app_name"; + public const string ApplicationName = "AppName"; /// /// Environment name. /// - public const string EnvironmentName = "env_cloud_env"; + public const string EnvironmentName = "CloudEnv"; /// /// Deployment ring. /// - public const string DeploymentRing = "env_cloud_deploymentRing"; + public const string DeploymentRing = "CloudDeploymentRing"; /// /// Build version. /// - public const string BuildVersion = "env_cloud_roleVer"; + public const string BuildVersion = "CloudRoleVer"; /// /// Gets a list of all dimension names. diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LogMethodHelper.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LogMethodHelper.cs deleted file mode 100644 index 8b2919130da..00000000000 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LogMethodHelper.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using Microsoft.Extensions.Compliance.Classification; -using Microsoft.Extensions.Diagnostics.Enrichment; -using Microsoft.Extensions.ObjectPool; -using Microsoft.Shared.Diagnostics; -using Microsoft.Shared.Pools; - -namespace Microsoft.Extensions.Logging; - -/// -/// Utility type to support generated logging methods. -/// -/// -/// This type is not intended to be directly invoked by application code, -/// it is intended to be invoked by generated logging method code. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public sealed class LogMethodHelper : List>, ITagCollector, IEnrichmentTagCollector, IResettable -{ - private const string Separator = "_"; - - /// - public void Add(string tagName, object? tagValue) - { - _ = Throw.IfNull(tagName); - - string fullName = ParameterName.Length > 0 ? ParameterName + Separator + tagName : tagName; - Add(new KeyValuePair(fullName, tagValue)); - } - - /// - public void Add(string tagName, object? tagValue, DataClassification classification) => Add(tagName, tagValue); - - /// - /// Resets state of this container as described in . - /// - /// - /// if the object successfully reset and can be reused. - /// - public bool TryReset() - { - Clear(); - ParameterName = string.Empty; - return true; - } - - /// - /// Gets or sets the name of the logging method parameter for which to collect tags. - /// - public string ParameterName { get; set; } = string.Empty; - - /// - /// Enumerates an enumerable into a string. - /// - /// The enumerable object. - /// - /// A string representation of the enumerable. - /// - public static string Stringify(IEnumerable? enumerable) - { - if (enumerable == null) - { - return "null"; - } - - var sb = PoolFactory.SharedStringBuilderPool.Get(); - _ = sb.Append('['); - - bool first = true; - foreach (object? e in enumerable) - { - if (!first) - { - _ = sb.Append(','); - } - - if (e == null) - { - _ = sb.Append("null"); - } - else - { - _ = sb.Append(FormattableString.Invariant($"\"{e}\"")); - } - - first = false; - } - - _ = sb.Append(']'); - var result = sb.ToString(); - PoolFactory.SharedStringBuilderPool.Return(sb); - return result; - } - - /// - /// Enumerates an enumerable of key/value pairs into a string. - /// - /// Type of keys. - /// Type of values. - /// The enumerable object. - /// - /// A string representation of the enumerable. - /// - public static string Stringify(IEnumerable>? enumerable) - { - if (enumerable == null) - { - return "null"; - } - - var sb = PoolFactory.SharedStringBuilderPool.Get(); - _ = sb.Append('{'); - - bool first = true; - foreach (var kvp in enumerable) - { - if (!first) - { - _ = sb.Append(','); - } - - if (typeof(TKey).IsValueType || kvp.Key is not null) - { - _ = sb.Append(FormattableString.Invariant($"\"{kvp.Key}\"=")); - } - else - { - _ = sb.Append("null="); - } - - if (typeof(TValue).IsValueType || kvp.Value is not null) - { - _ = sb.Append(FormattableString.Invariant($"\"{kvp.Value}\"")); - } - else - { - _ = sb.Append("null"); - } - - first = false; - } - - _ = sb.Append('}'); - var result = sb.ToString(); - PoolFactory.SharedStringBuilderPool.Return(sb); - return result; - } - - private static readonly ObjectPool _helpers = PoolFactory.CreateResettingPool(); - - /// - /// Gets an instance of a helper from the global pool. - /// - /// A usable instance. - public static LogMethodHelper GetHelper() => _helpers.Get(); - - /// - /// Returns a helper instance to the global pool. - /// - /// The helper instance. - public static void ReturnHelper(LogMethodHelper helper) => _helpers.Return(helper); - - /// - void IEnrichmentTagCollector.Add(string tagName, object tagValue) - { - _ = Throw.IfNullOrEmpty(tagName); - Add(new KeyValuePair(tagName, tagValue)); - } - - /// - /// Gets log define options configured to skip the log level enablement check. - /// - public static LogDefineOptions SkipEnabledCheckOptions { get; } = new() { SkipEnabledCheck = true }; -} diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.cs index b9aa9dfcb3a..efebea99ea6 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Logging/LoggerMessageState.cs @@ -16,9 +16,9 @@ namespace Microsoft.Extensions.Logging; [EditorBrowsable(EditorBrowsableState.Never)] public sealed partial class LoggerMessageState { - private KeyValuePair[] _tags = Array.Empty>(); - private KeyValuePair[] _redactedTags = Array.Empty>(); - private ClassifiedTag[] _classifiedTags = Array.Empty(); + private KeyValuePair[] _tags = []; + private KeyValuePair[] _redactedTags = []; + private ClassifiedTag[] _classifiedTags = []; #pragma warning disable CA1819 // Properties should not return arrays /// diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json index 44dd7b3edd0..adecc16174c 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/Microsoft.Extensions.Diagnostics.ExtraAbstractions.json @@ -600,54 +600,6 @@ } ] }, - { - "Type": "sealed class Microsoft.Extensions.Logging.LogMethodHelper : System.Collections.Generic.List>, Microsoft.Extensions.Logging.ITagCollector, Microsoft.Extensions.Diagnostics.Enrichment.IEnrichmentTagCollector, Microsoft.Extensions.ObjectPool.IResettable", - "Stage": "Stable", - "Methods": [ - { - "Member": "Microsoft.Extensions.Logging.LogMethodHelper.LogMethodHelper();", - "Stage": "Stable" - }, - { - "Member": "void Microsoft.Extensions.Logging.LogMethodHelper.Add(string tagName, object? tagValue);", - "Stage": "Stable" - }, - { - "Member": "void Microsoft.Extensions.Logging.LogMethodHelper.Add(string tagName, object? tagValue, Microsoft.Extensions.Compliance.Classification.DataClassification classification);", - "Stage": "Stable" - }, - { - "Member": "static Microsoft.Extensions.Logging.LogMethodHelper Microsoft.Extensions.Logging.LogMethodHelper.GetHelper();", - "Stage": "Stable" - }, - { - "Member": "static void Microsoft.Extensions.Logging.LogMethodHelper.ReturnHelper(Microsoft.Extensions.Logging.LogMethodHelper helper);", - "Stage": "Stable" - }, - { - "Member": "static string Microsoft.Extensions.Logging.LogMethodHelper.Stringify(System.Collections.IEnumerable? enumerable);", - "Stage": "Stable" - }, - { - "Member": "static string Microsoft.Extensions.Logging.LogMethodHelper.Stringify(System.Collections.Generic.IEnumerable>? enumerable);", - "Stage": "Stable" - }, - { - "Member": "bool Microsoft.Extensions.Logging.LogMethodHelper.TryReset();", - "Stage": "Stable" - } - ], - "Properties": [ - { - "Member": "string Microsoft.Extensions.Logging.LogMethodHelper.ParameterName { get; set; }", - "Stage": "Stable" - }, - { - "Member": "static Microsoft.Extensions.Logging.LogDefineOptions Microsoft.Extensions.Logging.LogMethodHelper.SkipEnabledCheckOptions { get; }", - "Stage": "Stable" - } - ] - }, { "Type": "sealed class Microsoft.Extensions.Logging.LogPropertiesAttribute : System.Attribute", "Stage": "Stable", @@ -908,4 +860,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/Shared/DiagnosticIds/Experiments.cs b/src/Shared/DiagnosticIds/Experiments.cs index dd7cbeed828..43deb56273c 100644 --- a/src/Shared/DiagnosticIds/Experiments.cs +++ b/src/Shared/DiagnosticIds/Experiments.cs @@ -30,4 +30,5 @@ internal static class Experiments internal const string ObjectPool = "EXTEXP0010"; internal const string DocumentDb = "EXTEXP0011"; internal const string AutoActivation = "EXTEXP0012"; + internal const string HttpLogging = "EXTEXP0013"; } diff --git a/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs b/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs index a27f0e08584..5aca5bbe07e 100644 --- a/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs +++ b/test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs @@ -237,7 +237,7 @@ public void LogPropertiesTest() ["classToLog_GetOnlyProperty"] = classToLog.GetOnlyProperty.ToString(CultureInfo.InvariantCulture), ["classToLog_VirtualPropertyBase"] = classToLog.VirtualPropertyBase, ["classToLog_NonVirtualPropertyBase"] = classToLog.NonVirtualPropertyBase, - ["classToLog_TransitivePropertyArray"] = LogMethodHelper.Stringify(classToLog.TransitivePropertyArray), + ["classToLog_TransitivePropertyArray"] = LoggerMessageHelper.Stringify(classToLog.TransitivePropertyArray), ["classToLog_TransitiveProperty_TransitiveNumberProp"] = classToLog.TransitiveProperty.TransitiveNumberProp.ToString(CultureInfo.InvariantCulture), diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Mvc.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Mvc.cs index 271c8e49fe0..36b4897fba8 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Mvc.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Mvc.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -41,7 +43,7 @@ public static void ConfigureServices(IServiceCollection services) public static void Configure(IApplicationBuilder app) => app .UseRouting() - .UseHttpLoggingMiddleware() + .UseHttpLogging() .UseEndpoints(endpoints => endpoints.MapControllers()); } @@ -55,7 +57,7 @@ public async Task TestServer_WhenController_RedactPath(HttpRouteParameterRedacti { await RunControllerAsync( LogLevel.Information, - services => services.AddHttpLogging(o => o.RequestPathParameterRedactionMode = mode), + services => services.AddHttpLoggingRedaction(o => o.RequestPathParameterRedactionMode = mode), async (logCollector, client) => { const string UserId = "testUserId"; @@ -90,7 +92,7 @@ public async Task TestServer_WhenControllerWithPathRoute_RedactParameters() { await RunControllerAsync( LogLevel.Information, - services => services.AddHttpLogging(x => x.RequestPathLoggingMode = IncomingPathLoggingMode.Structured), + services => services.AddHttpLoggingRedaction(x => x.RequestPathLoggingMode = IncomingPathLoggingMode.Structured), async (logCollector, client) => { const string UserId = "testUserId"; @@ -129,7 +131,7 @@ public async Task TestServer_WhenControllerWithPathRoute_HonorRouteParamDataClas { await RunControllerAsync( LogLevel.Information, - services => services.AddHttpLogging(x => + services => services.AddHttpLoggingRedaction(x => { x.RouteParameterDataClasses.Add(new(NoDataClassParamName, DataClassification.None)); x.RequestPathLoggingMode = IncomingPathLoggingMode.Structured; @@ -180,7 +182,7 @@ public async Task TestServer_WhenControllerWithPathRoute_RedactionModeNone() { await RunControllerAsync( LogLevel.Information, - services => services.AddHttpLogging(x => x.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None), + services => services.AddHttpLoggingRedaction(x => x.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None), async (logCollector, client) => { const string UserId = "testUserId"; @@ -218,7 +220,7 @@ public async Task TestServer_WhenControllerWithoutPathRoute_LogPath(bool routePa await RunControllerAsync( LogLevel.Information, - services => services.AddHttpLogging(x => + services => services.AddHttpLoggingRedaction(x => { x.RequestPathParameterRedactionMode = routeParameterRedactionModeNone ? HttpRouteParameterRedactionMode.None : HttpRouteParameterRedactionMode.Strict; @@ -250,3 +252,4 @@ await RunControllerAsync( }); } } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Routing.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Routing.cs index f01edd65cd6..7d95b0501b2 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Routing.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.Routing.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -32,7 +34,7 @@ public static void ConfigureServices(IServiceCollection services) public static void Configure(IApplicationBuilder app) => app .UseRouting() - .UseHttpLoggingMiddleware() + .UseHttpLogging() .UseEndpoints(endpoints => { endpoints.MapControllers(); @@ -129,7 +131,7 @@ await RunRoutingTestAsync( httpPath, configureHttpLogging: services => { - services.AddHttpLogging(o => o.RequestPathParameterRedactionMode = mode); + services.AddHttpLoggingRedaction(o => o.RequestPathParameterRedactionMode = mode); }, validateRequestState: state => { @@ -165,7 +167,7 @@ public async Task Routing_WithStructuredPath_RedactParameters( { await RunRoutingTestAsync( httpPath, - configureHttpLogging: services => services.AddHttpLogging(options => + configureHttpLogging: services => services.AddHttpLoggingRedaction(options => { options.RequestPathLoggingMode = IncomingPathLoggingMode.Structured; }), @@ -194,3 +196,4 @@ await RunRoutingTestAsync( }); } } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs index 9769f4d8bf0..1e195026f57 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs @@ -1,20 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mime; -using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpLogging; using Microsoft.AspNetCore.TestHost; +using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.Enrichment; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting.Testing; using Microsoft.Extensions.Http.Diagnostics; @@ -29,7 +33,7 @@ namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; public partial class AcceptanceTests { - private const string LoggingCategory = "Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingMiddleware"; + private const string LoggingCategory = "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware"; private const int ErrorRouteProcessingTimeMs = 1_000; private const int SlashRouteProcessingTimeMs = 2_000; private static readonly TimeSpan _defaultLogTimeout = TimeSpan.FromSeconds(5); @@ -41,20 +45,20 @@ public static void ConfigureServices(IServiceCollection services) { services.AddRouting(); services.AddFakeRedaction(); - services.AddHttpLogging(); - services.AddSingleton(); + services.AddHttpLoggingRedaction(); } [SuppressMessage("Major Code Smell", "S1144:Unused private types or members should be removed", Justification = "Used through reflection")] public static void Configure(IApplicationBuilder app) { app.UseRouting(); - app.UseMiddleware(); - app.UseHttpLoggingMiddleware(); + app.UseHttpLogging(); app.Map("/error", static x => x.Run(static async context => { + await context.Request.Body.DrainAsync(default); + if (context.Request.QueryString.HasValue) { if (context.Request.QueryString.Value!.Contains("status")) @@ -69,18 +73,18 @@ public static void Configure(IApplicationBuilder app) } } - var middleware = context.RequestServices.GetRequiredService(); - var fakeTimeProvider = (FakeTimeProvider)middleware.TimeProvider; + var fakeTimeProvider = context.RequestServices.GetRequiredService(); fakeTimeProvider.Advance(TimeSpan.FromMilliseconds(ErrorRouteProcessingTimeMs)); throw new InvalidOperationException("Test exception"); })); app.Run(static async context => { - var middleware = context.RequestServices.GetRequiredService(); - var fakeTimeProvider = (FakeTimeProvider)middleware.TimeProvider; + var fakeTimeProvider = context.RequestServices.GetRequiredService(); fakeTimeProvider.Advance(TimeSpan.FromMilliseconds(SlashRouteProcessingTimeMs)); + await context.Request.Body.DrainAsync(default).ConfigureAwait(false); + context.Response.ContentType = MediaTypeNames.Text.Plain; context.Response.Headers.Append(HeaderNames.TransferEncoding, "chunked"); await context.Response.WriteAsync("Server: hello!").ConfigureAwait(false); @@ -97,8 +101,7 @@ private static Task RunAsync(LogLevel level, Action configur private static async Task RunAsync( LogLevel level, Action configure, - Func func, - Action? configureMiddleware = null) + Func func) where TStartup : class { using var host = await FakeHost.CreateBuilder(options => options.FakeLogging = false) @@ -106,21 +109,21 @@ private static async Task RunAsync( .AddFilter("Microsoft.Hosting", LogLevel.Warning) .AddFilter("Microsoft.Extensions.Hosting", LogLevel.Warning) .AddFilter("Microsoft.AspNetCore", LogLevel.Warning) - .AddFilter("Microsoft.AspNetCore.Diagnostics", level) + .AddFilter("Microsoft.AspNetCore.HttpLogging", level) .SetMinimumLevel(level) .AddFakeLogging()) - .ConfigureServices(x => x.AddSingleton()) - .ConfigureServices(configure) + .ConfigureServices(x => + { + x.AddSingleton(); + x.AddSingleton(s => s.GetRequiredService()); + }) .ConfigureWebHost(static builder => builder .UseStartup() .UseTestServer()) + .ConfigureServices(configure) .StartAsync(); var logCollector = host.Services.GetFakeLogCollector(); - var fakeClock = new FakeTimeProvider(); - var middleware = host.Services.GetRequiredService(); - middleware.TimeProvider = fakeClock; - configureMiddleware?.Invoke(middleware); using var client = host.GetTestClient(); @@ -157,9 +160,10 @@ await RunAsync( LogLevel.Information, services => services.AddHttpLogging(x => { - x.ResponseBodyContentTypes.Add(responseContentTypeToLog); - x.LogBody = true; - }), + x.MediaTypeOptions.Clear(); + x.MediaTypeOptions.AddText(responseContentTypeToLog); + x.LoggingFields |= HttpLoggingFields.ResponseBody; + }).AddHttpLoggingRedaction(), async (logCollector, client) => { const string Content = "Client: hello!"; @@ -192,12 +196,12 @@ await RunAsync( if (shouldLog) { Assert.Single(state, x => x.Key == HttpLoggingTagNames.ResponseBody && x.Value == "Server: hello!Server: world!"); - Assert.Equal(7, state!.Count); + Assert.Equal(8, state!.Count); } else { Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody); - Assert.Equal(6, state!.Count); + Assert.Equal(7, state!.Count); } }); } @@ -216,9 +220,10 @@ await RunAsync( LogLevel.Information, services => services.AddHttpLogging(x => { - x.RequestBodyContentTypes.Add("text/*"); - x.LogBody = true; - }), + x.MediaTypeOptions.Clear(); + x.MediaTypeOptions.AddText("text/*"); + x.LoggingFields |= HttpLoggingFields.RequestBody; + }).AddHttpLoggingRedaction(), async (logCollector, client) => { const string Content = "Client: hello!"; @@ -251,64 +256,30 @@ await RunAsync( if (shouldLog) { Assert.Single(state, x => x.Key == HttpLoggingTagNames.RequestBody && x.Value == Content); - Assert.Equal(7, state!.Count); + Assert.Equal(9, state!.Count); } else { Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.RequestBody); - Assert.Equal(6, state!.Count); + Assert.Equal(7, state!.Count); } }); } - [Fact] - public async Task HttpLogging_WhenMultiSegmentRequestPipe_LogRequestBody() - { - await RunAsync( - LogLevel.Information, - services => services.AddHttpLogging(x => - { - x.RequestBodyContentTypes.Add("text/*"); - x.LogBody = true; - }), - async (logCollector, client) => - { - const string Content = "Whatever..."; - - using var content = new StringContent(Content, null, MediaTypeNames.Text.Plain); - using var response = await client.PostAsync("/multi-segment-pipe", content).ConfigureAwait(false); - Assert.True(response.IsSuccessStatusCode); - - await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); - - Assert.Equal(1, logCollector.Count); - Assert.Null(logCollector.LatestRecord.Exception); - Assert.Equal(LogLevel.Information, logCollector.LatestRecord.Level); - Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category); - - var responseStatus = ((int)response.StatusCode).ToInvariantString(); - var state = logCollector.LatestRecord.StructuredState; - - Assert.Equal(7, state!.Count); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString()); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.RequestBody && x.Value == "Test Segment"); - }); - } - [Fact] public async Task HttpLogging_WhenLogLevelInfo_LogRequestStart() { await RunAsync( LogLevel.Information, - static services => services.AddHttpLogging(static x => + static services => services.AddHttpLoggingRedaction(x => { - x.LogRequestStart = true; - x.LogBody = true; x.RequestHeadersDataClasses.Add(HeaderNames.Accept, DataClassification.None); x.ResponseHeadersDataClasses.Add(HeaderNames.TransferEncoding, DataClassification.None); - x.RequestBodyContentTypes.Add(MediaTypeNames.Text.Plain); - x.ResponseBodyContentTypes.Add(MediaTypeNames.Text.Plain); + }).AddHttpLogging(static x => + { + x.CombineLogs = false; + x.LoggingFields = HttpLoggingFields.All; + x.MediaTypeOptions.AddText(MediaTypeNames.Text.Plain); }), async static (logCollector, client) => { @@ -326,36 +297,40 @@ async static (logCollector, client) => await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout, expectedRecords: 2); var logRecords = logCollector.GetSnapshot(); - Assert.Equal(2, logRecords.Count); + Assert.Equal(5, logRecords.Count); Assert.All(logRecords, x => Assert.Null(x.Exception)); Assert.All(logRecords, x => Assert.Equal(LogLevel.Information, x.Level)); Assert.All(logRecords, x => Assert.Equal(LoggingCategory, x.Category)); var responseStatus = ((int)response.StatusCode).ToInvariantString(); - var firstState = logRecords[0].StructuredState; - var secondState = logRecords[1].StructuredState; - - Assert.Equal(6, firstState!.Count); - Assert.DoesNotContain(firstState, x => x.Key == HttpLoggingTagNames.ResponseBody); - Assert.DoesNotContain(firstState, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix)); - Assert.DoesNotContain(firstState, x => x.Key == HttpLoggingTagNames.StatusCode); - Assert.DoesNotContain(firstState, x => x.Key == HttpLoggingTagNames.Duration); - Assert.Single(firstState, x => x.Key == HttpLoggingTagNames.RequestBody && x.Value == Content); - Assert.Single(firstState, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + HeaderNames.Accept); - Assert.Single(firstState, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value)); - Assert.Single(firstState, x => x.Key == HttpLoggingTagNames.Path && x.Value == TelemetryConstants.Unknown); - Assert.Single(firstState, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString()); - - Assert.Equal(10, secondState!.Count); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + HeaderNames.Accept); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.ResponseHeaderPrefix + HeaderNames.TransferEncoding); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.RequestBody && x.Value == Content); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.ResponseBody && x.Value == "Server: hello!Server: world!"); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value)); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.Path && x.Value == TelemetryConstants.Unknown); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString()); - Assert.Single(secondState, x => x.Key == HttpLoggingTagNames.Duration && x.Value != null); + var requestState = logRecords[0].StructuredState; + var requestBodyState = logRecords[1].StructuredState; + var responseState = logRecords[2].StructuredState; + var responseBodyState = logRecords[3].StructuredState; + var durationState = logRecords[4].StructuredState; + + Assert.Equal(6, requestState!.Count); + Assert.DoesNotContain(requestState, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix)); + Assert.DoesNotContain(requestState, x => x.Key == HttpLoggingTagNames.StatusCode); + Assert.DoesNotContain(requestState, x => x.Key == HttpLoggingTagNames.Duration); + Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + HeaderNames.Accept); + Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value)); + Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.Path && x.Value == TelemetryConstants.Unknown); + Assert.Single(requestState, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString()); + Assert.Single(requestState, x => x.Key == "Protocol" && x.Value == "HTTP/1.1"); + + Assert.Equal(3, requestBodyState!.Count); + Assert.Single(requestBodyState, x => x.Key == "Body" && x.Value == Content); + + Assert.Equal(2, responseState!.Count); + Assert.Single(responseState, x => x.Key == HttpLoggingTagNames.ResponseHeaderPrefix + HeaderNames.TransferEncoding); + Assert.Single(responseState, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); + + Assert.Equal(2, responseBodyState!.Count); + Assert.Single(responseBodyState, x => x.Key == "Body" && x.Value == "Server: hello!Server: world!"); + + Assert.Equal(2, durationState!.Count); + Assert.Single(durationState, x => x.Key == HttpLoggingTagNames.Duration && x.Value != null); }); } @@ -364,7 +339,7 @@ public async Task HttpLogging_WhenLogLevelInfo_LogHeaders() { await RunAsync( LogLevel.Information, - static services => services.AddHttpLogging(static x => + static services => services.AddHttpLoggingRedaction(static x => { x.RequestHeadersDataClasses.Add(HeaderNames.Accept, DataClassification.None); x.ResponseHeadersDataClasses.Add(HeaderNames.TransferEncoding, DataClassification.None); @@ -389,7 +364,7 @@ async static (logCollector, client) => var responseStatus = ((int)response.StatusCode).ToInvariantString(); var state = lastRecord.StructuredState; - Assert.Equal(8, state!.Count); + Assert.Equal(9, state!.Count); Assert.Single(state, x => x.Key == HttpLoggingTagNames.RequestHeaderPrefix + HeaderNames.Accept); Assert.Single(state, x => x.Key == HttpLoggingTagNames.ResponseHeaderPrefix + HeaderNames.TransferEncoding); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value)); @@ -418,7 +393,7 @@ await RunAsync( static x => { x.AddHttpLogEnricher(); - x.AddHttpLogging(); + x.AddHttpLoggingRedaction(); }, async static (logCollector, client) => { @@ -435,7 +410,7 @@ async static (logCollector, client) => var responseStatus = ((int)response.StatusCode).ToInvariantString(); var state = logCollector.LatestRecord.StructuredState; - Assert.Equal(8, state!.Count); + Assert.Equal(9, state!.Count); Assert.Single(state, x => x.Key == TestHttpLogEnricher.Key1 && x.Value == TestHttpLogEnricher.Value1); Assert.Single(state, x => x.Key == TestHttpLogEnricher.Key2 && x.Value == TestHttpLogEnricher.Value2.ToString(CultureInfo.CurrentCulture)); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Delete.ToString()); @@ -446,44 +421,6 @@ async static (logCollector, client) => }); } - [Fact] - public async Task HttpLogging_WhenMultipleEnrichersAdded_ErrorLog_CorrectlyFormatted() - { - await RunAsync( - LogLevel.Information, - static x => - { - x.AddHttpLogEnricher(); - x.AddHttpLogEnricher(); - x.AddHttpLogging(); - }, - async (logCollector, client) => - { - var ex = await Assert.ThrowsAsync(() => client.GetAsync("/error")); - await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); - Assert.Equal("GET localhost/unknown", logCollector.LatestRecord.Message); - }); - } - - [Fact] - public async Task HttpLogging_WhenMultipleEnrichersAdded_InformationLog_CorrectlyFormatted() - { - await RunAsync( - LogLevel.Information, - static x => - { - x.AddHttpLogEnricher(); - x.AddHttpLogEnricher(); - x.AddHttpLogging(opt => opt.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None); - }, - async (logCollector, client) => - { - await client.GetAsync("/api/users/123/add-task/345"); - await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); - Assert.Equal("GET localhost//api/users/123/add-task/345", logCollector.LatestRecord.Message); - }); - } - [Theory] [InlineData(IncomingPathLoggingMode.Structured)] [InlineData(IncomingPathLoggingMode.Formatted)] @@ -493,7 +430,7 @@ await RunAsync( LogLevel.Information, x => { - x.AddHttpLogging(options => + x.AddHttpLoggingRedaction(options => { options.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None; options.RequestPathLoggingMode = pathLoggingMode; @@ -515,7 +452,7 @@ async static (logCollector, client) => var responseStatus = ((int)response.StatusCode).ToInvariantString(); var state = logCollector.LatestRecord.StructuredState; - Assert.Equal(6, state!.Count); + Assert.Equal(7, state!.Count); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Path && x.Value == RequestPath); }); } @@ -528,7 +465,7 @@ await RunAsync( static x => { x.AddHttpLogEnricher(); - x.AddHttpLogging(x => x.LogRequestStart = true); + x.AddHttpLoggingRedaction().AddHttpLogging(x => x.CombineLogs = false); }, async static (logCollector, client) => { @@ -538,7 +475,7 @@ async static (logCollector, client) => await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout, expectedRecords: 2); var logRecords = logCollector.GetSnapshot(); - Assert.Equal(2, logRecords.Count); + Assert.Equal(3, logRecords.Count); Assert.All(logRecords, x => Assert.Null(x.Exception)); Assert.All(logRecords, x => Assert.Equal(LogLevel.Information, x.Level)); Assert.All(logRecords, x => Assert.Equal(LoggingCategory, x.Category)); @@ -547,11 +484,11 @@ async static (logCollector, client) => var firstState = logRecords[0].StructuredState; var secondState = logRecords[1].StructuredState; - Assert.Equal(4, firstState!.Count); + Assert.Equal(5, firstState!.Count); Assert.DoesNotContain(firstState, x => x.Key == TestHttpLogEnricher.Key1 && x.Value == TestHttpLogEnricher.Value1); Assert.DoesNotContain(firstState, x => x.Key == TestHttpLogEnricher.Key2 && x.Value == TestHttpLogEnricher.Value2.ToString(CultureInfo.CurrentCulture)); - Assert.Equal(8, secondState!.Count); + Assert.Equal(3, secondState!.Count); Assert.Single(secondState, x => x.Key == TestHttpLogEnricher.Key1 && x.Value == TestHttpLogEnricher.Value1); Assert.Single(secondState, x => x.Key == TestHttpLogEnricher.Key2 && x.Value == TestHttpLogEnricher.Value2.ToString(CultureInfo.CurrentCulture)); }); @@ -562,7 +499,7 @@ public async Task HttpLogging_WhenSecondLogRequestStart_DontLogDurationAndStatus { await RunAsync( LogLevel.Information, - static x => x.AddHttpLogging(x => x.LogRequestStart = true), + static x => x.AddHttpLoggingRedaction().AddHttpLogging(x => x.CombineLogs = false), async static (logCollector, client) => { using var firstResponse = await client.DeleteAsync("/").ConfigureAwait(false); @@ -576,7 +513,7 @@ async static (logCollector, client) => await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout, expectedRecords: 4); var logRecords = logCollector.GetSnapshot(); - Assert.Equal(4, logRecords.Count); + Assert.Equal(6, logRecords.Count); Assert.All(logRecords, x => Assert.Null(x.Exception)); Assert.All(logRecords, x => Assert.Equal(LogLevel.Information, x.Level)); Assert.All(logRecords, x => Assert.Equal(LoggingCategory, x.Category)); @@ -586,49 +523,55 @@ async static (logCollector, client) => var secondRecord = logRecords[1].StructuredState; var thirdRecord = logRecords[2].StructuredState; var fourthRecord = logRecords[3].StructuredState; + var fithRecord = logRecords[4].StructuredState; + var sixthRecord = logRecords[5].StructuredState; - Assert.Equal(4, firstRecord!.Count); - Assert.Equal(4, thirdRecord!.Count); + Assert.Equal(5, firstRecord!.Count); + Assert.Equal(1, secondRecord!.Count); + Assert.Equal(5, fourthRecord!.Count); + Assert.Equal(1, fithRecord!.Count); Assert.DoesNotContain(firstRecord, x => x.Key == HttpLoggingTagNames.StatusCode); Assert.DoesNotContain(firstRecord, x => x.Key == HttpLoggingTagNames.Duration); - Assert.DoesNotContain(thirdRecord, x => x.Key == HttpLoggingTagNames.StatusCode); - Assert.DoesNotContain(thirdRecord, x => x.Key == HttpLoggingTagNames.Duration); + Assert.DoesNotContain(secondRecord, x => x.Key == HttpLoggingTagNames.Duration); + Assert.DoesNotContain(fourthRecord, x => x.Key == HttpLoggingTagNames.StatusCode); + Assert.DoesNotContain(fourthRecord, x => x.Key == HttpLoggingTagNames.Duration); + Assert.DoesNotContain(fithRecord, x => x.Key == HttpLoggingTagNames.Duration); - Assert.Equal(6, secondRecord!.Count); - Assert.Equal(6, fourthRecord!.Count); + Assert.Equal(1, secondRecord!.Count); + Assert.Equal(1, fithRecord!.Count); Assert.Single(secondRecord, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); - Assert.Single(secondRecord, x => x.Key == HttpLoggingTagNames.Duration && x.Value != null); - Assert.Single(fourthRecord, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); - Assert.Single(fourthRecord, x => x.Key == HttpLoggingTagNames.Duration && x.Value != null); + Assert.Single(fithRecord, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); + + Assert.Equal(2, thirdRecord!.Count); + Assert.Equal(2, sixthRecord!.Count); + Assert.Single(thirdRecord, x => x.Key == HttpLoggingTagNames.Duration && x.Value != null); + Assert.Single(sixthRecord, x => x.Key == HttpLoggingTagNames.Duration && x.Value != null); }); } [Theory] - [InlineData("/error", "0")] + [InlineData("/error", "200")] [InlineData("/error?status=1", "400")] public async Task HttpLogging_WhenException_LogError(string requestPath, string expectedStatus) { await RunAsync( LogLevel.Information, - static services => services.AddHttpLogging(), + static services => services.AddHttpLoggingRedaction(), async (logCollector, client) => { var ex = await Assert.ThrowsAsync(() => client.GetAsync(requestPath)); Assert.Equal("Test exception", ex.Message); Assert.Equal(1, logCollector.Count); - Assert.Equal(LogLevel.Error, logCollector.LatestRecord.Level); + Assert.Equal(LogLevel.Information, logCollector.LatestRecord.Level); Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category); - Assert.NotNull(logCollector.LatestRecord.Exception); - Assert.Same(ex, logCollector.LatestRecord.Exception); + Assert.Null(logCollector.LatestRecord.Exception); var state = logCollector.LatestRecord.StructuredState; - Assert.Equal(6, state!.Count); + Assert.Equal(7, state!.Count); Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix)); Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix)); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.RequestBody); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Get.ToString()); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value)); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Path && x.Value == TelemetryConstants.Unknown); @@ -646,10 +589,9 @@ await RunAsync( LogLevel.Information, static services => services.AddHttpLogging(static x => { - x.RequestBodyContentTypes.Add(MediaTypeNames.Text.Plain); - x.ResponseBodyContentTypes.Add(MediaTypeNames.Text.Plain); - x.LogBody = true; - }), + x.MediaTypeOptions.AddText(MediaTypeNames.Text.Plain); + x.LoggingFields |= HttpLoggingFields.RequestBody | HttpLoggingFields.ResponseBody; + }).AddHttpLoggingRedaction(), async (logCollector, client) => { const string Content = "Client: hello!"; @@ -662,14 +604,13 @@ await RunAsync( Assert.Equal("Test exception", originalException!.Message); Assert.Equal(1, logCollector.Count); - Assert.Equal(LogLevel.Error, logCollector.LatestRecord.Level); + Assert.Equal(LogLevel.Information, logCollector.LatestRecord.Level); Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category); - Assert.NotNull(logCollector.LatestRecord.Exception); - Assert.Same(originalException, logCollector.LatestRecord.Exception); + Assert.Null(logCollector.LatestRecord.Exception); var state = logCollector.LatestRecord.StructuredState; - Assert.Equal(8, state!.Count); + Assert.Equal(10, state!.Count); Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix)); Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix)); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Put.ToString()); @@ -683,7 +624,8 @@ public async Task HttpLogging_WhenException_DontLogResponseBody() { await RunAsync( LogLevel.Information, - static services => services.AddHttpLogging(static x => x.LogBody = false), + static services => services.AddHttpLogging(static x => x.LoggingFields &= ~HttpLoggingFields.ResponseBody) + .AddHttpLoggingRedaction(), async (logCollector, client) => { const string Content = "Client: hello!"; @@ -696,170 +638,16 @@ await RunAsync( Assert.Equal("Test exception", originalException!.Message); Assert.Equal(1, logCollector.Count); - Assert.Equal(LogLevel.Error, logCollector.LatestRecord.Level); - Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category); - Assert.NotNull(logCollector.LatestRecord.Exception); - Assert.Same(originalException, logCollector.LatestRecord.Exception); - - var state = logCollector.LatestRecord.StructuredState; - - Assert.Equal(6, state!.Count); - Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix)); - Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix)); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Put.ToString()); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.RequestBody); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody); - }); - } - - [Fact] - public async Task HttpLogging_WhenRequestBodyReadTimeout_LogException() - { - await RunAsync( - LogLevel.Information, - static services => services.AddHttpLogging(static x => - { - x.RequestBodyContentTypes.Add(MediaTypeNames.Text.Plain); - x.LogBody = true; - }), - async (logCollector, client, serviceProvider) => - { - using var stream = new InfiniteStream('A'); - using var content = new StreamContent(stream); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Text.Plain); - - using var cts = new CancellationTokenSource(); - var pipeMiddleware = serviceProvider.GetRequiredService(); - pipeMiddleware.RequestBodyInfinitePipeFeatureCallback = cts.Cancel; - - var ex = await Assert.ThrowsAnyAsync(() => client.PutAsync("/infinite-pipe", content, cts.Token)); - Assert.NotNull(ex); - - await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); - - Assert.Equal(1, logCollector.Count); - Assert.Equal(LogLevel.Error, logCollector.LatestRecord.Level); + Assert.Equal(LogLevel.Information, logCollector.LatestRecord.Level); Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category); - Assert.NotNull(logCollector.LatestRecord.Exception); - Assert.IsType(logCollector.LatestRecord.Exception); + Assert.Null(logCollector.LatestRecord.Exception); var state = logCollector.LatestRecord.StructuredState; - Assert.Equal(6, state!.Count); + Assert.Equal(7, state!.Count); Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix)); Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix)); Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Put.ToString()); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == "0"); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.RequestBody); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody); - }, - configureMiddleware: static x => - { - x.BodyReadSizeLimit = int.MaxValue; - x.RequestBodyReadTimeout = Timeout.InfiniteTimeSpan; - }); - } - - [Fact] - public async Task HttpLogging_WhenRequestBodyReadError_LogException() - { - await RunAsync( - LogLevel.Information, - static services => services.AddHttpLogging(static x => - { - x.RequestBodyContentTypes.Add(MediaTypeNames.Text.Plain); - x.LogBody = true; - }), - async (logCollector, client) => - { - const string Content = "Client: hello!"; - - using var content = new StringContent(Content); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Text.Plain); - - using var response = await client.PutAsync("/err-pipe", content); - - await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout, expectedRecords: 2); - - var records = logCollector.GetSnapshot(); - Assert.Equal(2, records.Count); - var firstRecord = records[0]; - var secondRecord = records[1]; - - Assert.Equal(LogLevel.Error, firstRecord.Level); - Assert.All(records, x => Assert.Equal(LoggingCategory, x.Category)); - Assert.NotNull(firstRecord.Exception); - Assert.Equal(Log.ReadingRequestBodyError, firstRecord.Message); - Assert.Equal(RequestBodyErrorPipeFeature.ErrorMessage, firstRecord.Exception!.Message); - - var state = secondRecord.StructuredState; - - Assert.Equal(6, state!.Count); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.RequestBody); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Put.ToString()); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == ((int)response.StatusCode).ToInvariantString()); - }); - } - - [Fact] - public async Task HttpLogging_WhenResponseBodyReadError_LogException() - { - const string ExceptionMessage = "Exception on response body intercepting"; - - static ReadOnlyMemory SyntheticInterseptingDataGetter(ResponseInterceptingStream _) - => throw new InvalidOperationException(ExceptionMessage); - - await RunAsync( - LogLevel.Information, - static services => services.AddHttpLogging(static x => - { - x.ResponseBodyContentTypes.Add(MediaTypeNames.Text.Plain); - x.LogBody = true; - }), - async (logCollector, client, _) => - { - using var response = await client.GetAsync("/"); - - await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout, expectedRecords: 2); - - var records = logCollector.GetSnapshot(); - Assert.Equal(2, records.Count); - var firstRecord = records[0]; - var secondRecord = records[1]; - - Assert.Equal(LogLevel.Error, firstRecord.Level); - Assert.All(records, x => Assert.Equal(LoggingCategory, x.Category)); - Assert.NotNull(firstRecord.Exception); - Assert.Equal(Log.ReadingResponseBodyError, firstRecord.Message); - Assert.Equal(ExceptionMessage, firstRecord.Exception!.Message); - - var state = secondRecord.StructuredState; - - Assert.Equal(6, state!.Count); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.RequestBody); - Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Get.ToString()); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == ((int)response.StatusCode).ToInvariantString()); - }, - configureMiddleware: static x => x.GetResponseBodyInterceptedData = SyntheticInterseptingDataGetter); - } - - [Fact] - public async Task HttpLogging_WhenLogLevelWarning_NoLogHttp_ButWarning() - { - await RunAsync( - LogLevel.Warning, - static _ => { }, - static (logCollector, _) => - { - Assert.Equal(1, logCollector.Count); - - var latestRecord = logCollector.LatestRecord; - Assert.Equal(LoggingCategory, latestRecord.Category); - Assert.Equal(LogLevel.Warning, latestRecord.Level); - Assert.StartsWith("HttpLogging middleware is injected into application pipeline, but LogLevel", latestRecord.Message); - return Task.CompletedTask; }); } @@ -892,11 +680,11 @@ async static (logCollector, client) => [InlineData("/", "/home", false)] [InlineData("", "/home", false)] [InlineData("/home", "/", true)] - public async Task HttpLogging_LogRecordIsNotCreated_If_isFilterd_True(string httpPath, string excludedPath, bool isFiltered) + public async Task HttpLogging_LogRecordIsNotCreated_If_isFiltered_True(string httpPath, string excludedPath, bool isFiltered) { await RunAsync( LogLevel.Information, - services => services.AddHttpLogging(x => + services => services.AddHttpLoggingRedaction(x => { x.ExcludePathStartsWith.Add(excludedPath); }), @@ -914,8 +702,53 @@ await RunAsync( { await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); Assert.Equal(1, logCollector.Count); - Assert.Equal(6, logCollector.GetSnapshot()[0].StructuredState!.Count); + Assert.Equal(7, logCollector.GetSnapshot()[0].StructuredState!.Count); } }); } + + [Fact] + public async Task HttpLogging_LogRecordIsNotCreated_If_Disabled() + { + await RunAsync( + LogLevel.Information, + services => services.AddHttpLoggingRedaction(x => + { + }).AddHttpLogging(o => + { + o.LoggingFields = HttpLoggingFields.None; + }), + async (logCollector, client) => + { + using var response = await client.GetAsync("").ConfigureAwait(false); + + Assert.True(response.IsSuccessStatusCode); + Assert.Equal(0, logCollector.Count); + }); + } + + [Fact] + public async Task HttpLogging_EnricherThrows_Logged() + { + await RunAsync( + LogLevel.Debug, + services => services.AddHttpLoggingRedaction() + .AddHttpLogEnricher(), + async (logCollector, client) => + { + using var response = await client.GetAsync("").ConfigureAwait(false); + + Assert.True(response.IsSuccessStatusCode); + await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); + Assert.Equal(2, logCollector.Count); + Assert.IsType(logCollector.GetSnapshot()[0].Exception); + Assert.Equal(7, logCollector.GetSnapshot()[1].StructuredState!.Count); + }); + } + + private class ThrowingEnricher : IHttpLogEnricher + { + public void Enrich(IEnrichmentTagCollector collector, HttpContext httpContext) => throw new InvalidOperationException(); + } } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ApiRoutingController.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ApiRoutingController.cs index 9527508b7bb..19c4d6fd009 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ApiRoutingController.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ApiRoutingController.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; using System.Diagnostics; using Microsoft.AspNetCore.Http; @@ -26,10 +28,10 @@ public ActionResult GetUser( Debug.Assert(noRedaction != null, "Test"); // Request processing imitation: - var middleware = HttpContext.RequestServices.GetRequiredService(); - var fakeTimeProvider = (FakeTimeProvider)middleware.TimeProvider; + var fakeTimeProvider = HttpContext.RequestServices.GetRequiredService(); fakeTimeProvider.Advance(TimeSpan.FromMilliseconds(AcceptanceTests.ControllerProcessingTimeMs)); return "User info..."; } } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AttributeRoutingController.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AttributeRoutingController.cs index 421eee02b93..9e343737107 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AttributeRoutingController.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AttributeRoutingController.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.Compliance.Testing; @@ -26,3 +28,4 @@ public class AttributeRoutingController : Controller [HttpGet("get-4/{param=all}")] public IActionResult GetWithUnclassifiedParam(string param) => Ok(param); } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ConventionalRoutingController.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ConventionalRoutingController.cs index 5d9741e0bd4..c67110ef12f 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ConventionalRoutingController.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ConventionalRoutingController.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Compliance.Testing; @@ -16,3 +18,4 @@ public class ConventionalRoutingController : Controller public IActionResult GetData(int param) => Ok(param); } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/CustomHttpLogEnricher.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/CustomHttpLogEnricher.cs index ab31af237b8..8712b67970b 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/CustomHttpLogEnricher.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/CustomHttpLogEnricher.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Diagnostics.Enrichment; @@ -14,9 +16,10 @@ internal sealed class CustomHttpLogEnricher : IHttpLogEnricher internal const string Key2 = "key2"; internal const double Value2 = 2; - public void Enrich(IEnrichmentTagCollector collector, HttpRequest request, HttpResponse response) + public void Enrich(IEnrichmentTagCollector collector, HttpContext httpContext) { collector.Add(Key1, Value1); collector.Add(Key2, Value2); } } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs index d1e771cfeea..b3af0edffae 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HeaderReaderTests.cs @@ -1,6 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - +#if NET8_0_OR_GREATER using System.Collections.Generic; using System.Net.Mime; using Microsoft.AspNetCore.Http; @@ -18,9 +18,9 @@ public class HeaderReaderTests public void ShouldNotAddHeaders_WhenFilteringSetEmpty() { var reader = new HeaderReader(new Dictionary(), null!); - var listToFill = new List>(); + var listToFill = new List>(); var headers = new HeaderDictionary(new Dictionary { [HeaderNames.Accept] = MediaTypeNames.Text.Plain }); - reader.Read(headers, listToFill); + reader.Read(headers, listToFill, string.Empty); Assert.Empty(listToFill); } @@ -28,8 +28,8 @@ public void ShouldNotAddHeaders_WhenFilteringSetEmpty() public void ShouldNotAddHeaders_WhenHeadersCollectionEmpty() { var reader = new HeaderReader(new Dictionary { [HeaderNames.Accept] = DataClassification.Unknown }, null!); - var listToFill = new List>(); - reader.Read(new HeaderDictionary(), listToFill); + var listToFill = new List>(); + reader.Read(new HeaderDictionary(), listToFill, string.Empty); Assert.Empty(listToFill); } @@ -44,8 +44,8 @@ public void ShouldAddHeaders_WhenHeadersCollectionNotEmpty() [HeaderNames.ContentType] = MediaTypeNames.Application.Pdf }; - var listToFill = new List>(); - reader.Read(new HeaderDictionary(headers), listToFill); + var listToFill = new List>(); + reader.Read(new HeaderDictionary(headers), listToFill, string.Empty); Assert.Single(listToFill); @@ -54,3 +54,4 @@ public void ShouldAddHeaders_WhenHeadersCollectionNotEmpty() Assert.Equal($"", redacted.Value); } } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpLoggingServiceExtensionsTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpLoggingServiceExtensionsTests.cs index b4f2c6d624a..4f9eb15ca78 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpLoggingServiceExtensionsTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpLoggingServiceExtensionsTests.cs @@ -1,18 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; -#if FIXME -using System.Net.Mime; -using Microsoft.Extensions.Compliance.Classification; -#endif +using System.Collections.Generic; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -#if FIXME +using Microsoft.Extensions.Http.Diagnostics; using Microsoft.Extensions.Options; -using Microsoft.Net.Http.Headers; -using Microsoft.Extensions.Telemetry; -#endif using Moq; using Xunit; @@ -25,44 +21,34 @@ public void ShouldThrow_WhenArgsNull() { var services = Mock.Of(); - Assert.Throws(static () => HttpLoggingServiceCollectionExtensions.AddHttpLogging(null!)); Assert.Throws(static () => HttpLoggingServiceCollectionExtensions.AddHttpLogEnricher(null!)); - Assert.Throws( - () => HttpLoggingServiceCollectionExtensions.AddHttpLogging(services, (Action)null!)); Assert.Throws( - () => HttpLoggingServiceCollectionExtensions.AddHttpLogging(services, (IConfigurationSection)null!)); + () => HttpLoggingServiceCollectionExtensions.AddHttpLoggingRedaction(services, (IConfigurationSection)null!)); } -#if FIXME [Fact] public void AddHttpLogging_WhenConfiguredUsingConfigurationSection_IsCorrect() { var services = new ServiceCollection(); - var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json"); + var builder = new ConfigurationBuilder().AddInMemoryCollection(new[] + { + new KeyValuePair("HttpLogging:RequestPathLoggingMode", "Structured"), + new KeyValuePair("HttpLogging:RequestPathParameterRedactionMode","None"), + new KeyValuePair("HttpLogging:ExcludePathStartsWith:[0]","/path0toexclude"), + new KeyValuePair("HttpLogging:ExcludePathStartsWith:[1]","/path1toexclude"), + }); var configuration = builder.Build(); - - services.AddHttpLogging(configuration.GetSection("HttpLogging")); + services.AddHttpLoggingRedaction(configuration.GetSection("HttpLogging")); using var provider = services.BuildServiceProvider(); - var options = provider.GetRequiredService>().Value; + var options = provider.GetRequiredService>().Value; - Assert.True(options.LogRequestStart); - Assert.True(options.RequestPathParameterRedactionMode == HttpRouteParameterRedactionMode.None); - Assert.Equal(64 * 1024, options.BodySizeLimit); - Assert.Equal(TimeSpan.FromSeconds(5), options.RequestBodyReadTimeout); Assert.Equal(IncomingPathLoggingMode.Structured, options.RequestPathLoggingMode); Assert.Equal(HttpRouteParameterRedactionMode.None, options.RequestPathParameterRedactionMode); - Assert.Collection(options.RequestHeadersDataClasses, static x => Assert.Equal(HeaderNames.Accept, x.Key)); - Assert.Collection(options.RequestHeadersDataClasses, static x => Assert.Equal(DataClassification.None, x.Value)); - Assert.Collection(options.ResponseHeadersDataClasses, static x => Assert.Equal(HeaderNames.ContentType, x.Key)); - Assert.Collection(options.ResponseHeadersDataClasses, static x => Assert.Equal(SimpleClassifications.PrivateData, x.Value)); - Assert.Collection(options.RequestBodyContentTypes, static x => Assert.Equal(MediaTypeNames.Text.Plain, x)); - Assert.Collection(options.ResponseBodyContentTypes, static x => Assert.Equal(MediaTypeNames.Application.Json, x)); - Assert.Equal(2, options.RouteParameterDataClasses.Count); - Assert.Contains(options.RouteParameterDataClasses, static x => x.Key == "userId" && x.Value == DataClass.EUII); - Assert.Contains(options.RouteParameterDataClasses, static x => x.Key == "userContent" && x.Value == SimpleClassifications.PrivateData); + Assert.Contains("/path0toexclude", options.ExcludePathStartsWith); + Assert.Contains("/path1toexclude", options.ExcludePathStartsWith); } -#endif } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpRequestBodyReaderTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpRequestBodyReaderTests.cs deleted file mode 100644 index 52a0991c933..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/HttpRequestBodyReaderTests.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Xunit; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -public class HttpRequestBodyReaderTests -{ - [Fact] - public async Task Should_ThrowOnCancellation() - { - var context = new DefaultHttpContext(); - using var stream = CreateMemoryStream("test"); - context.Request.Body = stream; - context.Request.ContentType = "text/plain"; - - var ex = await Assert.ThrowsAsync( - async () => - await HttpRequestBodyReader.ReadBodyAsync(context.Request, TimeSpan.FromMinutes(100), int.MaxValue, new(canceled: true))); - - Assert.True(ex.CancellationToken.IsCancellationRequested); - Assert.Equal(0, stream.Position); - } - - [Fact] - public async Task FormatAsync_WhenTimeoutReading_ErrorMessage() - { - var context = new DefaultHttpContext(); - using var stream = new InfiniteStream('A'); - context.Request.Body = stream; - context.Request.ContentType = "text/plain"; - - using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(100)); - - var body = await HttpRequestBodyReader.ReadBodyAsync(context.Request, TimeSpan.FromMilliseconds(100), int.MaxValue, cts.Token); - - Assert.Equal(HttpRequestBodyReader.ReadCancelled, Encoding.UTF8.GetString(body.FirstSpan)); - Assert.Equal(0, stream.Position); - } - - private static MemoryStream CreateMemoryStream(string value) - => new(Encoding.UTF8.GetBytes(value)); -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingHttpDimensionsTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingHttpDimensionsTests.cs index f0db4c331d7..76a798436ec 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingHttpDimensionsTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingHttpDimensionsTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System.Collections.Generic; using Xunit; @@ -18,3 +20,4 @@ public void Should_ReturnList_AllDimensions() Assert.Equal(names.Count, dimensions.Count); } } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestLogRecordTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestLogRecordTests.cs deleted file mode 100644 index 82b108ddd40..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestLogRecordTests.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Xunit; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -public class IncomingRequestLogRecordTests -{ - [Fact] - public void ShouldInitializeProps() - { - // This is kill Stryker's mutants: - var logRecord = new IncomingRequestLogRecord(); - Assert.Empty(logRecord.Host); - Assert.Empty(logRecord.Method); - Assert.Empty(logRecord.Path); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestStructTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestStructTests.cs deleted file mode 100644 index 1e958c4aa43..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/IncomingRequestStructTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.Collections.Generic; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -public class IncomingRequestStructTests -{ - [Fact] - public void Should_ReturnEnumerator_WithElements() - { - var helper = LogMethodHelper.GetHelper(); - helper.Add("prop1", "value1"); - helper.Add("prop_2", "value_2"); - - var reqStruct = new Log.IncomingRequestStruct(helper); - var enumerable = (IEnumerable)reqStruct; - var list = new List(); - foreach (var item in enumerable) - { - list.Add(item); - } - - Assert.Collection(list, - x => Assert.Equal(helper[0], x), - x => Assert.Equal(helper[1], x)); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/InfiniteStream.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/InfiniteStream.cs deleted file mode 100644 index a0e85cb6a41..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/InfiniteStream.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -internal sealed class InfiniteStream : Stream -{ - private readonly byte _charToFill; - - public InfiniteStream(char charToFill) - { - _charToFill = Convert.ToByte(charToFill); - } - - public override bool CanRead => true; - - public override bool CanSeek => true; - - public override bool CanWrite => false; - - public override long Length => long.MaxValue; - - public override long Position { get; set; } - - public override void Flush() - { - // empty by design - } - - public override int Read(byte[] buffer, int offset, int count) - { - for (int i = 0; i < count; i++) - { - buffer[i + offset] = _charToFill; - } - - return count; - } - - public override long Seek(long offset, SeekOrigin origin) - => offset; - - public override void SetLength(long value) - => throw new NotSupportedException(); - - public override void Write(byte[] buffer, int offset, int count) - => throw new NotSupportedException(); -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingMiddlewareTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingMiddlewareTests.cs deleted file mode 100644 index 2d71158586c..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingMiddlewareTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Threading; -using Microsoft.Extensions.Compliance.Redaction; -using Microsoft.Extensions.Http.Diagnostics; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Shared.Diagnostics; -using Moq; -using Xunit; -using IOptionsFactory = Microsoft.Extensions.Options.Options; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -public class LoggingMiddlewareTests -{ - [Fact] - public void HttpLoggingMiddleware_While_Having_Attached_Debugger_Has_Infinite_Timeout_For_Reading_A_Body() - { - var middleware = new HttpLoggingMiddleware( - options: IOptionsFactory.Create(new LoggingOptions()), - logger: NullLogger.Instance, - httpLogEnrichers: Array.Empty(), - httpRouteParser: new Mock().Object, - httpRouteFormatter: new Mock().Object, - redactorProvider: NullRedactorProvider.Instance, - httpRouteUtility: new Mock().Object, - debugger: DebuggerState.Attached); - - Assert.Equal(middleware.RequestBodyReadTimeout, Timeout.InfiniteTimeSpan); - } - - [Fact] - public void HttpLoggingMiddleware_While_Having_Attached_Detached_Has_Timeout_Set_By_Options_For_Reading_A_Body() - { - var options = new LoggingOptions(); - - var middleware = new HttpLoggingMiddleware( - options: IOptionsFactory.Create(options), - logger: NullLogger.Instance, - httpLogEnrichers: Array.Empty(), - httpRouteParser: new Mock().Object, - httpRouteFormatter: new Mock().Object, - redactorProvider: NullRedactorProvider.Instance, - httpRouteUtility: new Mock().Object, - debugger: DebuggerState.Detached); - - Assert.Equal(middleware.RequestBodyReadTimeout, options.RequestBodyReadTimeout); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingOptionsValidationTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingOptionsValidationTests.cs index 03c80eca1a2..d18ff2a4512 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingOptionsValidationTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/LoggingOptionsValidationTests.cs @@ -1,13 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using System; -using System.Collections.Generic; +using Microsoft.AspNetCore.HttpLogging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Http.Diagnostics; -using Microsoft.Extensions.Options; using Xunit; -using TOpt = System.Action; namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; @@ -22,11 +22,11 @@ public void Should_Throw_OnInvalidPathLoggingMode(int mode) using var services = new ServiceCollection() .AddLogging() .AddFakeRedaction() - .AddHttpLogging(x => x.RequestPathLoggingMode = (IncomingPathLoggingMode)mode) + .AddHttpLoggingRedaction(x => x.RequestPathLoggingMode = (IncomingPathLoggingMode)mode) .BuildServiceProvider(); var ex = Assert.Throws( - () => services.GetRequiredService()); + () => services.GetRequiredService()); Assert.Equal($"Unsupported value '{mode}' for enum type 'IncomingPathLoggingMode'", ex.Message); } @@ -38,11 +38,11 @@ public void Should_NotThrow_OnValidPathLoggingMode(IncomingPathLoggingMode mode) using var services = new ServiceCollection() .AddLogging() .AddFakeRedaction() - .AddHttpLogging(x => x.RequestPathLoggingMode = mode) + .AddHttpLoggingRedaction(x => x.RequestPathLoggingMode = mode) .BuildServiceProvider(); var ex = Record.Exception( - () => services.GetRequiredService()); + () => services.GetRequiredService()); Assert.Null(ex); } @@ -54,50 +54,13 @@ public void Should_NotThrow_OnValidPathParameterRedactionMode(HttpRouteParameter using var services = new ServiceCollection() .AddLogging() .AddFakeRedaction() - .AddHttpLogging(x => x.RequestPathParameterRedactionMode = mode) + .AddHttpLoggingRedaction(x => x.RequestPathParameterRedactionMode = mode) .BuildServiceProvider(); var ex = Record.Exception( - () => services.GetRequiredService()); + () => services.GetRequiredService()); Assert.Null(ex); } - - [Theory] - [MemberData(nameof(OptionsConfigureActions))] - public void Should_Throw_OnInvalidOption(string fieldName, TOpt configure) - { - using var services = new ServiceCollection() - .AddLogging() - .AddFakeRedaction() - .AddHttpLogging(configure) - .BuildServiceProvider(); - - var ex = Assert.Throws( - () => services.GetRequiredService()); - - Assert.Contains($"{nameof(LoggingOptions)}.{fieldName}", ex.Message); - } - - public static IEnumerable OptionsConfigureActions => - new List - { - new object[] { "BodySizeLimit", (TOpt) (x => x.BodySizeLimit = 0) }, - new object[] { "BodySizeLimit", (TOpt) (x => x.BodySizeLimit = -1) }, - new object[] { "BodySizeLimit", (TOpt) (x => x.BodySizeLimit = 1_572_865) }, - new object[] { "BodySizeLimit", (TOpt) (x => x.BodySizeLimit = int.MaxValue) }, - new object[] { "BodySizeLimit", (TOpt) (x => x.BodySizeLimit = int.MinValue) }, - new object[] { "RequestBodyReadTimeout", (TOpt) (x => x.RequestBodyReadTimeout = TimeSpan.Zero) }, - new object[] { "RequestBodyReadTimeout", (TOpt) (x => x.RequestBodyReadTimeout = TimeSpan.FromMilliseconds(-1)) }, - new object[] { "RequestBodyReadTimeout", (TOpt) (x => x.RequestBodyReadTimeout = TimeSpan.FromMilliseconds(60_001)) }, - new object[] { "RequestBodyReadTimeout", (TOpt) (x => x.RequestBodyReadTimeout = TimeSpan.FromMinutes(2)) }, - new object[] { "RequestBodyReadTimeout", (TOpt) (x => x.RequestBodyReadTimeout = TimeSpan.MaxValue) }, - new object[] { "RequestBodyReadTimeout", (TOpt) (x => x.RequestBodyReadTimeout = TimeSpan.MinValue) }, - new object[] { "RequestBodyContentTypes", (TOpt) (x => x.RequestBodyContentTypes = null!) }, - new object[] { "RequestHeadersDataClasses", (TOpt) (x => x.RequestHeadersDataClasses = null!) }, - new object[] { "ResponseBodyContentTypes", (TOpt) (x => x.ResponseBodyContentTypes = null!) }, - new object[] { "ResponseHeadersDataClasses", (TOpt) (x => x.ResponseHeadersDataClasses = null!) }, - new object[] { "RouteParameterDataClasses", (TOpt) (x => x.RouteParameterDataClasses = null!) }, - new object[] { "ExcludePathStartsWith", (TOpt) ( x=> x.ExcludePathStartsWith = null!) } - }; } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MediaTypeSetExtensionsTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MediaTypeSetExtensionsTests.cs deleted file mode 100644 index 4deaef3e896..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MediaTypeSetExtensionsTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Mvc.Formatters; -using Xunit; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -public class MediaTypeSetExtensionsTests -{ - [Fact] - public void Covers_WhenCovers_ReturnsTrue() - { - var collection = new[] - { - new MediaType("application/xml"), - new MediaType("text/*") - }; - - Assert.True(collection.Covers("application/xml")); - Assert.True(collection.Covers("text/whatever")); - Assert.True(collection.Covers("text/whatever-else")); - } - - [Fact] - public void Covers_WhenNotCovers_ReturnsFalse() - { - var collection = new[] - { - new MediaType("application/xml"), - new MediaType("text/*") - }; - - Assert.False(collection.Covers(null)); - Assert.False(collection.Covers(string.Empty)); - Assert.False(collection.Covers("image")); - Assert.False(collection.Covers("image/png")); - Assert.False(collection.Covers("audio/ogg")); - Assert.False(collection.Covers("application")); - Assert.False(collection.Covers("application/octet-stream")); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MixedRoutingController.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MixedRoutingController.cs index e4887e0aabf..ba488909a56 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MixedRoutingController.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/MixedRoutingController.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.Compliance.Testing; @@ -23,3 +25,4 @@ public class MixedRoutingController : Controller [HttpGet("mixed/attribute-routing-4/{param=all}")] public IActionResult AttributeRoutingWithUnclassifiedParam(string param) => Ok(param); } +#endif diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/PipeReaderExtensionsTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/PipeReaderExtensionsTests.cs deleted file mode 100644 index ab63bf82a63..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/PipeReaderExtensionsTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.IO; -using System.IO.Pipelines; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -public class PipeReaderExtensionsTests -{ - [Theory] - [InlineData("T", 32)] - [InlineData("Hr", 2)] - [InlineData("AG8", 2)] - [InlineData("IpR7E", 5)] - [InlineData("DSbwoedf", 16)] - [InlineData("dthT18LsIaZNy", 1)] - [InlineData("ShOXqmhQyLFxW78V4DgwE", 11)] - [InlineData("FB3GefYUQxQeDiKnqtXglzd2szS2o7X6ei", 64)] - [InlineData("qREdmuDaoNmWdC2gbhD3rsRVke6FloRlw7fbM0of7d6RTEXGGc5D3HF", 64)] - public async Task ReadAsync_VariousSizesDefaultPipeReader(string content, int numBytes) - { - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)); - var pipe = PipeReader.Create(stream); - - var result = await pipe.ReadAsync(numBytes, CancellationToken.None); - - Assert.Equal( - content.Substring(0, Math.Min(content.Length, numBytes)), - Encoding.UTF8.GetString(result.ToArray())); - } - - [Theory] - [InlineData("T", 32)] - [InlineData("Hr", 2)] - [InlineData("AG8", 2)] - [InlineData("IpR7E", 5)] - [InlineData("DSbwoedf", 16)] - [InlineData("dthT18LsIaZNy", 1)] - [InlineData("ShOXqmhQyLFxW78V4DgwE", 11)] - [InlineData("FB3GefYUQxQeDiKnqtXglzd2szS2o7X6ei", 64)] - [InlineData("qREdmuDaoNmWdC2gbhD3rsRVke6FloRlw7fbM0of7d6RTEXGGc5D3HF", 64)] - public async Task ReadAsync_VariousSizesSmallBuffersPipeReader(string content, int numBytes) - { - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)); - var options = new StreamPipeReaderOptions(bufferSize: 16, minimumReadSize: 16); - var pipe = PipeReader.Create(stream, options); - - var result = await pipe.ReadAsync(numBytes, CancellationToken.None); - - Assert.Equal( - content.Substring(0, Math.Min(content.Length, numBytes)), - Encoding.UTF8.GetString(result.ToArray())); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyErrorPipeFeature.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyErrorPipeFeature.cs deleted file mode 100644 index afe3bcedc0d..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyErrorPipeFeature.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO.Pipelines; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -internal sealed class RequestBodyErrorPipeFeature : IRequestBodyPipeFeature -{ - internal const string ErrorMessage = "TestPipeReader synthetic error"; - - public PipeReader Reader => new ErrorPipeReader(); - - private sealed class ErrorPipeReader : PipeReader - { - public override void AdvanceTo(SequencePosition consumed) => throw new InvalidOperationException(ErrorMessage); - public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) => throw new InvalidOperationException(ErrorMessage); - public override void CancelPendingRead() => throw new InvalidOperationException(ErrorMessage); - public override void Complete(Exception? exception = null) => throw new InvalidOperationException(ErrorMessage); - public override ValueTask ReadAsync(CancellationToken cancellationToken = default) => throw new InvalidOperationException(ErrorMessage); - public override bool TryRead(out ReadResult result) => throw new InvalidOperationException(ErrorMessage); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyInfinitePipeFeature.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyInfinitePipeFeature.cs deleted file mode 100644 index 3e16c637d20..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyInfinitePipeFeature.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO.Pipelines; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -internal sealed class RequestBodyInfinitePipeFeature : IRequestBodyPipeFeature -{ - private readonly Action? _requestBodyStartReadingCallback; - - public RequestBodyInfinitePipeFeature(Action? requestBodyStartReadingCallback) - { - _requestBodyStartReadingCallback = requestBodyStartReadingCallback; - } - - public PipeReader Reader => new InfinitePipeReader(_requestBodyStartReadingCallback); - - private class InfinitePipeReader : PipeReader - { - private Action? _requestBodyStartReadingCallback; - - public InfinitePipeReader(Action? requestBodyStartReadingCallback) - { - _requestBodyStartReadingCallback = requestBodyStartReadingCallback; - } - - public override async ValueTask ReadAsync(CancellationToken cancellationToken = default) - { - _requestBodyStartReadingCallback?.Invoke(); - await Task.Delay(Timeout.Infinite, cancellationToken).ConfigureAwait(false); - return default; - } - - public override void AdvanceTo(SequencePosition consumed) => throw new InvalidOperationException(); - public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) => throw new InvalidOperationException(); - public override void CancelPendingRead() => throw new InvalidOperationException(); - public override void Complete(Exception? exception = null) => throw new InvalidOperationException(); - public override bool TryRead(out ReadResult result) => throw new InvalidOperationException(); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyMultiSegmentPipeFeature.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyMultiSegmentPipeFeature.cs deleted file mode 100644 index 2faa1be963a..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestBodyMultiSegmentPipeFeature.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.IO.Pipelines; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -internal sealed class RequestBodyMultiSegmentPipeFeature : IRequestBodyPipeFeature -{ - public PipeReader Reader => new ErrorPipeReader(); - - private sealed class ErrorPipeReader : PipeReader - { - private readonly ReadOnlySequence _buffer; - private bool _isFirstReturn = true; - - public ErrorPipeReader() - { - var memory = new byte[] { 84, 101, 115, 116, 32, 83, 101, 103, 109, 101, 110, 116 }; // "Test Segment" - - var secondSegment = new SequenceSegment(memory, null, memory.Length); - var firstSegment = new SequenceSegment(memory, secondSegment, 0); - - _buffer = new ReadOnlySequence(firstSegment, 0, secondSegment, memory.Length); - } - - public override void CancelPendingRead() => throw new NotSupportedException(); - public override void Complete(Exception? exception = null) => throw new NotSupportedException(); - public override bool TryRead(out ReadResult result) => throw new NotSupportedException(); - public override void AdvanceTo(SequencePosition consumed) => throw new NotSupportedException(); - public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) - { - // do nothing - } - - public override ValueTask ReadAsync(CancellationToken cancellationToken = default) - { - var result = new ReadResult(_buffer, isCanceled: false, isCompleted: !_isFirstReturn); - - _isFirstReturn = false; - return new ValueTask(result); - } - - private class SequenceSegment : ReadOnlySequenceSegment - { - public SequenceSegment(ReadOnlyMemory memory, ReadOnlySequenceSegment? next, long runningIndex) - { - Memory = memory; - Next = next; - RunningIndex = runningIndex; - } - } - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherExtensionsTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherExtensionsTests.cs index 5311bceb2ef..23d1d2bd715 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherExtensionsTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherExtensionsTests.cs @@ -3,11 +3,9 @@ using System; using Microsoft.Extensions.Compliance.Classification; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.Enrichment; using Microsoft.Extensions.Options; -using Moq; using Xunit; namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; @@ -24,13 +22,7 @@ public void RequestHeadersLogEnricher_GivenAnyNullArgument_Throws() ((IServiceCollection)null!).AddRequestHeadersLogEnricher(_ => { })); Assert.Throws(() => - ((IServiceCollection)null!).AddRequestHeadersLogEnricher(Mock.Of())); - - Assert.Throws(() => - new ServiceCollection().AddRequestHeadersLogEnricher((IConfigurationSection)null!)); - - Assert.Throws(() => - new ServiceCollection().AddRequestHeadersLogEnricher((Action)null!)); + new ServiceCollection().AddRequestHeadersLogEnricher(null!)); } [Fact] @@ -43,16 +35,6 @@ public void RequestHeadersLogEnricher_GivenOptions_HeaderKeysWithDataClass_NoRed Assert.Throws(() => sp.GetRequiredService()); } - [Fact] - public void RequestHeadersLogEnricher_GivenInvalidOptions_HeaderKeysWithDataClass_Throws() - { - using var sp = new ServiceCollection() - .AddRequestHeadersLogEnricher(e => e.HeadersDataClasses = null!) - .BuildServiceProvider(); - - Assert.Throws(() => sp.GetRequiredService>().Value); - } - [Fact] public void RequestHeadersLogEnricher_GivenNoArguments_WithRedaction_RegistersInDI() { @@ -85,34 +67,4 @@ public void RequestHeadersLogEnricher_GivenHeaderKeysWithDataClassAndRedaction_R Assert.NotNull(options.HeadersDataClasses); Assert.Single(options.HeadersDataClasses); } - -#if FIXME - [Fact] - public void RequestHeadersLogEnricher_GivenConfigurationSectionAndRedaction_RegistersInDI() - { - var dc = new DataClassification("TAX", 1); - - // Arrange - var configRoot = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary { ["Section:HeadersDataClasses:TestKey"] = "SimpleClassifications.PrivateData" }) - .Build(); - - // Act - using var serviceProvider = new ServiceCollection() - .AddRequestHeadersLogEnricher(configRoot.GetSection("Section")) - .AddFakeRedaction() - .BuildServiceProvider(); - - // Assert - var enricher = serviceProvider.GetRequiredService(); - Assert.NotNull(enricher); - Assert.IsType(enricher); - - var options = serviceProvider.GetRequiredService>().Value; - Assert.NotNull(options); - Assert.NotNull(options.HeadersDataClasses); - Assert.Single(options.HeadersDataClasses); - Assert.Equal(SimpleClassifications.PrivateData, options.HeadersDataClasses["TestKey"]); - } -#endif } diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs index a0382dd6ebb..fc0fccba72d 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Diagnostics.Logging; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.Compliance.Redaction; using Microsoft.Extensions.Compliance.Testing; using Moq; @@ -54,10 +53,7 @@ public RequestHeadersEnricherTests() public void RequestHeadersEnricher_GivenDisabledEnricherOptions_HeaderKeysDataClasses_DoesNotEnrich() { // Arrange - var options = new RequestHeadersLogEnricherOptions - { - HeadersDataClasses = new Dictionary() - }; + var options = new RequestHeadersLogEnricherOptions(); var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object); var enrichedProperties = new TestLogEnrichmentTagCollector(); @@ -76,7 +72,7 @@ public void RequestHeadersEnricher_GivenEnricherOptions_HeaderKeysDataClasses_En // Arrange var options = new RequestHeadersLogEnricherOptions { - HeadersDataClasses = new Dictionary + HeadersDataClasses = { { HeaderKey1, FakeClassifications.PrivateData }, { HeaderKey4, FakeClassifications.PublicData } @@ -109,7 +105,7 @@ public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsEmpty_He // Arrange var options = new RequestHeadersLogEnricherOptions { - HeadersDataClasses = new Dictionary + HeadersDataClasses = { { HeaderKey1, FakeClassifications.PrivateData }, { HeaderKey2, FakeClassifications.PublicData } @@ -139,7 +135,7 @@ public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsNull_Hea // Arrange var options = new RequestHeadersLogEnricherOptions { - HeadersDataClasses = new Dictionary + HeadersDataClasses = { { HeaderKey1, FakeClassifications.PublicData }, { HeaderKey3, FakeClassifications.PublicData } @@ -166,7 +162,7 @@ public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsMissing_ var headerKey2 = "header_does_not_exist"; var options = new RequestHeadersLogEnricherOptions { - HeadersDataClasses = new Dictionary + HeadersDataClasses = { { HeaderKey1, FakeClassifications.PublicData }, { headerKey2, FakeClassifications.PublicData } @@ -191,7 +187,7 @@ public void RequestHeadersEnricher_GivenNullHttpContext_HeaderKeysDataClasses_Do // Arrange var options = new RequestHeadersLogEnricherOptions { - HeadersDataClasses = new Dictionary + HeadersDataClasses = { { HeaderKey1, FakeClassifications.PublicData } } @@ -218,7 +214,7 @@ public void RequestHeadersEnricher_GivenNullRequest_HeaderKeysDataClasses_DoesNo // Arrange var options = new RequestHeadersLogEnricherOptions { - HeadersDataClasses = new Dictionary + HeadersDataClasses = { { HeaderKey1, FakeClassifications.PublicData } } diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ResponseInterceptingStreamTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ResponseInterceptingStreamTests.cs deleted file mode 100644 index 94908dc4227..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/ResponseInterceptingStreamTests.cs +++ /dev/null @@ -1,204 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Shared.Pools; -using Moq; -using Xunit; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -public class ResponseInterceptingStreamTests -{ - [Fact] - public async Task ResponseInterceptingStream_Calls_Intercepted_Stream_Methods() - { - var streamMock = new Mock(); - const int Position = 1; - const int WriteTimeout = 1; - - using var stream = new MemoryStream(); - using var responseInterceptingStream = new ResponseInterceptingStream( - interceptedStream: streamMock.Object, - responseBodyFeature: new StreamResponseBodyFeature(new MemoryStream()), - bufferWriter: new BufferWriter(), - interceptedValueWriteLimit: 1000); - - var writeResult = responseInterceptingStream.BeginWrite(Array.Empty(), 0, 0, _ => { }, new object()); - responseInterceptingStream.EndWrite(writeResult); - var readResult = responseInterceptingStream.BeginRead(Array.Empty(), 0, 0, _ => { }, new object()); - responseInterceptingStream.EndRead(readResult); - _ = responseInterceptingStream.CanRead; - _ = responseInterceptingStream.CanSeek; - _ = responseInterceptingStream.CanWrite; - await responseInterceptingStream.CompleteAsync(); - responseInterceptingStream.CopyTo(stream, 10); - await responseInterceptingStream.CopyToAsync(stream, 10, default); - responseInterceptingStream.Flush(); - await responseInterceptingStream.FlushAsync(default); - _ = responseInterceptingStream.Length; - responseInterceptingStream.Position = Position; - _ = responseInterceptingStream.Position; - await responseInterceptingStream.ReadAsync(Array.Empty()); - _ = responseInterceptingStream.Seek(0, SeekOrigin.Current); - responseInterceptingStream.SetLength(0); - await responseInterceptingStream.WriteAsync(Array.Empty()); - _ = responseInterceptingStream.WriteTimeout; - responseInterceptingStream.WriteTimeout = WriteTimeout; - await responseInterceptingStream.ReadAsync(Array.Empty(), 0, 0, default); - await responseInterceptingStream.WriteAsync(Array.Empty(), 0, 0, default); - responseInterceptingStream.Read(Array.Empty(), 0, 0); - await responseInterceptingStream.DisposeAsync(); - - streamMock.Verify( - expression: mock => mock.BeginWrite(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.EndWrite(It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.BeginRead(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.EndRead(It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.CanRead, - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.CanSeek, - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.CanWrite, - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.CopyTo(It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.CopyToAsync(It.IsAny(), It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.DisposeAsync(), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.Flush(), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.FlushAsync(It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.Length, - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.Position, - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.ReadAsync(It.IsAny>(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.Seek(It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.SetLength(It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.WriteAsync(It.IsAny>(), It.IsAny()), - times: Times.AtLeast(2)); - - streamMock.Verify( - expression: mock => mock.WriteTimeout, - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.Read(It.IsAny(), It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.Verify( - expression: mock => mock.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - streamMock.VerifySet(x => x.Position = Position, Times.AtLeastOnce); - streamMock.VerifySet(x => x.WriteTimeout = WriteTimeout, Times.AtLeastOnce); - - Assert.Equal(responseInterceptingStream, responseInterceptingStream.Stream); - } - - [Fact] - public async Task ResponseInterceptingStream_Calls_Underlying_HttpResponseBodyFeature_Methods() - { - var featureMock = new Mock(); - - using var responseInterceptingStream = new ResponseInterceptingStream( - interceptedStream: new MemoryStream(), - responseBodyFeature: featureMock.Object, - bufferWriter: new BufferWriter(), - interceptedValueWriteLimit: 1000); - - responseInterceptingStream.DisableBuffering(); - await responseInterceptingStream.StartAsync(default); - await responseInterceptingStream.SendFileAsync(string.Empty, 0, 0, default); - await responseInterceptingStream.CompleteAsync(); - - featureMock.Verify( - expression: mock => mock.DisableBuffering(), - times: Times.AtLeastOnce); - - featureMock.Verify( - expression: mock => mock.StartAsync(It.IsAny()), - times: Times.AtLeastOnce); - - featureMock.Verify( - expression: mock => mock.SendFileAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), - times: Times.AtLeastOnce); - - featureMock.Verify( - expression: mock => mock.CompleteAsync(), - times: Times.AtLeastOnce); - } - - [Fact] - public void Consumer_Can_Write_Or_Read_From_InterceptingStream_Using_Span_Overloads() - { - using var responseInterceptingStream = new ResponseInterceptingStream( - interceptedStream: new MemoryStream(), - responseBodyFeature: new StreamResponseBodyFeature(new MemoryStream()), - bufferWriter: new BufferWriter(), - interceptedValueWriteLimit: 1000); - - var data = Encoding.UTF8.GetBytes("Kebab"); - - responseInterceptingStream.Write(data, 0, data.Length); - - Assert.Equal(responseInterceptingStream.Position, data.Length); - - responseInterceptingStream.Seek(0, SeekOrigin.Begin); - - var buffer = new byte[data.Length]; - responseInterceptingStream.Read(buffer); - - Assert.Equal(Encoding.UTF8.GetString(data), Encoding.UTF8.GetString(buffer)); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestBodyPipeFeatureMiddleware.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestBodyPipeFeatureMiddleware.cs deleted file mode 100644 index 307e3a8922b..00000000000 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestBodyPipeFeatureMiddleware.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; - -namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; - -internal sealed class TestBodyPipeFeatureMiddleware : IMiddleware -{ - public Action? RequestBodyInfinitePipeFeatureCallback { get; set; } - - public async Task InvokeAsync(HttpContext context, RequestDelegate next) - { - if (context.Request.Path.StartsWithSegments("/err-pipe")) - { - context.Features.Set(new RequestBodyErrorPipeFeature()); - } - - if (context.Request.Path.StartsWithSegments("/multi-segment-pipe")) - { - context.Features.Set(new RequestBodyMultiSegmentPipeFeature()); - } - - if (context.Request.Path.StartsWithSegments("/infinite-pipe")) - { - var infinitePipeFeature = new RequestBodyInfinitePipeFeature(RequestBodyInfinitePipeFeatureCallback); - context.Features.Set(infinitePipeFeature); - } - - await next(context); - } -} diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestHttpLogEnricher.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestHttpLogEnricher.cs index 21ed4127b73..fd162ac39f5 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestHttpLogEnricher.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestHttpLogEnricher.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET8_0_OR_GREATER + using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Diagnostics.Enrichment; @@ -14,9 +16,10 @@ internal sealed class TestHttpLogEnricher : IHttpLogEnricher internal const string Key2 = "MyEnrichedProperty_2"; internal const double Value2 = 1.75; - public void Enrich(IEnrichmentTagCollector collector, HttpRequest request, HttpResponse response) + public void Enrich(IEnrichmentTagCollector collector, HttpContext httpContext) { collector.Add(Key1, Value1); collector.Add(Key2, Value2); } } +#endif diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerTests.cs index 9752d07e743..4b699558376 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerTests.cs @@ -53,10 +53,6 @@ public static void Basic() var logger = lf.CreateLogger(Category); logger.LogWarning("MSG0"); - var lmh = LogMethodHelper.GetHelper(); - lmh.Add("PK1", "PV1"); - logger.Log(LogLevel.Error, new EventId(1, "ID1"), lmh, null, (_, _) => "MSG1"); - var lms = LoggerMessageHelper.ThreadLocalState; var index = lms.ReserveTagSpace(1); lms.TagArray[index] = new("PK2", "PV2"); @@ -70,7 +66,7 @@ public static void Basic() var sink = provider.Logger!; var collector = sink.Collector; Assert.Equal(Category, sink.Category); - Assert.Equal(3, collector.Count); + Assert.Equal(2, collector.Count); var snap = collector.GetSnapshot(); @@ -83,21 +79,13 @@ public static void Basic() Assert.Equal(Category, snap[1].Category); Assert.Null(snap[1].Exception); - Assert.Equal(new EventId(1, "ID1"), snap[1].Id); - Assert.Equal("MSG1", snap[1].Message); - Assert.Equal("PV1", snap[1].StructuredState!.GetValue("PK1")); + Assert.Equal(new EventId(2, "ID2"), snap[1].Id); + Assert.Equal("MSG2", snap[1].Message); + Assert.Equal("PV2", snap[1].StructuredState!.GetValue("PK2")); + Assert.Equal("REDACTED", snap[1].StructuredState!.GetValue("PK3")); + Assert.Null(snap[1].StructuredState!.GetValue("PK4")); Assert.Equal("EV1", snap[1].StructuredState!.GetValue("EK1")); Assert.Equal("SEV1", snap[1].StructuredState!.GetValue("SEK1")); - - Assert.Equal(Category, snap[2].Category); - Assert.Null(snap[2].Exception); - Assert.Equal(new EventId(2, "ID2"), snap[2].Id); - Assert.Equal("MSG2", snap[2].Message); - Assert.Equal("PV2", snap[2].StructuredState!.GetValue("PK2")); - Assert.Equal("REDACTED", snap[2].StructuredState!.GetValue("PK3")); - Assert.Null(snap[2].StructuredState!.GetValue("PK4")); - Assert.Equal("EV1", snap[2].StructuredState!.GetValue("EK1")); - Assert.Equal("SEV1", snap[2].StructuredState!.GetValue("SEK1")); } [Theory] @@ -128,10 +116,6 @@ public static void BagAndJoiner(bool objectVersion) var logger = lf.CreateLogger(Category); logger.LogWarning("MSG0"); - var lmh = LogMethodHelper.GetHelper(); - lmh.Add("PK1", "PV1"); - logger.Log(LogLevel.Error, new EventId(1, "ID1"), lmh, null, (_, _) => "MSG1"); - var lms = LoggerMessageHelper.ThreadLocalState; var index = lms.ReserveTagSpace(1); lms.TagArray[index] = new("PK2", "PV2"); @@ -140,7 +124,7 @@ public static void BagAndJoiner(bool objectVersion) var sink = provider.Logger!; var collector = sink.Collector; Assert.Equal(Category, sink.Category); - Assert.Equal(3, collector.Count); + Assert.Equal(2, collector.Count); var snap = collector.GetSnapshot(); @@ -153,19 +137,11 @@ public static void BagAndJoiner(bool objectVersion) Assert.Equal(Category, snap[1].Category); Assert.Null(snap[1].Exception); - Assert.Equal(new EventId(1, "ID1"), snap[1].Id); - Assert.Equal("MSG1", snap[1].Message); - Assert.Equal("PV1", snap[1].StructuredState!.GetValue("PK1")); + Assert.Equal(new EventId(2, "ID2"), snap[1].Id); + Assert.Equal("MSG2", snap[1].Message); + Assert.Equal("PV2", snap[1].StructuredState!.GetValue("PK2")); Assert.Equal("EV1", snap[1].StructuredState!.GetValue("EK1")); - Assert.Equal("EV2", snap[0].StructuredState!.GetValue("EK2")); - - Assert.Equal(Category, snap[2].Category); - Assert.Null(snap[2].Exception); - Assert.Equal(new EventId(2, "ID2"), snap[2].Id); - Assert.Equal("MSG2", snap[2].Message); - Assert.Equal("PV2", snap[2].StructuredState!.GetValue("PK2")); - Assert.Equal("EV1", snap[2].StructuredState!.GetValue("EK1")); - Assert.Equal("EV2", snap[0].StructuredState!.GetValue("EK2")); + Assert.Equal("EV2", snap[1].StructuredState!.GetValue("EK2")); } [Fact] @@ -206,10 +182,6 @@ public static void NullStateObject() logger.Log(LogLevel.Error, new EventId(0, "ID0"), null, null, (_, _) => "MSG0"); logger.Log(LogLevel.Error, new EventId(0, "ID0b"), null, null, (_, _) => "MSG0b"); - var lmh = LogMethodHelper.GetHelper(); - logger.Log(LogLevel.Error, new EventId(1, "ID1"), (LogMethodHelper?)null, null, (_, _) => "MSG1"); - logger.Log(LogLevel.Error, new EventId(1, "ID1b"), (LogMethodHelper?)null, null, (_, _) => "MSG1b"); - var lms = LoggerMessageHelper.ThreadLocalState; logger.Log(LogLevel.Warning, new EventId(2, "ID2"), (LoggerMessageState?)null, null, (_, _) => "MSG2"); logger.Log(LogLevel.Warning, new EventId(2, "ID2b"), (LoggerMessageState?)null, null, (_, _) => "MSG2b"); @@ -217,7 +189,7 @@ public static void NullStateObject() var sink = provider.Logger!; var collector = sink.Collector; Assert.Equal(Category, sink.Category); - Assert.Equal(6, collector.Count); + Assert.Equal(4, collector.Count); var snap = collector.GetSnapshot(); @@ -237,31 +209,17 @@ public static void NullStateObject() Assert.Equal(Category, snap[2].Category); Assert.Null(snap[2].Exception); - Assert.Equal(new EventId(1, "ID1"), snap[2].Id); - Assert.Equal("MSG1", snap[2].Message); + Assert.Equal(new EventId(2, "ID2"), snap[2].Id); + Assert.Equal("MSG2", snap[2].Message); Assert.Equal("EV1", snap[2].StructuredState!.GetValue("EK1")); Assert.Equal("SEV1", snap[2].StructuredState!.GetValue("SEK1")); Assert.Equal(Category, snap[3].Category); Assert.Null(snap[3].Exception); - Assert.Equal(new EventId(1, "ID1b"), snap[3].Id); - Assert.Equal("MSG1b", snap[3].Message); + Assert.Equal(new EventId(2, "ID2b"), snap[3].Id); + Assert.Equal("MSG2b", snap[3].Message); Assert.Equal("EV1", snap[3].StructuredState!.GetValue("EK1")); Assert.Equal("SEV1", snap[3].StructuredState!.GetValue("SEK1")); - - Assert.Equal(Category, snap[4].Category); - Assert.Null(snap[4].Exception); - Assert.Equal(new EventId(2, "ID2"), snap[4].Id); - Assert.Equal("MSG2", snap[4].Message); - Assert.Equal("EV1", snap[4].StructuredState!.GetValue("EK1")); - Assert.Equal("SEV1", snap[4].StructuredState!.GetValue("SEK1")); - - Assert.Equal(Category, snap[5].Category); - Assert.Null(snap[5].Exception); - Assert.Equal(new EventId(2, "ID2b"), snap[5].Id); - Assert.Equal("MSG2b", snap[5].Message); - Assert.Equal("EV1", snap[5].StructuredState!.GetValue("EK1")); - Assert.Equal("SEV1", snap[5].StructuredState!.GetValue("SEK1")); } [Fact] @@ -440,10 +398,6 @@ public static void Exceptions(bool includeExceptionMessage) logger.Log(LogLevel.Error, new EventId(0, "ID0"), null, null, (_, _) => "MSG0"); logger.Log(LogLevel.Error, new EventId(0, "ID0b"), null, ex, (_, _) => "MSG0b"); - var lmh = LogMethodHelper.GetHelper(); - logger.Log(LogLevel.Error, new EventId(1, "ID1"), lmh, null, (_, _) => "MSG1"); - logger.Log(LogLevel.Error, new EventId(1, "ID1b"), lmh, ex, (_, _) => "MSG1b"); - var lms = LoggerMessageHelper.ThreadLocalState; logger.Log(LogLevel.Warning, new EventId(2, "ID2"), lms, null, (_, _) => "MSG2"); logger.Log(LogLevel.Warning, new EventId(2, "ID2b"), lms, ex, (_, _) => "MSG2b"); @@ -451,7 +405,7 @@ public static void Exceptions(bool includeExceptionMessage) var sink = provider.Logger!; var collector = sink.Collector; Assert.Equal(Category, sink.Category); - Assert.Equal(6, collector.Count); + Assert.Equal(4, collector.Count); var snap = collector.GetSnapshot(); @@ -467,25 +421,15 @@ public static void Exceptions(bool includeExceptionMessage) Assert.Equal(Category, snap[2].Category); Assert.Null(snap[2].Exception); - Assert.Equal(new EventId(1, "ID1"), snap[2].Id); - Assert.Equal("MSG1", snap[2].Message); + Assert.Equal(new EventId(2, "ID2"), snap[2].Id); + Assert.Equal("MSG2", snap[2].Message); Assert.Equal(Category, snap[3].Category); Assert.NotNull(snap[3].Exception); - Assert.Equal(new EventId(1, "ID1b"), snap[3].Id); - Assert.Equal("MSG1b", snap[3].Message); - - Assert.Equal(Category, snap[4].Category); - Assert.Null(snap[4].Exception); - Assert.Equal(new EventId(2, "ID2"), snap[4].Id); - Assert.Equal("MSG2", snap[4].Message); - - Assert.Equal(Category, snap[5].Category); - Assert.NotNull(snap[5].Exception); - Assert.Equal(new EventId(2, "ID2b"), snap[5].Id); - Assert.Equal("MSG2b", snap[5].Message); + Assert.Equal(new EventId(2, "ID2b"), snap[3].Id); + Assert.Equal("MSG2b", snap[3].Message); - var stackTrace = snap[5].StructuredState!.GetValue("stackTrace")!; + var stackTrace = snap[3].StructuredState!.GetValue("stackTrace")!; Assert.Contains("AggregateException", stackTrace); Assert.Contains("ArgumentNullException", stackTrace); Assert.Contains("ArgumentOutOfRangeException", stackTrace); diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/SerialExtendedLoggerTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/SerialExtendedLoggerTests.cs index 8386141f2fe..e4a25ff60b8 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/SerialExtendedLoggerTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/SerialExtendedLoggerTests.cs @@ -51,18 +51,15 @@ public static void BadContributors() var logger = lf.CreateLogger(Category); logger.Log(LogLevel.Error, new EventId(0, "ID0"), null, null, (_, _) => "MSG0"); - var lmh = LogMethodHelper.GetHelper(); - logger.Log(LogLevel.Error, new EventId(1, "ID1"), (LogMethodHelper?)null, null, (_, _) => "MSG1"); - var lms = LoggerMessageHelper.ThreadLocalState; logger.Log(LogLevel.Warning, new EventId(2, "ID2"), (LoggerMessageState?)null, null, (_, _) => "MSG2"); var sink = provider.Logger!; var collector = sink.Collector; Assert.Equal(Category, sink.Category); - Assert.Equal(3, collector.Count); + Assert.Equal(2, collector.Count); - Assert.Equal(3, eventCount); + Assert.Equal(2, eventCount); } private sealed class Provider : ILoggerProvider diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LogMethodHelperTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LogMethodHelperTests.cs deleted file mode 100644 index 672eab38e67..00000000000 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LogMethodHelperTests.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.Collections.Generic; -using Microsoft.Extensions.Compliance.Classification; -using Microsoft.Extensions.Diagnostics.Enrichment; -using Xunit; - -namespace Microsoft.Extensions.Logging.Test; - -public static class LogMethodHelperTests -{ - [Fact] - public static void CollectorContract() - { - const string ParamName = "param_name Name"; - const string PropName = "Property Name"; - const string Value = "Value"; - - var list = new LogMethodHelper(); - Assert.Equal(list.ParameterName, string.Empty); - Assert.Empty(list); - - list.ParameterName = ParamName; - list.Add(PropName, Value); - Assert.Single(list); - Assert.Equal(ParamName, list.ParameterName); - Assert.Equal(ParamName + "_" + PropName, list[0].Key); - Assert.Equal(Value, list[0].Value); - - _ = list.TryReset(); - Assert.Empty(list); - Assert.Equal(string.Empty, list.ParameterName); - - list.Add(PropName, Value, DataClassification.None); - Assert.Single(list); - Assert.Equal(PropName, list[0].Key); - Assert.Equal(Value, list[0].Value); - - var collector = (IEnrichmentTagCollector)list; - - _ = list.TryReset(); - collector.Add(PropName, Value); - Assert.Single(list); - Assert.Equal(PropName, list[0].Key); - Assert.Equal(Value, list[0].Value); - } - - [Theory] - [InlineData(null, "null")] - [InlineData(new[] { "One" }, "[\"One\"]")] - [InlineData(new[] { "One", "Two" }, "[\"One\",\"Two\"]")] - [InlineData(new[] { "One", null }, "[\"One\",null]")] - [InlineData(new[] { 1, 2, 3 }, "[\"1\",\"2\",\"3\"]")] - public static void Enumerate(IEnumerable? enumerable, string expected) - { - Assert.Equal(expected, LogMethodHelper.Stringify(enumerable)); - } - - [Fact] - public static void EnumerateKeyValuePair() - { - Assert.Equal("null", LogMethodHelper.Stringify((IEnumerable>?)null)); - - var d0 = new Dictionary - { - { "One", "Un" } - }; - Assert.Equal("{\"One\"=\"Un\"}", LogMethodHelper.Stringify(d0)); - - var d1 = new Dictionary - { - { "One", "Un" }, - { "Two", "Deux" } - }; - Assert.Equal("{\"One\"=\"Un\",\"Two\"=\"Deux\"}", LogMethodHelper.Stringify(d1)); - - var d2 = new List> - { - new(null, "Un"), - new("Two", null), - }; - Assert.Equal("{null=\"Un\",\"Two\"=null}", LogMethodHelper.Stringify(d2)); - - var d3 = new Dictionary - { - { "Zero", 0 }, - { "One", 1 }, - { "Two", 2 } - }; - Assert.Equal("{\"Zero\"=\"0\",\"One\"=\"1\",\"Two\"=\"2\"}", LogMethodHelper.Stringify(d3)); - - var d4 = new Dictionary - { - { 0, "Zero" }, - { 1, "One" }, - { 2, "Two" } - }; - Assert.Equal("{\"0\"=\"Zero\",\"1\"=\"One\",\"2\"=\"Two\"}", LogMethodHelper.Stringify(d4)); - } - - [Fact] - public static void Pool() - { - var list = LogMethodHelper.GetHelper(); - Assert.NotNull(list); - list.Add("Foo", "Bar"); - LogMethodHelper.ReturnHelper(list); - } - -#if NET6_0_OR_GREATER - [Fact] - public static void Options() - { - var opt = LogMethodHelper.SkipEnabledCheckOptions; - Assert.True(opt.SkipEnabledCheck); - } -#endif -} diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageHelperTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageHelperTests.cs index a367e87dc88..ed63c97dd3e 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageHelperTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions.Tests/Logging/LoggerMessageHelperTests.cs @@ -71,13 +71,4 @@ public static void ThreadLocal() var lmp2 = LoggerMessageHelper.ThreadLocalState; Assert.Same(lmp1, lmp2); } - -#if NET6_0_OR_GREATER - [Fact] - public static void Options() - { - var opt = LogMethodHelper.SkipEnabledCheckOptions; - Assert.True(opt.SkipEnabledCheck); - } -#endif } diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs index 5ac6fab0460..ad33ed517d8 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs @@ -16,7 +16,6 @@ using Microsoft.Extensions.Http.Diagnostics; using Microsoft.Extensions.Http.Logging.Internal; using Microsoft.Extensions.Http.Logging.Test.Internal; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Telemetry.Internal; using Moq; using Xunit; @@ -163,7 +162,6 @@ public async Task ReadAsync_NoHost_ReturnsLogRecordWithoutHost() var actualRecord = new LogRecord(); var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - var propertyBag = new LogMethodHelper(); await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None).ConfigureAwait(false); @@ -244,7 +242,6 @@ public async Task ReadAsync_AllDataWithRequestMetadataSet_ReturnsLogRecord() var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - var propertyBag = new LogMethodHelper(); var actualRecord = new LogRecord(); await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None).ConfigureAwait(false); @@ -328,7 +325,6 @@ public async Task ReadAsync_FormatRequestPathDisabled_ReturnsLogRecordWithRoute( var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - var propertyBag = new LogMethodHelper(); var actualRecord = new LogRecord(); await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None).ConfigureAwait(false); @@ -395,7 +391,6 @@ public async Task ReadAsync_RouteParameterRedactionModeNone_ReturnsLogRecordWith var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - var propertyBag = new LogMethodHelper(); var actualRecord = new LogRecord(); await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); @@ -473,7 +468,6 @@ public async Task ReadAsync_RequestMetadataRequestNameSetAndRouteMissing_Returns var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - var propertyBag = new LogMethodHelper(); var actualRecord = new LogRecord(); await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None).ConfigureAwait(false); @@ -551,7 +545,6 @@ public async Task ReadAsync_NoMetadataUsesRedactedString_ReturnsLogRecord() var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - var propertyBag = new LogMethodHelper(); var actualRecord = new LogRecord(); await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None).ConfigureAwait(false); @@ -629,7 +622,6 @@ public async Task ReadAsync_MetadataWithoutRequestRouteOrNameUsesConstants_Retur var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - var propertyBag = new LogMethodHelper(); var actualRecord = new LogRecord(); await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None).ConfigureAwait(false); From 2bd949575afe5010a4570883eee46d986894ec96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Mon, 9 Oct 2023 15:43:23 -0600 Subject: [PATCH 03/28] Documentation transport package (#4254) * Pasting the exact same file as in dotnet/runtime. * Remove runtime-specific code. * Update global.json Microsoft.Build.NoTargets to 3.7.0. * Add LibrariesProjectRoot property to root Directory.Build.props * Initial adaptation of Microsoft.Internal.Extensions.DotNetApiDocs.Transport.proj * Failed attempt to fix the first set of errors * Make transport package build * tweaks * More tweaks * boo * Fix stage * Append the specific *.proj to the _ProjectsToBuild in eng/build.proj * Move PackageDescription a few lines below so that TargetFramework is the first property. * Exclude this proj from ProjectReference * Update src/Packages/Microsoft.Internal.Extensions.DotNetApiDocs.Transport/Microsoft.Internal.Extensions.DotNetApiDocs.Transport.proj Co-authored-by: Viktor Hofer --------- Co-authored-by: Eric StJohn Co-authored-by: Igor Velikorossov Co-authored-by: Viktor Hofer --- .editorconfig | 2 +- eng/MSBuild/Packaging.props | 5 ++ eng/MSBuild/ProjectStaging.targets | 2 +- eng/build.proj | 3 +- global.json | 2 +- ...al.Extensions.DotNetApiDocs.Transport.proj | 49 +++++++++++++++++++ 6 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/Packages/Microsoft.Internal.Extensions.DotNetApiDocs.Transport/Microsoft.Internal.Extensions.DotNetApiDocs.Transport.proj diff --git a/.editorconfig b/.editorconfig index 0944c8107fa..66333e7af3b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,7 +16,7 @@ spelling_exclusion_path = .\eng\spellchecking_exclusions.dic file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license. -[*.{appxmanifest,axml,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,yml,xml,xsd}] +[*.{appxmanifest,axml,build,config,proj,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,yml,xml,xsd}] indent_style = space indent_size = 2 tab_width = 2 diff --git a/eng/MSBuild/Packaging.props b/eng/MSBuild/Packaging.props index b7140218db1..fb4aa3577b8 100644 --- a/eng/MSBuild/Packaging.props +++ b/eng/MSBuild/Packaging.props @@ -7,6 +7,11 @@ true + + + $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'Libraries')) + + true diff --git a/eng/MSBuild/ProjectStaging.targets b/eng/MSBuild/ProjectStaging.targets index 78d17b28d49..af5baa16a8e 100644 --- a/eng/MSBuild/ProjectStaging.targets +++ b/eng/MSBuild/ProjectStaging.targets @@ -1,7 +1,7 @@ - + diff --git a/eng/build.proj b/eng/build.proj index 8be3210928b..9c3a38d8ef0 100644 --- a/eng/build.proj +++ b/eng/build.proj @@ -5,7 +5,8 @@ <_ProjectsToBuild Include="$(MSBuildThisFileDirectory)..\test\**\*.csproj" /> <_ProjectsToBuild Include="$(MSBuildThisFileDirectory)..\bench\**\*.csproj" /> - + + <_ProjectsToBuild Include="$(MSBuildThisFileDirectory)..\src\Packages\Microsoft.Internal.Extensions.DotNetApiDocs.Transport\Microsoft.Internal.Extensions.DotNetApiDocs.Transport.proj" /> diff --git a/global.json b/global.json index b8243d53d90..d9945feb7b1 100644 --- a/global.json +++ b/global.json @@ -14,7 +14,7 @@ } }, "msbuild-sdks": { - "Microsoft.Build.NoTargets": "3.5.0", + "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.2.0", "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23463.1", "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23463.1" diff --git a/src/Packages/Microsoft.Internal.Extensions.DotNetApiDocs.Transport/Microsoft.Internal.Extensions.DotNetApiDocs.Transport.proj b/src/Packages/Microsoft.Internal.Extensions.DotNetApiDocs.Transport/Microsoft.Internal.Extensions.DotNetApiDocs.Transport.proj new file mode 100644 index 00000000000..5c9f35c95bb --- /dev/null +++ b/src/Packages/Microsoft.Internal.Extensions.DotNetApiDocs.Transport/Microsoft.Internal.Extensions.DotNetApiDocs.Transport.proj @@ -0,0 +1,49 @@ + + + + $(LatestTargetFramework) + + + + transport + false + + Internal transport package to provide dotnet-api-docs with the reference assemblies and compiler generated documentation files from dotnet/extensions. + true + false + false + true + $(NoWarn);NU5128;NU5131 + false + $(TargetsForTfmSpecificContentInPackage);IncludeProjectReferenceCompilerGeneratedSecondaryOutputInPackage + + false + + + + + + + + + + + <_projectReferencesToInclude Include="@(ReferencePath->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" /> + <_referenceAssemblyFile Include="@(_projectReferencesToInclude->Metadata('ReferenceAssembly'))" /> + + + <_referenceXmlDocFile Include="@(_projectReferencesToInclude->'%(RootDir)%(Directory)%(Filename).xml')" /> + + + + + + From f67c147ec14052773b2dccf930d682f74a560633 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Mon, 9 Oct 2023 18:42:15 -0700 Subject: [PATCH 04/28] [release/8.0] Update SDK (#4543) * Update global.json * Fix build --------- Co-authored-by: Igor Velikorossov --- Directory.Build.props | 2 +- .../RedactionBenchmark.cs | 2 +- global.json | 4 +-- .../CallAnalysis/CallAnalyzer.State.cs | 4 +-- .../ApiLifecycle/AssemblyAnalysis.cs | 14 +++++----- .../ApiLifecycle/Json/JsonArray.cs | 2 +- .../ApiLifecycle/Json/JsonReader.cs | 4 +-- .../CallAnalysis/CallAnalyzer.State.cs | 4 +-- .../AutoClientGenerator.cs | 6 ++--- .../Model/RestApiMethod.cs | 6 ++--- .../Model/RestApiType.cs | 4 +-- .../Model/ClassifiedItem.cs | 2 +- .../Model/ClassifiedLogMethod.cs | 2 +- .../Microsoft.Gen.ComplianceReports/Parser.cs | 8 +++--- .../ContextReceiver.cs | 2 +- .../Model/OptionsContextType.cs | 2 +- .../Microsoft.Gen.Logging/Emission/Emitter.cs | 2 +- .../Model/LoggingMethod.cs | 2 +- .../Model/LoggingMethodParameter.cs | 4 +-- .../Model/LoggingProperty.cs | 4 +-- .../Model/LoggingType.cs | 4 +-- .../Parsing/Parser.LogProperties.cs | 2 +- .../Microsoft.Gen.Metrics/MetricsGenerator.cs | 6 ++--- .../Model/MetricMethod.cs | 8 +++--- .../Microsoft.Gen.Metrics/Model/MetricType.cs | 2 +- .../Microsoft.Gen.Metrics/Parser.cs | 5 ++-- .../StrongTypeAttributeParameters.cs | 6 ++--- .../FakeSslCertificateFactory.cs | 5 ++-- .../FeaturesPooledPolicy.cs | 2 +- .../RedactorProviderOptions.cs | 2 +- .../FakeRedactionCollector.cs | 4 +-- .../AutoActivatorOptions.cs | 4 +-- .../Logging/ExtendedLogger.cs | 12 ++++----- .../Logging/ExtendedLoggerFactory.cs | 4 +-- .../Logging/FakeLogCollector.cs | 2 +- .../Metrics/MetricCollector.cs | 12 ++++----- .../Internal/RequestMessageSnapshot.cs | 4 +-- .../ContextualOptionsFactory.cs | 6 ++--- .../Internal/FailureEventMetricsOptions.cs | 2 +- .../FakeTimeProvider.cs | 2 +- .../ApiLifecycle/AnalysisModelTest.cs | 11 ++++---- .../Generated/ContextualOptionsTests.cs | 2 +- .../Unit/ContextualOptionsTests.cs | 2 +- .../TestClasses/EnumerableTestExtensions.cs | 2 +- .../Unit/EmitterTests.cs | 18 ++++++------- .../Logging/TestLogEnrichmentTagCollector.cs | 2 +- .../FeaturesPooledPolicyTests.cs | 2 +- .../ExceptionSummarizerTests.cs | 6 ++--- .../TestLogEnrichmentTagCollector.cs | 2 +- .../TestMetricEnrichmentTagCollector.cs | 2 +- .../Logging/ExtendedLoggerFactoryTests.cs | 4 +-- .../Linux/Resources/TestResources.cs | 2 +- .../Logging/HttpClientLoggerTest.cs | 24 ++++++++--------- .../Logging/HttpRequestReaderTest.cs | 26 +++++++++---------- .../Hedging/HedgingTests.cs | 2 +- ...ClientBuilderExtensionsTests.Resilience.cs | 2 +- .../Routing/RoutingStrategyTest.cs | 2 +- .../ResilienceMetricsEnricherTests.cs | 4 +-- .../Data.Validation/LengthAttributeTests.cs | 4 +-- 59 files changed, 145 insertions(+), 148 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 4c1585b5276..24c1bb96cae 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,7 +36,7 @@ false - preview + latest enable disable true diff --git a/bench/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.PerformanceTests/RedactionBenchmark.cs b/bench/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.PerformanceTests/RedactionBenchmark.cs index 910c42583ce..d8aae25fead 100644 --- a/bench/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.PerformanceTests/RedactionBenchmark.cs +++ b/bench/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.PerformanceTests/RedactionBenchmark.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Diagnostics.Bench; public class RedactionBenchmark { private readonly string _httpPath; - private readonly Dictionary _routeParameterDataClasses = new(); + private readonly Dictionary _routeParameterDataClasses = []; private readonly ObjectPool _stringBuilderPool; private readonly Dictionary _routeValues = new() { diff --git a/global.json b/global.json index d9945feb7b1..24ad944a079 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "8.0.100-rc.2.23462.28" + "version": "8.0.100-rtm.23506.1" }, "tools": { - "dotnet": "8.0.100-rc.2.23462.28", + "dotnet": "8.0.100-rtm.23506.1", "runtimes": { "dotnet/x64": [ "6.0.22" diff --git a/src/Analyzers/Microsoft.Analyzers.Extra/CallAnalysis/CallAnalyzer.State.cs b/src/Analyzers/Microsoft.Analyzers.Extra/CallAnalysis/CallAnalyzer.State.cs index 6db42e7f340..6822b26e8f4 100644 --- a/src/Analyzers/Microsoft.Analyzers.Extra/CallAnalysis/CallAnalyzer.State.cs +++ b/src/Analyzers/Microsoft.Analyzers.Extra/CallAnalysis/CallAnalyzer.State.cs @@ -18,7 +18,7 @@ internal sealed class State public readonly Dictionary>> Props = new(SymbolEqualityComparer.Default); public readonly Dictionary>> ExceptionTypes = new(SymbolEqualityComparer.Default); public readonly Dictionary> Interfaces = new(SymbolEqualityComparer.Default); - public readonly HashSet InterfaceMethodNames = new(); + public readonly HashSet InterfaceMethodNames = []; } internal sealed class MethodHandlers @@ -29,6 +29,6 @@ public MethodHandlers(IMethodSymbol method) } public IMethodSymbol Method { get; } - public List> Actions { get; } = new(); + public List> Actions { get; } = []; } } diff --git a/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/AssemblyAnalysis.cs b/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/AssemblyAnalysis.cs index 8d94502674c..b29e34e2af0 100644 --- a/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/AssemblyAnalysis.cs +++ b/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/AssemblyAnalysis.cs @@ -13,13 +13,13 @@ namespace Microsoft.Extensions.LocalAnalyzers.ApiLifecycle; internal sealed class AssemblyAnalysis { public Assembly Assembly { get; } - public HashSet MissingTypes { get; } = new(); + public HashSet MissingTypes { get; } = []; public Dictionary> MissingConstraints { get; } = new(SymbolEqualityComparer.Default); public Dictionary> MissingBaseTypes { get; } = new(SymbolEqualityComparer.Default); - public HashSet MissingMethods { get; } = new(); - public HashSet MissingProperties { get; } = new(); - public HashSet MissingFields { get; } = new(); - public HashSet<(ISymbol symbol, Stage stage)> FoundInBaseline { get; } = new(); + public HashSet MissingMethods { get; } = []; + public HashSet MissingProperties { get; } = []; + public HashSet MissingFields { get; } = []; + public HashSet<(ISymbol symbol, Stage stage)> FoundInBaseline { get; } = []; public HashSet NotFoundInBaseline { get; } = new(SymbolEqualityComparer.Default); #pragma warning disable SA1118 // Parameter should not span multiple lines @@ -168,7 +168,7 @@ public void AnalyzeType(INamedTypeSymbol type) } else { - MissingBaseTypes.Add(type, new List { @base }); + MissingBaseTypes.Add(type, [@base]); } } } @@ -184,7 +184,7 @@ public void AnalyzeType(INamedTypeSymbol type) } else { - MissingConstraints.Add(type, new List { constraint }); + MissingConstraints.Add(type, [constraint]); } } } diff --git a/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonArray.cs b/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonArray.cs index 76e210c8430..b4921a4d0c1 100644 --- a/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonArray.cs +++ b/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonArray.cs @@ -17,7 +17,7 @@ namespace Microsoft.Extensions.LocalAnalyzers.Json; [DebuggerTypeProxy(typeof(JsonArrayDebugView))] internal sealed class JsonArray : IEnumerable { - private readonly List _items = new(); + private readonly List _items = []; /// /// Initializes a new instance of the class, adding the given values to the collection. diff --git a/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonReader.cs b/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonReader.cs index 7d06df7b378..fab89823a3f 100644 --- a/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonReader.cs +++ b/src/Analyzers/Microsoft.Analyzers.Local/ApiLifecycle/Json/JsonReader.cs @@ -257,7 +257,7 @@ private char ReadUnicodeLiteral() #pragma warning restore S109 // Magic numbers should not be used private JsonObject ReadObject() { - return ReadObject(new JsonObject()); + return ReadObject([]); } private JsonObject ReadObject(JsonObject jsonObject) @@ -328,7 +328,7 @@ private JsonObject ReadObject(JsonObject jsonObject) private JsonArray ReadArray() { - return ReadArray(new JsonArray()); + return ReadArray([]); } private JsonArray ReadArray(JsonArray jsonArray) diff --git a/src/Analyzers/Microsoft.Analyzers.Local/CallAnalysis/CallAnalyzer.State.cs b/src/Analyzers/Microsoft.Analyzers.Local/CallAnalysis/CallAnalyzer.State.cs index c1ca1b321e7..3c623302d46 100644 --- a/src/Analyzers/Microsoft.Analyzers.Local/CallAnalysis/CallAnalyzer.State.cs +++ b/src/Analyzers/Microsoft.Analyzers.Local/CallAnalysis/CallAnalyzer.State.cs @@ -18,7 +18,7 @@ internal sealed class State public readonly Dictionary>> Props = new(SymbolEqualityComparer.Default); public readonly Dictionary>> ExceptionTypes = new(SymbolEqualityComparer.Default); public readonly Dictionary> Interfaces = new(SymbolEqualityComparer.Default); - public readonly HashSet InterfaceMethodNames = new(); + public readonly HashSet InterfaceMethodNames = []; } internal sealed class MethodHandlers @@ -29,6 +29,6 @@ public MethodHandlers(IMethodSymbol method) } public IMethodSymbol Method { get; } - public List> Actions { get; } = new(); + public List> Actions { get; } = []; } } diff --git a/src/Generators/Microsoft.Gen.AutoClient/AutoClientGenerator.cs b/src/Generators/Microsoft.Gen.AutoClient/AutoClientGenerator.cs index 6685b982bef..4d54eac662c 100644 --- a/src/Generators/Microsoft.Gen.AutoClient/AutoClientGenerator.cs +++ b/src/Generators/Microsoft.Gen.AutoClient/AutoClientGenerator.cs @@ -15,8 +15,8 @@ namespace Microsoft.Gen.AutoClient; [Generator] public class AutoClientGenerator : IIncrementalGenerator { - private static readonly HashSet _attributeNames = new() - { + private static readonly HashSet _attributeNames = + [ SymbolLoader.RestApiAttribute, SymbolLoader.RestGetAttribute, @@ -31,7 +31,7 @@ public class AutoClientGenerator : IIncrementalGenerator SymbolLoader.RestHeaderAttribute, SymbolLoader.RestQueryAttribute, SymbolLoader.RestBodyAttribute - }; + ]; public void Initialize(IncrementalGeneratorInitializationContext context) { diff --git a/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiMethod.cs b/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiMethod.cs index 2a6da882091..cea22a9d395 100644 --- a/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiMethod.cs +++ b/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiMethod.cs @@ -7,12 +7,12 @@ namespace Microsoft.Gen.AutoClient.Model; internal sealed class RestApiMethod { - public readonly List AllParameters = new(); - public readonly List FormatParameters = new(); + public readonly List AllParameters = []; + public readonly List FormatParameters = []; public string MethodName = string.Empty; public string? HttpMethod = string.Empty; public string? Path = string.Empty; public string? ReturnType = string.Empty; public string RequestName = string.Empty; - public Dictionary StaticHeaders = new(); + public Dictionary StaticHeaders = []; } diff --git a/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiType.cs b/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiType.cs index ea9a5ee37db..b3e3b1a50a6 100644 --- a/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiType.cs +++ b/src/Generators/Microsoft.Gen.AutoClient/Model/RestApiType.cs @@ -7,13 +7,13 @@ namespace Microsoft.Gen.AutoClient.Model; internal sealed class RestApiType { - public readonly List Methods = new(); + public readonly List Methods = []; public string Namespace = string.Empty; public string Name = string.Empty; public string Constraints = string.Empty; public string Modifiers = string.Empty; public string Keyword = string.Empty; public string HttpClientName = string.Empty; - public Dictionary StaticHeaders = new(); + public Dictionary StaticHeaders = []; public string DependencyName = string.Empty; } diff --git a/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedItem.cs b/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedItem.cs index 5c47c9e5872..5071ad96e39 100644 --- a/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedItem.cs +++ b/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedItem.cs @@ -15,5 +15,5 @@ internal sealed class ClassifiedItem public string Name = string.Empty; public string TypeName = string.Empty; - public List Classifications = new(); + public List Classifications = []; } diff --git a/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedLogMethod.cs b/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedLogMethod.cs index 7348c2f45ae..d7cc30252a1 100644 --- a/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedLogMethod.cs +++ b/src/Generators/Microsoft.Gen.ComplianceReports/Model/ClassifiedLogMethod.cs @@ -12,5 +12,5 @@ internal sealed class ClassifiedLogMethod { public string MethodName = string.Empty; public string LogMethodMessage = string.Empty; - public List Parameters = new(); + public List Parameters = []; } diff --git a/src/Generators/Microsoft.Gen.ComplianceReports/Parser.cs b/src/Generators/Microsoft.Gen.ComplianceReports/Parser.cs index 5823bef622e..039a3af2a32 100644 --- a/src/Generators/Microsoft.Gen.ComplianceReports/Parser.cs +++ b/src/Generators/Microsoft.Gen.ComplianceReports/Parser.cs @@ -127,7 +127,7 @@ private static string FormatType(ITypeSymbol typeSymbol) ci.Name = ps.Name; ci.TypeName = FormatType(ps.Type); - classifiedMembers ??= new(); + classifiedMembers ??= []; classifiedMembers[ci.Name] = ci; } } @@ -188,7 +188,7 @@ private static string FormatType(ITypeSymbol typeSymbol) ci.Name = member.Name; ci.TypeName = FormatType(memberType); - classifiedMembers ??= new(); + classifiedMembers ??= []; classifiedMembers[ci.Name] = ci; } @@ -237,10 +237,10 @@ private static string FormatType(ITypeSymbol typeSymbol) ci = AppendAttributeClassifications(ci, attribute); } - clm.Parameters.Add(ci); + clm.Parameters.Add(ci!); } - classifiedLogMethods ??= new(); + classifiedLogMethods ??= []; classifiedLogMethods.Add(clm); } } diff --git a/src/Generators/Microsoft.Gen.ContextualOptions/ContextReceiver.cs b/src/Generators/Microsoft.Gen.ContextualOptions/ContextReceiver.cs index 8c3bbe53d9a..ccf837bc065 100644 --- a/src/Generators/Microsoft.Gen.ContextualOptions/ContextReceiver.cs +++ b/src/Generators/Microsoft.Gen.ContextualOptions/ContextReceiver.cs @@ -21,7 +21,7 @@ public ContextReceiver(CancellationToken token) _token = token; } - private readonly List _typeDeclarations = new(); + private readonly List _typeDeclarations = []; public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { diff --git a/src/Generators/Microsoft.Gen.ContextualOptions/Model/OptionsContextType.cs b/src/Generators/Microsoft.Gen.ContextualOptions/Model/OptionsContextType.cs index 798a383e1c0..ae99413327d 100644 --- a/src/Generators/Microsoft.Gen.ContextualOptions/Model/OptionsContextType.cs +++ b/src/Generators/Microsoft.Gen.ContextualOptions/Model/OptionsContextType.cs @@ -10,7 +10,7 @@ namespace Microsoft.Gen.ContextualOptions.Model; internal sealed class OptionsContextType { - public readonly List Diagnostics = new(); + public readonly List Diagnostics = []; public readonly INamedTypeSymbol Symbol; public readonly ImmutableArray Definitions; public readonly ImmutableArray OptionsContextProperties; diff --git a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.cs b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.cs index b342266e9c9..6ff26d88fd8 100644 --- a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.cs +++ b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.cs @@ -16,7 +16,7 @@ internal sealed partial class Emitter : EmitterBase private const string LoggerMessageHelperType = "global::Microsoft.Extensions.Logging.LoggerMessageHelper"; private readonly StringBuilderPool _sbPool = new(); - private readonly Dictionary _classificationMap = new(); + private readonly Dictionary _classificationMap = []; public string Emit(IEnumerable logTypes, CancellationToken cancellationToken) { diff --git a/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs index 8d1276ad38b..321aa84180c 100644 --- a/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs +++ b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs @@ -13,7 +13,7 @@ namespace Microsoft.Gen.Logging.Model; [DebuggerDisplay("{Name}")] internal sealed class LoggingMethod { - public readonly List Parameters = new(); + public readonly List Parameters = []; public readonly Dictionary TemplateToParameterName = new(StringComparer.OrdinalIgnoreCase); public string Name = string.Empty; public string Message = string.Empty; diff --git a/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodParameter.cs b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodParameter.cs index f1fba226506..cf58a1754c9 100644 --- a/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodParameter.cs +++ b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodParameter.cs @@ -29,8 +29,8 @@ internal sealed class LoggingMethodParameter public bool SkipNullProperties; public bool OmitReferenceName; public bool UsedAsTemplate; - public HashSet ClassificationAttributeTypes = new(); - public List Properties = new(); + public HashSet ClassificationAttributeTypes = []; + public List Properties = []; public TagProvider? TagProvider; public string NameWithAt => NeedsAtSign ? "@" + Name : Name; diff --git a/src/Generators/Microsoft.Gen.Logging/Model/LoggingProperty.cs b/src/Generators/Microsoft.Gen.Logging/Model/LoggingProperty.cs index 663aec23cb5..ee7871c0a3f 100644 --- a/src/Generators/Microsoft.Gen.Logging/Model/LoggingProperty.cs +++ b/src/Generators/Microsoft.Gen.Logging/Model/LoggingProperty.cs @@ -11,7 +11,7 @@ internal sealed class LoggingProperty { public string Name = string.Empty; public string Type = string.Empty; - public HashSet ClassificationAttributeTypes = new(); + public HashSet ClassificationAttributeTypes = []; public bool NeedsAtSign; public bool IsNullable; public bool IsReference; @@ -19,7 +19,7 @@ internal sealed class LoggingProperty public bool ImplementsIConvertible; public bool ImplementsIFormattable; public bool ImplementsISpanFormattable; - public List Properties = new(); + public List Properties = []; public bool OmitReferenceName; public TagProvider? TagProvider; diff --git a/src/Generators/Microsoft.Gen.Logging/Model/LoggingType.cs b/src/Generators/Microsoft.Gen.Logging/Model/LoggingType.cs index 1f9a61147d1..251e5138603 100644 --- a/src/Generators/Microsoft.Gen.Logging/Model/LoggingType.cs +++ b/src/Generators/Microsoft.Gen.Logging/Model/LoggingType.cs @@ -12,8 +12,8 @@ namespace Microsoft.Gen.Logging.Model; [DebuggerDisplay("{Name}")] internal sealed class LoggingType { - public readonly List Methods = new(); - public readonly List AllMembers = new(); + public readonly List Methods = []; + public readonly List AllMembers = []; public string Keyword = string.Empty; public string Namespace = string.Empty; public string Name = string.Empty; diff --git a/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.LogProperties.cs b/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.LogProperties.cs index 7fc8372f048..88869bdf551 100644 --- a/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.LogProperties.cs +++ b/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.LogProperties.cs @@ -18,7 +18,7 @@ namespace Microsoft.Gen.Logging.Parsing; internal partial class Parser { - private static readonly HashSet _allowedTypeKinds = new() { TypeKind.Class, TypeKind.Struct, TypeKind.Interface }; + private static readonly HashSet _allowedTypeKinds = [TypeKind.Class, TypeKind.Struct, TypeKind.Interface]; private bool ProcessLogPropertiesForParameter( AttributeData logPropertiesAttribute, diff --git a/src/Generators/Microsoft.Gen.Metrics/MetricsGenerator.cs b/src/Generators/Microsoft.Gen.Metrics/MetricsGenerator.cs index e3471bdeb02..c56d7775ba9 100644 --- a/src/Generators/Microsoft.Gen.Metrics/MetricsGenerator.cs +++ b/src/Generators/Microsoft.Gen.Metrics/MetricsGenerator.cs @@ -14,14 +14,14 @@ namespace Microsoft.Gen.Metrics; [Generator] public class MetricsGenerator : IIncrementalGenerator { - private static readonly HashSet _attributeNames = new() - { + private static readonly HashSet _attributeNames = + [ SymbolLoader.CounterAttribute, SymbolLoader.CounterTAttribute.Replace("`1", ""), SymbolLoader.HistogramAttribute, SymbolLoader.HistogramTAttribute.Replace("`1", ""), SymbolLoader.GaugeAttribute - }; + ]; public void Initialize(IncrementalGeneratorInitializationContext context) { diff --git a/src/Generators/Microsoft.Gen.Metrics/Model/MetricMethod.cs b/src/Generators/Microsoft.Gen.Metrics/Model/MetricMethod.cs index 029dd20b329..8a2db1ed8ff 100644 --- a/src/Generators/Microsoft.Gen.Metrics/Model/MetricMethod.cs +++ b/src/Generators/Microsoft.Gen.Metrics/Model/MetricMethod.cs @@ -7,9 +7,9 @@ namespace Microsoft.Gen.Metrics.Model; internal sealed class MetricMethod { - public readonly List AllParameters = new(); - public HashSet TagKeys = new(); - public Dictionary TagDescriptionDictionary = new(); + public readonly List AllParameters = []; + public HashSet TagKeys = []; + public Dictionary TagDescriptionDictionary = []; public string? Name; public string? MetricName; public string? XmlDefinition; @@ -19,7 +19,7 @@ internal sealed class MetricMethod public string MetricTypeName = string.Empty; public InstrumentKind InstrumentKind; public string GenericType = string.Empty; - public List StrongTypeConfigs = new(); // Used for strong type creation only + public List StrongTypeConfigs = []; // Used for strong type creation only public string? StrongTypeObjectName; // Used for strong type creation only public bool IsTagTypeClass; // Used for strong type creation only } diff --git a/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs b/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs index 0225b6dbe15..e96ef625fff 100644 --- a/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs +++ b/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs @@ -7,7 +7,7 @@ namespace Microsoft.Gen.Metrics.Model; internal sealed class MetricType { - public readonly List Methods = new(); + public readonly List Methods = []; public string Namespace = string.Empty; public string Name = string.Empty; public string Constraints = string.Empty; diff --git a/src/Generators/Microsoft.Gen.Metrics/Parser.cs b/src/Generators/Microsoft.Gen.Metrics/Parser.cs index 90ecf00ce13..ed7342f999a 100644 --- a/src/Generators/Microsoft.Gen.Metrics/Parser.cs +++ b/src/Generators/Microsoft.Gen.Metrics/Parser.cs @@ -32,8 +32,7 @@ internal sealed class Parser SymbolDisplayMiscellaneousOptions.UseSpecialTypes); private static readonly HashSet _allowedGenericAttributeTypeArgs = - new() - { + [ SpecialType.System_Byte, SpecialType.System_Int16, SpecialType.System_Int32, @@ -41,7 +40,7 @@ internal sealed class Parser SpecialType.System_Decimal, SpecialType.System_Single, SpecialType.System_Double - }; + ]; private readonly CancellationToken _cancellationToken; private readonly Compilation _compilation; diff --git a/src/Generators/Microsoft.Gen.Metrics/StrongTypeAttributeParameters.cs b/src/Generators/Microsoft.Gen.Metrics/StrongTypeAttributeParameters.cs index d4e6d0dda9f..139973aa661 100644 --- a/src/Generators/Microsoft.Gen.Metrics/StrongTypeAttributeParameters.cs +++ b/src/Generators/Microsoft.Gen.Metrics/StrongTypeAttributeParameters.cs @@ -9,9 +9,9 @@ namespace Microsoft.Gen.Metrics; internal sealed class StrongTypeAttributeParameters { public string MetricNameFromAttribute = string.Empty; - public HashSet TagHashSet = new(); - public Dictionary TagDescriptionDictionary = new(); - public List StrongTypeConfigs = new(); + public HashSet TagHashSet = []; + public Dictionary TagDescriptionDictionary = []; + public List StrongTypeConfigs = []; public string StrongTypeObjectName = string.Empty; public bool IsClass; } diff --git a/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs b/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs index 2473fee5dc9..1a3252df00a 100644 --- a/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs +++ b/src/Libraries/Microsoft.AspNetCore.Testing/FakeSslCertificateFactory.cs @@ -33,11 +33,10 @@ public static X509Certificate2 CreateSslCertificate() request.CertificateExtensions.Add(sanBuilder.Build()); request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension( - new OidCollection - { + [ new("1.3.6.1.5.5.7.3.1"), // serverAuth Object ID - indicates that the certificate is an SSL server certificate new("1.3.6.1.5.5.7.3.2") // clientAuth Object ID - indicates that the certificate is an SSL client certificate - }, + ], false)); return request.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddYears(1)); diff --git a/src/Libraries/Microsoft.Extensions.AsyncState/FeaturesPooledPolicy.cs b/src/Libraries/Microsoft.Extensions.AsyncState/FeaturesPooledPolicy.cs index c83245405e2..3b045465595 100644 --- a/src/Libraries/Microsoft.Extensions.AsyncState/FeaturesPooledPolicy.cs +++ b/src/Libraries/Microsoft.Extensions.AsyncState/FeaturesPooledPolicy.cs @@ -11,7 +11,7 @@ internal sealed class FeaturesPooledPolicy : IPooledObjectPolicy> /// public List Create() { - return new List(); + return []; } /// diff --git a/src/Libraries/Microsoft.Extensions.Compliance.Redaction/RedactorProviderOptions.cs b/src/Libraries/Microsoft.Extensions.Compliance.Redaction/RedactorProviderOptions.cs index 04efeda7ed8..9df4b724e30 100644 --- a/src/Libraries/Microsoft.Extensions.Compliance.Redaction/RedactorProviderOptions.cs +++ b/src/Libraries/Microsoft.Extensions.Compliance.Redaction/RedactorProviderOptions.cs @@ -20,5 +20,5 @@ internal sealed class RedactorProviderOptions /// /// Gets a dictionary of classification-specific redactors. /// - public Dictionary Redactors { get; } = new(); + public Dictionary Redactors { get; } = []; } diff --git a/src/Libraries/Microsoft.Extensions.Compliance.Testing/FakeRedactionCollector.cs b/src/Libraries/Microsoft.Extensions.Compliance.Testing/FakeRedactionCollector.cs index 79c6037cce9..a024912ab44 100644 --- a/src/Libraries/Microsoft.Extensions.Compliance.Testing/FakeRedactionCollector.cs +++ b/src/Libraries/Microsoft.Extensions.Compliance.Testing/FakeRedactionCollector.cs @@ -12,8 +12,8 @@ namespace Microsoft.Extensions.Compliance.Testing; /// public class FakeRedactionCollector { - private readonly List _redactorRequestedLog = new(); - private readonly List _dataRedactedLog = new(); + private readonly List _redactorRequestedLog = []; + private readonly List _dataRedactedLog = []; /// /// Gets the last redactor request "event". diff --git a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs index c2aa14f3177..d0897b614b9 100644 --- a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs +++ b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs @@ -8,6 +8,6 @@ namespace Microsoft.Extensions.DependencyInjection; internal sealed class AutoActivatorOptions { - public HashSet AutoActivators { get; } = new(); - public HashSet<(Type serviceType, object? serviceKey)> KeyedAutoActivators { get; } = new(); + public HashSet AutoActivators { get; } = []; + public HashSet<(Type serviceType, object? serviceKey)> KeyedAutoActivators { get; } = []; } diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLogger.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLogger.cs index d895c6e8d20..02711d97be7 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLogger.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLogger.cs @@ -75,7 +75,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except catch (Exception ex) { #pragma warning disable CA1508 // Avoid dead conditional code - exceptions ??= new(); + exceptions ??= []; #pragma warning restore CA1508 // Avoid dead conditional code exceptions.Add(ex); } @@ -107,7 +107,7 @@ public bool IsEnabled(LogLevel logLevel) catch (Exception ex) { #pragma warning disable CA1508 // Avoid dead conditional code - exceptions ??= new(); + exceptions ??= []; #pragma warning restore CA1508 // Avoid dead conditional code exceptions.Add(ex); } @@ -231,7 +231,7 @@ private void ModernPath(LogLevel logLevel, EventId eventId, LoggerMessageState m } catch (Exception ex) { - exceptions ??= new(); + exceptions ??= []; exceptions.Add(ex); } } @@ -257,7 +257,7 @@ private void ModernPath(LogLevel logLevel, EventId eventId, LoggerMessageState m } catch (Exception ex) { - exceptions ??= new(); + exceptions ??= []; exceptions.Add(ex); } } @@ -315,7 +315,7 @@ private void LegacyPath(LogLevel logLevel, EventId eventId, TState state } catch (Exception ex) { - exceptions ??= new(); + exceptions ??= []; exceptions.Add(ex); } } @@ -341,7 +341,7 @@ private void LegacyPath(LogLevel logLevel, EventId eventId, TState state } catch (Exception ex) { - exceptions ??= new(); + exceptions ??= []; exceptions.Add(ex); } } diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLoggerFactory.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLoggerFactory.cs index 9d601f39ba7..340262ca414 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLoggerFactory.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Extra/Logging/ExtendedLoggerFactory.cs @@ -16,7 +16,7 @@ namespace Microsoft.Extensions.Logging; internal sealed class ExtendedLoggerFactory : ILoggerFactory { private readonly Dictionary _loggers = new(StringComparer.Ordinal); - private readonly List _providerRegistrations = new(); + private readonly List _providerRegistrations = []; private readonly object _sync = new(); private readonly IDisposable? _filterOptionsChangeTokenRegistration; private readonly LoggerFactoryOptions _factoryOptions; @@ -205,7 +205,7 @@ private LoggerInformation[] CreateLoggers(string categoryName) private (MessageLogger[] messageLoggers, ScopeLogger[] scopeLoggers) ApplyFilters(LoggerInformation[] loggers) { var messageLoggers = new List(); - List? scopeLoggers = _filterOptions.CaptureScopes ? new List() : null; + List? scopeLoggers = _filterOptions.CaptureScopes ? [] : null; foreach (LoggerInformation loggerInformation in loggers) { diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Logging/FakeLogCollector.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Logging/FakeLogCollector.cs index e891fb9acd9..376cc87be8c 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Logging/FakeLogCollector.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Logging/FakeLogCollector.cs @@ -16,7 +16,7 @@ namespace Microsoft.Extensions.Logging.Testing; [DebuggerTypeProxy(typeof(FakeLogCollectorDebugView))] public class FakeLogCollector { - private readonly List _records = new(); + private readonly List _records = []; private readonly FakeLogCollectorOptions _options; /// diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Metrics/MetricCollector.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Metrics/MetricCollector.cs index 5dea2d6efac..7102de8987c 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Metrics/MetricCollector.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.Testing/Metrics/MetricCollector.cs @@ -21,8 +21,8 @@ namespace Microsoft.Extensions.Diagnostics.Metrics.Testing; public sealed class MetricCollector : IDisposable where T : struct { - private static readonly HashSet _supportedTs = new() - { + private static readonly HashSet _supportedTs = + [ typeof(int), typeof(byte), typeof(short), @@ -30,12 +30,12 @@ public sealed class MetricCollector : IDisposable typeof(float), typeof(double), typeof(decimal), - }; + ]; internal int WaitersCount => _waiters.Count; // Internal for testing private readonly MeterListener _meterListener = new(); - private readonly List> _measurements = new(); - private readonly List _waiters = new(); + private readonly List> _measurements = []; + private readonly List _waiters = []; private readonly TimeProvider _timeProvider; private bool _disposed; private Instrument? _instrument; @@ -308,7 +308,7 @@ private void OnMeasurementRecorded(Instrument instrument, T measurement, ReadOnl { if (_measurements.Count >= _waiters[i].MinCount) { - toBeWoken ??= new(); + toBeWoken ??= []; toBeWoken.Add(_waiters[i]); _waiters.RemoveAt(i); } diff --git a/src/Libraries/Microsoft.Extensions.Http.Resilience/Internal/RequestMessageSnapshot.cs b/src/Libraries/Microsoft.Extensions.Http.Resilience/Internal/RequestMessageSnapshot.cs index 7f6412bd15c..2c2e55098c9 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Resilience/Internal/RequestMessageSnapshot.cs +++ b/src/Libraries/Microsoft.Extensions.Http.Resilience/Internal/RequestMessageSnapshot.cs @@ -14,8 +14,8 @@ internal sealed class RequestMessageSnapshot : IResettable, IDisposable { private static readonly ObjectPool _snapshots = PoolFactory.CreateResettingPool(); - private readonly List>> _headers = new(); - private readonly List> _properties = new(); + private readonly List>> _headers = []; + private readonly List> _properties = []; private HttpMethod? _method; private Uri? _requestUri; diff --git a/src/Libraries/Microsoft.Extensions.Options.Contextual/ContextualOptionsFactory.cs b/src/Libraries/Microsoft.Extensions.Options.Contextual/ContextualOptionsFactory.cs index 344beba1c09..4d951e52a50 100644 --- a/src/Libraries/Microsoft.Extensions.Options.Contextual/ContextualOptionsFactory.cs +++ b/src/Libraries/Microsoft.Extensions.Options.Contextual/ContextualOptionsFactory.cs @@ -72,7 +72,7 @@ async ValueTask ConfigureOptions(TContext context) } catch (Exception e) { - loadExceptions ??= new(); + loadExceptions ??= []; loadExceptions.Add(e); break; } @@ -90,7 +90,7 @@ async ValueTask ConfigureOptions(TContext context) } catch (Exception e) { - loadExceptions ??= new(); + loadExceptions ??= []; loadExceptions.Add(e); } finally @@ -119,7 +119,7 @@ async ValueTask ConfigureOptions(TContext context) var result = validate.Validate(name, options); if (result.Failed) { - failures ??= new(); + failures ??= []; failures.AddRange(result.Failures); } } diff --git a/src/Libraries/Microsoft.Extensions.Resilience/Resilience/Internal/FailureEventMetricsOptions.cs b/src/Libraries/Microsoft.Extensions.Resilience/Resilience/Internal/FailureEventMetricsOptions.cs index 720018215f4..a964c2283d0 100644 --- a/src/Libraries/Microsoft.Extensions.Resilience/Resilience/Internal/FailureEventMetricsOptions.cs +++ b/src/Libraries/Microsoft.Extensions.Resilience/Resilience/Internal/FailureEventMetricsOptions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Resilience.Internal; internal sealed class FailureEventMetricsOptions { - public Dictionary> Factories { get; } = new(); + public Dictionary> Factories { get; } = []; public void ConfigureFailureResultContext(Func configure) { diff --git a/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs b/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs index 3fa1c401ab1..c408348842e 100644 --- a/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs +++ b/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs @@ -15,7 +15,7 @@ namespace Microsoft.Extensions.Time.Testing; /// public class FakeTimeProvider : TimeProvider { - internal readonly HashSet Waiters = new(); + internal readonly HashSet Waiters = []; private DateTimeOffset _now = new(2000, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); private TimeZoneInfo _localTimeZone = TimeZoneInfo.Utc; private volatile int _wakeWaitersGate; diff --git a/test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs b/test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs index 1896fa585a7..2501dc537c9 100644 --- a/test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs +++ b/test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs @@ -3,7 +3,6 @@ using System; using Microsoft.Extensions.LocalAnalyzers.ApiLifecycle.Model; -using Microsoft.Extensions.LocalAnalyzers.Json; using Xunit; namespace Microsoft.Extensions.LocalAnalyzers.ApiLifecycle.Test; @@ -13,7 +12,7 @@ public class AnalysisModelTest [Fact] public void Field_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() { - var field = new Field(new JsonObject()); + var field = new Field([]); Assert.Equal(string.Empty, field.Member); Assert.Equal(Stage.Experimental, field.Stage); @@ -22,7 +21,7 @@ public void Field_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() [Fact] public void PublicMember_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() { - var member = new TypeDef(new JsonObject()); + var member = new TypeDef([]); Assert.Equal(string.Empty, member.ModifiersAndName); Assert.Equal(Stage.Experimental, member.Stage); @@ -36,7 +35,7 @@ public void PublicMember_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_J [Fact] public void Prop_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() { - var prop = new Prop(new JsonObject()); + var prop = new Prop([]); Assert.Equal(string.Empty, prop.Member); Assert.Equal(Stage.Experimental, prop.Stage); @@ -62,7 +61,7 @@ public void PackageAnalysis_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_I [Fact] public void Package_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() { - var package = new Assembly(new JsonObject()); + var package = new Assembly([]); Assert.Equal(Array.Empty(), package.Types); Assert.Equal(string.Empty, package.Name); @@ -71,7 +70,7 @@ public void Package_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() [Fact] public void Method_FallbacksTo_NotNull_Defaults_When_Value_Not_Found_In_Json() { - var method = new Method(new JsonObject()); + var method = new Method([]); Assert.Equal(string.Empty, method.Member); Assert.Equal(Stage.Experimental, method.Stage); diff --git a/test/Generators/Microsoft.Gen.ContextualOptions/Generated/ContextualOptionsTests.cs b/test/Generators/Microsoft.Gen.ContextualOptions/Generated/ContextualOptionsTests.cs index 8349fc22193..5dea0143d3f 100644 --- a/test/Generators/Microsoft.Gen.ContextualOptions/Generated/ContextualOptionsTests.cs +++ b/test/Generators/Microsoft.Gen.ContextualOptions/Generated/ContextualOptionsTests.cs @@ -14,7 +14,7 @@ public class ContextualOptionsTests { private class Receiver : IOptionsContextReceiver { - public List<(string key, object? value)> Received { get; } = new(); + public List<(string key, object? value)> Received { get; } = []; public void Receive(string key, T value) => Received.Add((key, value)); } diff --git a/test/Generators/Microsoft.Gen.ContextualOptions/Unit/ContextualOptionsTests.cs b/test/Generators/Microsoft.Gen.ContextualOptions/Unit/ContextualOptionsTests.cs index ddc08ec404f..71bfa6797e0 100644 --- a/test/Generators/Microsoft.Gen.ContextualOptions/Unit/ContextualOptionsTests.cs +++ b/test/Generators/Microsoft.Gen.ContextualOptions/Unit/ContextualOptionsTests.cs @@ -14,7 +14,7 @@ public class ContextualOptionsTests { private class Receiver : IOptionsContextReceiver { - public List<(string key, object? value)> Received { get; } = new(); + public List<(string key, object? value)> Received { get; } = []; public void Receive(string key, T value) => Received.Add((key, value)); } diff --git a/test/Generators/Microsoft.Gen.Logging/TestClasses/EnumerableTestExtensions.cs b/test/Generators/Microsoft.Gen.Logging/TestClasses/EnumerableTestExtensions.cs index a003da74e78..11f0ecd08de 100644 --- a/test/Generators/Microsoft.Gen.Logging/TestClasses/EnumerableTestExtensions.cs +++ b/test/Generators/Microsoft.Gen.Logging/TestClasses/EnumerableTestExtensions.cs @@ -59,7 +59,7 @@ internal static partial class EnumerableTestExtensions public readonly struct StructEnumerable : IEnumerable { - private static readonly List _numbers = new() { 1, 2, 3 }; + private static readonly List _numbers = [1, 2, 3]; public IEnumerator GetEnumerator() => _numbers.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _numbers.GetEnumerator(); } diff --git a/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs b/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs index d6df799b261..f3207d39570 100644 --- a/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs +++ b/test/Generators/Microsoft.Gen.MetricsReports/Unit/EmitterTests.cs @@ -25,7 +25,7 @@ public class EmitterTests MetricName = "Requests", Summary = "Requests summary.", Kind = InstrumentKind.Counter, - Dimensions = new() { "StatusCode", "ErrorCode"}, + Dimensions = ["StatusCode", "ErrorCode"], DimensionsDescriptions = new Dictionary { { "StatusCode", "Status code for request." }, @@ -37,15 +37,15 @@ public class EmitterTests MetricName = "Latency", Summary = "Latency summary.", Kind = InstrumentKind.Histogram, - Dimensions = new() { "Dim1" }, - DimensionsDescriptions = new() + Dimensions = ["Dim1"], + DimensionsDescriptions = [] }, new ReportedMetricMethod { MetricName = "MemoryUsage", Kind = InstrumentKind.Gauge, - Dimensions = new(), - DimensionsDescriptions = new() + Dimensions = [], + DimensionsDescriptions = [] } } }, @@ -60,15 +60,15 @@ public class EmitterTests MetricName = "Counter", Summary = "Counter summary.", Kind = InstrumentKind.Counter, - Dimensions = new(), - DimensionsDescriptions = new() + Dimensions = [], + DimensionsDescriptions = [] }, new ReportedMetricMethod { MetricName = "Test\\MemoryUsage", Summary = "MemoryUsage summary.", Kind = InstrumentKind.Gauge, - Dimensions = new() { "Path"}, + Dimensions = ["Path"], DimensionsDescriptions = new Dictionary { { "Path", "Test\\Description\\Path" } @@ -117,7 +117,7 @@ public void GetMetricClassDefinition_GivenMetricTypeIsUnknown_ThrowsNotSupported { MetricName = "UnknownMetric", Kind = (InstrumentKind)UnknownMetricType, - Dimensions = new() { "Dim1" } + Dimensions = ["Dim1"] } } }; diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestLogEnrichmentTagCollector.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestLogEnrichmentTagCollector.cs index 61e2f7bada6..2ff605dc3cf 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestLogEnrichmentTagCollector.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/TestLogEnrichmentTagCollector.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Diagnostics.Logging.Test; public class TestLogEnrichmentTagCollector : IEnrichmentTagCollector { - private readonly Dictionary _tags = new(); + private readonly Dictionary _tags = []; public TestLogEnrichmentTagCollector(IEnumerable>? input = null) { diff --git a/test/Libraries/Microsoft.Extensions.AsyncState.Tests/FeaturesPooledPolicyTests.cs b/test/Libraries/Microsoft.Extensions.AsyncState.Tests/FeaturesPooledPolicyTests.cs index 0f28db6c904..909389acb95 100644 --- a/test/Libraries/Microsoft.Extensions.AsyncState.Tests/FeaturesPooledPolicyTests.cs +++ b/test/Libraries/Microsoft.Extensions.AsyncState.Tests/FeaturesPooledPolicyTests.cs @@ -13,7 +13,7 @@ public void Return_ShouldBeTrue() { var policy = new FeaturesPooledPolicy(); - Assert.True(policy.Return(new List())); + Assert.True(policy.Return([])); } [Fact] diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization.Tests/ExceptionSummarizerTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization.Tests/ExceptionSummarizerTests.cs index 1a74a1518a3..e5cd705eacf 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization.Tests/ExceptionSummarizerTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization.Tests/ExceptionSummarizerTests.cs @@ -20,13 +20,13 @@ public class ExceptionSummarizerTests private static readonly List _httpDescriptions = new List { "TaskTimeout", "TaskCanceled" } .Concat(Enum.GetNames(typeof(WebExceptionStatus)).ToList()) .Concat(Enum.GetNames(typeof(SocketError)).ToList()).ToList(); - private static readonly List _httpSupportedExceptionTypes = new() - { + private static readonly List _httpSupportedExceptionTypes = + [ typeof(TaskCanceledException), typeof(OperationCanceledException), typeof(WebException), typeof(SocketException), - }; + ]; private readonly Mock _httpExceptionProviderMock; private readonly IExceptionSummarizer _exceptionSummarizer; diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestLogEnrichmentTagCollector.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestLogEnrichmentTagCollector.cs index 38e30faeb0a..28b14ddc3da 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestLogEnrichmentTagCollector.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestLogEnrichmentTagCollector.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Diagnostics.Enrichment.Test.Internals; public class TestLogEnrichmentTagCollector : IEnrichmentTagCollector { - private readonly Dictionary _tags = new(); + private readonly Dictionary _tags = []; public TestLogEnrichmentTagCollector(IEnumerable>? input = null) { diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestMetricEnrichmentTagCollector.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestMetricEnrichmentTagCollector.cs index 7acad13397f..0b0cfae3f4c 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestMetricEnrichmentTagCollector.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Enrichment/Internals/TestMetricEnrichmentTagCollector.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Diagnostics.Enrichment.Test.Internals; public class TestMetricEnrichmentTagCollector : IEnrichmentTagCollector { - private readonly Dictionary _tags = new(); + private readonly Dictionary _tags = []; public TestMetricEnrichmentTagCollector(IEnumerable>? input = null) { diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerFactoryTests.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerFactoryTests.cs index 9e32068c4da..61d2b7aa7eb 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerFactoryTests.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.Extra.Tests/Logging/ExtendedLoggerFactoryTests.cs @@ -539,7 +539,7 @@ public static void CreateDisposeDisposesInnerServiceProvider() private class InternalScopeLoggerProvider : ILoggerProvider, ILogger { private IExternalScopeProvider _scopeProvider = new LoggerExternalScopeProvider(); - public List LogText { get; set; } = new List(); + public List LogText { get; set; } = []; public void Dispose() { @@ -578,7 +578,7 @@ public void SetScopeProvider(IExternalScopeProvider scopeProvider) public IExternalScopeProvider? ScopeProvider { get; set; } public int BeginScopeCalledTimes { get; set; } - public List LogText { get; set; } = new List(); + public List LogText { get; set; } = []; public void Dispose() { // nop diff --git a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/TestResources.cs b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/TestResources.cs index fe28b35efec..a7377fe019f 100644 --- a/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/TestResources.cs +++ b/test/Libraries/Microsoft.Extensions.Diagnostics.ResourceMonitoring.Tests/Linux/Resources/TestResources.cs @@ -29,7 +29,7 @@ internal sealed class TestResources : IDisposable "/proc" }; - private readonly HashSet _set = new(); + private readonly HashSet _set = []; public readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); public void Dispose() diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs index 0d589f46603..95d2f7b5844 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpClientLoggerTest.cs @@ -173,8 +173,8 @@ public async Task HttpLoggingHandler_AllOptions_LogsOutgoingRequest() Method = HttpMethod.Post, Path = "foo/bar", StatusCode = 200, - ResponseHeaders = new() { new(TestExpectedResponseHeaderKey, Redacted), new(testSharedResponseHeaderKey, Redacted) }, - RequestHeaders = new() { new(TestExpectedRequestHeaderKey, Redacted), new(testSharedRequestHeaderKey, Redacted) }, + ResponseHeaders = [new(TestExpectedResponseHeaderKey, Redacted), new(testSharedResponseHeaderKey, Redacted)], + RequestHeaders = [new(TestExpectedRequestHeaderKey, Redacted), new(testSharedRequestHeaderKey, Redacted)], RequestBody = requestContent, ResponseBody = responseContent, EnrichmentTags = testEnricher.EnrichmentBag @@ -268,8 +268,8 @@ public async Task HttpLoggingHandler_AllOptionsWithLogRequestStart_LogsOutgoingR Method = HttpMethod.Post, Path = "foo/bar", StatusCode = 200, - ResponseHeaders = new() { new(TestResponseHeader, Redacted) }, - RequestHeaders = new() { new(TestRequestHeader, Redacted) }, + ResponseHeaders = [new(TestResponseHeader, Redacted)], + RequestHeaders = [new(TestRequestHeader, Redacted)], RequestBody = requestContent, ResponseBody = responseContent, EnrichmentTags = testEnricher.EnrichmentBag @@ -373,8 +373,8 @@ public async Task HttpLoggingHandler_AllOptionsSendAsyncFailed_LogsRequestInform Method = HttpMethod.Post, Path = "foo/bar", StatusCode = 200, - ResponseHeaders = new() { new(TestResponseHeader, Redacted) }, - RequestHeaders = new() { new(TestRequestHeader, Redacted) }, + ResponseHeaders = [new(TestResponseHeader, Redacted)], + RequestHeaders = [new(TestRequestHeader, Redacted)], RequestBody = requestContent, ResponseBody = responseContent, EnrichmentTags = testEnricher.EnrichmentBag @@ -469,8 +469,8 @@ public async Task HttpLoggingHandler_ReadResponseThrows_LogsException() Method = HttpMethod.Post, Path = "foo/bar", StatusCode = 200, - ResponseHeaders = new() { new(TestResponseHeader, Redacted) }, - RequestHeaders = new() { new(TestRequestHeader, Redacted) }, + ResponseHeaders = [new(TestResponseHeader, Redacted)], + RequestHeaders = [new(TestRequestHeader, Redacted)], RequestBody = requestContent, ResponseBody = responseContent, EnrichmentTags = testEnricher.EnrichmentBag @@ -585,8 +585,8 @@ public async Task HttpLoggingHandler_AllOptionsTransferEncodingIsNotChunked_Logs Method = HttpMethod.Post, Path = "foo/bar", StatusCode = 200, - ResponseHeaders = new() { new(TestResponseHeader, Redacted) }, - RequestHeaders = new() { new(TestRequestHeader, Redacted) }, + ResponseHeaders = [new(TestResponseHeader, Redacted)], + RequestHeaders = [new(TestRequestHeader, Redacted)], RequestBody = requestContent, ResponseBody = responseContent, EnrichmentTags = testEnricher.EnrichmentBag, @@ -847,8 +847,8 @@ public async Task HttpLoggingHandler_AllOptionsTransferEncodingChunked_LogsOutgo Path = "foo/bar", Duration = 1000, StatusCode = 200, - ResponseHeaders = new() { new(TestExpectedResponseHeaderKey, Redacted) }, - RequestHeaders = new() { new(TestExpectedRequestHeaderKey, Redacted) }, + ResponseHeaders = [new(TestExpectedResponseHeaderKey, Redacted)], + RequestHeaders = [new(TestExpectedRequestHeaderKey, Redacted)], RequestBody = requestInput, ResponseBody = responseInput, EnrichmentTags = testEnricher.EnrichmentBag diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs index ad33ed517d8..6012ced34f8 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs @@ -49,8 +49,8 @@ public async Task ReadAsync_AllData_ReturnsLogRecord() Method = HttpMethod.Post, Path = TelemetryConstants.Redacted, StatusCode = 200, - RequestHeaders = new() { new("Header1", Redacted), new("Header3", Redacted) }, - ResponseHeaders = new() { new("Header2", Redacted), new("Header3", Redacted) }, + RequestHeaders = [new("Header1", Redacted), new("Header3", Redacted)], + ResponseHeaders = [new("Header2", Redacted), new("Header3", Redacted)], RequestBody = requestContent, ResponseBody = responseContent, }; @@ -192,8 +192,8 @@ public async Task ReadAsync_AllDataWithRequestMetadataSet_ReturnsLogRecord() Method = HttpMethod.Post, Path = "foo/bar/123", StatusCode = 200, - RequestHeaders = new() { new("Header1", Redacted) }, - ResponseHeaders = new() { new("Header2", Redacted) }, + RequestHeaders = [new("Header1", Redacted)], + ResponseHeaders = [new("Header2", Redacted)], RequestBody = requestContent, ResponseBody = responseContent, }; @@ -272,8 +272,8 @@ public async Task ReadAsync_FormatRequestPathDisabled_ReturnsLogRecordWithRoute( Method = HttpMethod.Post, Path = "foo/bar/{userId}", StatusCode = 200, - RequestHeaders = new() { new("Header1", Redacted) }, - ResponseHeaders = new() { new("Header2", Redacted) }, + RequestHeaders = [new("Header1", Redacted)], + ResponseHeaders = [new("Header2", Redacted)], RequestBody = requestContent, ResponseBody = responseContent, }; @@ -353,7 +353,7 @@ public async Task ReadAsync_RouteParameterRedactionModeNone_ReturnsLogRecordWith Host = host, Method = HttpMethod.Post, Path = "/foo/bar/123", - RequestHeaders = new() { new("Header1", Redacted) }, + RequestHeaders = [new("Header1", Redacted)], RequestBody = requestContent, }; @@ -418,8 +418,8 @@ public async Task ReadAsync_RequestMetadataRequestNameSetAndRouteMissing_Returns Method = HttpMethod.Post, Path = "TestRequest", StatusCode = 200, - RequestHeaders = new() { new("Header1", Redacted) }, - ResponseHeaders = new() { new("Header2", Redacted) }, + RequestHeaders = [new("Header1", Redacted)], + ResponseHeaders = [new("Header2", Redacted)], RequestBody = requestContent, ResponseBody = responseContent, }; @@ -499,8 +499,8 @@ public async Task ReadAsync_NoMetadataUsesRedactedString_ReturnsLogRecord() Method = HttpMethod.Post, Path = TelemetryConstants.Redacted, StatusCode = 200, - RequestHeaders = new() { new("Header1", Redacted) }, - ResponseHeaders = new() { new("Header2", Redacted) }, + RequestHeaders = [new("Header1", Redacted)], + ResponseHeaders = [new("Header2", Redacted)], RequestBody = requestContent, ResponseBody = responseContent, }; @@ -575,8 +575,8 @@ public async Task ReadAsync_MetadataWithoutRequestRouteOrNameUsesConstants_Retur Method = HttpMethod.Post, Path = TelemetryConstants.Unknown, StatusCode = 200, - RequestHeaders = new() { new("Header1", Redacted) }, - ResponseHeaders = new() { new("Header2", Redacted) }, + RequestHeaders = [new("Header1", Redacted)], + ResponseHeaders = [new("Header2", Redacted)], RequestBody = requestContent, ResponseBody = responseContent, }; diff --git a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Hedging/HedgingTests.cs b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Hedging/HedgingTests.cs index db42d34b535..d4d00464298 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Hedging/HedgingTests.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Hedging/HedgingTests.cs @@ -54,7 +54,7 @@ private protected HedgingTests(Func Requests { get; } = new(); + public List Requests { get; } = []; public void Dispose() { diff --git a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Resilience/HttpClientBuilderExtensionsTests.Resilience.cs b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Resilience/HttpClientBuilderExtensionsTests.Resilience.cs index 13d678370a1..bcdd3784c33 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Resilience/HttpClientBuilderExtensionsTests.Resilience.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Resilience/HttpClientBuilderExtensionsTests.Resilience.cs @@ -266,7 +266,7 @@ public void AddResilienceHandler_AuthorityByCustomSelector_NotValidated() private class TestMetricsEnricher : MeteringEnricher { - public List> Tags { get; } = new(); + public List> Tags { get; } = []; public override void Enrich(in EnrichmentContext context) { diff --git a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Routing/RoutingStrategyTest.cs b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Routing/RoutingStrategyTest.cs index 059c56c44d4..143998ff8a0 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Routing/RoutingStrategyTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Resilience.Tests/Routing/RoutingStrategyTest.cs @@ -125,7 +125,7 @@ protected void ReloadHelper( CollectUrls(CreateStrategy()).Should().Equal(urls1); // empty data -> failure - Assert.Throws(() => provider.Reload(new Dictionary())); + Assert.Throws(() => provider.Reload([])); provider.Reload(config2); CollectUrls(CreateStrategy()).Should().Equal(urls2); diff --git a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/ResilienceMetricsEnricherTests.cs b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/ResilienceMetricsEnricherTests.cs index 0bb15773844..df5c60f0c27 100644 --- a/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/ResilienceMetricsEnricherTests.cs +++ b/test/Libraries/Microsoft.Extensions.Resilience.Tests/Resilience/ResilienceMetricsEnricherTests.cs @@ -19,11 +19,11 @@ namespace Microsoft.Extensions.Resilience.Test.Resilience; public class ResilienceMetricsEnricherTests { - private readonly List _outgoingRequestContexts = new(); + private readonly List _outgoingRequestContexts = []; private readonly FailureEventMetricsOptions _options = new(); private Mock? _summarizer = new(MockBehavior.Strict); - private List> _tags = new(); + private List> _tags = []; private IReadOnlyDictionary Tags => _tags.ToDictionary(v => v.Key, v => v.Value); diff --git a/test/Shared/Data.Validation/LengthAttributeTests.cs b/test/Shared/Data.Validation/LengthAttributeTests.cs index 1504d3288aa..368a6cc2d79 100644 --- a/test/Shared/Data.Validation/LengthAttributeTests.cs +++ b/test/Shared/Data.Validation/LengthAttributeTests.cs @@ -379,7 +379,7 @@ public void NakedContext() public class TestOptionsCustomMessage { [Length(5, ErrorMessage = "My custom message for '{0}'.")] - public List CustomMessage { get; set; } = new List(); + public List CustomMessage { get; set; } = []; } [Fact] @@ -399,7 +399,7 @@ public void CustomErrorMessage() public class TestOptionsDefaultMessage { [Length(5)] - public List DefaultMessage { get; set; } = new List(); + public List DefaultMessage { get; set; } = []; } [Fact] From a39a5d6d3dd563ff8bc037dfea877233adcd7298 Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Tue, 10 Oct 2023 18:56:52 +0200 Subject: [PATCH 05/28] Fix file name; (#4541) Place internal files appropriately Co-authored-by: Nikita Balabaev --- .../Logging/{ => Internal}/HttpHeadersRedactor.cs | 0 .../{ => Internal}/IDownstreamDependencyMetadataManager.cs | 0 .../Logging/{ => Internal}/IHttpHeadersRedactor.cs | 0 .../Logging/{ => Internal}/OutgoingRequestContext.cs | 0 .../TelemetryCommonHttpExtensions.cs} | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/{ => Internal}/HttpHeadersRedactor.cs (100%) rename src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/{ => Internal}/IDownstreamDependencyMetadataManager.cs (100%) rename src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/{ => Internal}/IHttpHeadersRedactor.cs (100%) rename src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/{ => Internal}/OutgoingRequestContext.cs (100%) rename src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/{TelemetryCommonExtensions2.cs => Internal/TelemetryCommonHttpExtensions.cs} (94%) diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/HttpHeadersRedactor.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpHeadersRedactor.cs similarity index 100% rename from src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/HttpHeadersRedactor.cs rename to src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpHeadersRedactor.cs diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/IDownstreamDependencyMetadataManager.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IDownstreamDependencyMetadataManager.cs similarity index 100% rename from src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/IDownstreamDependencyMetadataManager.cs rename to src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IDownstreamDependencyMetadataManager.cs diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/IHttpHeadersRedactor.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IHttpHeadersRedactor.cs similarity index 100% rename from src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/IHttpHeadersRedactor.cs rename to src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/IHttpHeadersRedactor.cs diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/OutgoingRequestContext.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/OutgoingRequestContext.cs similarity index 100% rename from src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/OutgoingRequestContext.cs rename to src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/OutgoingRequestContext.cs diff --git a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/TelemetryCommonExtensions2.cs b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/TelemetryCommonHttpExtensions.cs similarity index 94% rename from src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/TelemetryCommonExtensions2.cs rename to src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/TelemetryCommonHttpExtensions.cs index 4345513e5e5..e7ca18c9ca4 100644 --- a/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/TelemetryCommonExtensions2.cs +++ b/src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/TelemetryCommonHttpExtensions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Telemetry.Internal; -internal static class TelemetryCommonExtensions2 +internal static class TelemetryCommonHttpExtensions { public static IServiceCollection AddHttpHeadersRedactor(this IServiceCollection services) { From 239e599528b12a939a62c89a68a7ef380c1037d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Tue, 10 Oct 2023 16:09:51 -0700 Subject: [PATCH 06/28] Add missing dash in build docs (#4549) --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 98d12fa08ab..1af6c48d0e6 100644 --- a/docs/building.md +++ b/docs/building.md @@ -46,7 +46,7 @@ Here are few commands that you will likely use the most: - `build.sh --build`: to build the solution1. - `build.sh --test`: to run all unit tests in the solution1. - `build.sh --vs `: to generate a "filtered" solution and save it as `SDK.sln`. It also performs the "restore" operation. For example: `./build.sh --vs Http,Fakes`.
- If for some reason you wish to generate a solution with all projects you can pass `*` for the keyword, e.g.: `./build.sh -vs '*'` (Note: you have to escape the asterisk or use `set -f` to turn off expansion).
+ If for some reason you wish to generate a solution with all projects you can pass `*` for the keyword, e.g.: `./build.sh --vs '*'` (Note: you have to escape the asterisk or use `set -f` to turn off expansion).
> Under the hood, this invokes `scripts/Slngen.ps1` script, which in turn executes [slngen tool][slngen-tool]. If you want to customize how the "filtered" solution is generated, you will need to invoke `scripts/Slngen.ps1` script directly.
Run `./scripts/Slngen.ps1 -help` for more details. From b271fee0e6035d99ce19714b9579736172260758 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 10:10:06 +1100 Subject: [PATCH 07/28] Update dependencies from https://github.com/dotnet/aspnetcore build 20231009.3 (#4547) Microsoft.AspNetCore.App.Runtime.win-x64 , Microsoft.AspNetCore.Mvc.Testing , Microsoft.AspNetCore.TestHost , Microsoft.Extensions.Caching.StackExchangeRedis , Microsoft.Extensions.Diagnostics.HealthChecks , Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions , Microsoft.Extensions.Features , Microsoft.Extensions.Http.Polly , Microsoft.Extensions.ObjectPool From Version 8.0.0-rtm.23506.5 -> To Version 8.0.0-rtm.23509.3 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 36 ++++++++++++++++++------------------ eng/Versions.props | 18 +++++++++--------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e122fd26f1d..daf4f3319f1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -132,41 +132,41 @@ https://github.com/dotnet/runtime b20f704cc00f390e5560a137deb8f0e64e863e1d - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 - + https://github.com/dotnet/aspnetcore - 6bd37340c54e3b9690102886afca6198a461cb3e + cfc7ac66d89f8e38def01cc19c6d15f4c010c630 diff --git a/eng/Versions.props b/eng/Versions.props index a924cd697eb..e6c43ff6bf6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -62,15 +62,15 @@ 8.0.0-rtm.23478.17 8.0.0-rtm.23478.17 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 - 8.0.0-rtm.23506.5 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 + 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 - 8.0.0-rtm.23509.3 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + 8.0.0-rtm.23510.7 + Include="$(SrcLibrariesDir)\*\*.*proj" + Exclude="$(MSBuildProjectFullPath)" /> From 53171ca387be22ca9ef69993c3894953889b4139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1n=20Guttek?= Date: Mon, 16 Oct 2023 18:06:49 +0200 Subject: [PATCH 18/28] Add try catch block in Enrich method for RequestHeadersLogEnricher (#4566) Co-authored-by: Jan Guttek --- .../Logging/RequestHeadersLogEnricher.cs | 37 ++++++++++------- .../Logging/RequestHeadersEnricherTests.cs | 40 +++++++++++++++++++ 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs index 47fff5f0f4d..8d0d8b028ab 100644 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs +++ b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/RequestHeadersLogEnricher.cs @@ -45,29 +45,36 @@ public RequestHeadersLogEnricher(IHttpContextAccessor httpContextAccessor, IOpti public void Enrich(IEnrichmentTagCollector collector) { - if (_httpContextAccessor.HttpContext?.Request == null) + try { - return; - } + if (_httpContextAccessor.HttpContext?.Request == null) + { + return; + } - var request = _httpContextAccessor.HttpContext.Request; + var request = _httpContextAccessor.HttpContext.Request; - if (_headersDataClasses.Count == 0) - { - return; - } + if (_headersDataClasses.Count == 0) + { + return; + } - if (_headersDataClasses.Count != 0) - { - foreach (var header in _headersDataClasses) + if (_headersDataClasses.Count != 0) { - if (request.Headers.TryGetValue(header.Key, out var headerValue) && !string.IsNullOrEmpty(headerValue)) + foreach (var header in _headersDataClasses) { - var redactor = _redactorProvider!.GetRedactor(header.Value); - var redacted = redactor.Redact(headerValue); - collector.Add(header.Key, redacted); + if (request.Headers.TryGetValue(header.Key, out var headerValue) && !string.IsNullOrEmpty(headerValue)) + { + var redactor = _redactorProvider!.GetRedactor(header.Value); + var redacted = redactor.Redact(headerValue); + collector.Add(header.Key, redacted); + } } } } + catch (ObjectDisposedException) + { + // Noop. + } } } diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs index fc0fccba72d..879fe93abb9 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/RequestHeadersEnricherTests.cs @@ -1,10 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using Microsoft.AspNetCore.Diagnostics.Logging; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.Compliance.Redaction; using Microsoft.Extensions.Compliance.Testing; using Moq; @@ -240,4 +242,42 @@ public void RequestHeadersEnricher_GivenNullRequest_HeaderKeysDataClasses_DoesNo // Assert Assert.Empty(enrichedState); } + + [Fact] + public void RequestHeadersEnricher_ObjectDisposedExceptionIsHandled_DoesNotEnrich() + { + // Arrange + var httpContextMock = new Mock(MockBehavior.Strict); + httpContextMock.SetupGet(c => c.Request.Headers).Throws(new ObjectDisposedException("")); + httpContextMock.SetupGet(c => c.Request.HttpContext).Returns(httpContextMock.Object); + httpContextMock.SetupGet(c => c.Features).Throws(new ObjectDisposedException("")); + + var accessorMock = new Mock(MockBehavior.Strict); + accessorMock.SetupGet(r => r.HttpContext).Returns(httpContextMock.Object); + + var options = new RequestHeadersLogEnricherOptions + { + HeadersDataClasses = new Dictionary + { + { HeaderKey1, FakeClassifications.PublicData } + } + }; + + Mock redactorProviderMock = new Mock(); + redactorProviderMock.Setup(x => x.GetRedactor(FakeClassifications.PublicData)) + .Returns(new FakeRedactor()); + redactorProviderMock.Setup(x => x.GetRedactor(FakeClassifications.PrivateData)) + .Returns(FakeRedactor.Create(new FakeRedactorOptions { RedactionFormat = "redacted:{0}" })); + + var enricher = new RequestHeadersLogEnricher(accessorMock.Object, options.ToOptions(), redactorProviderMock.Object); + + var enrichedProperties = new TestLogEnrichmentTagCollector(); + + // Act + enricher.Enrich(enrichedProperties); + var enrichedState = enrichedProperties.Properties; + + // Assert + Assert.True(enrichedState.Count == 0); + } } From 2fe3f6e0039b5ecf30857f72074e6fc37fc99819 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:54:11 -0700 Subject: [PATCH 19/28] Update dependencies from https://github.com/dotnet/arcade build 20231005.1 (#4569) Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.23463.1 -> To Version 8.0.0-beta.23505.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/common/sdk-task.ps1 | 2 +- eng/common/tools.ps1 | 6 +++--- global.json | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1a4dccab9f5..c6459339e73 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -170,13 +170,13 @@ - + https://github.com/dotnet/arcade - 1d451c32dda2314c721adbf8829e1c0cd4e681ff + e6be64c3e27aeb29f93f6aa751fad972e4ef2d52 - + https://github.com/dotnet/arcade - 1d451c32dda2314c721adbf8829e1c0cd4e681ff + e6be64c3e27aeb29f93f6aa751fad972e4ef2d52 diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index 6c4ac6fec1a..91f8196cc80 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.6.0-2" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.7.2-1" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index aa74ab4a81e..84cfe7cd9cb 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -379,13 +379,13 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } # Minimum VS version to require. - $vsMinVersionReqdStr = '17.6' + $vsMinVersionReqdStr = '17.7' $vsMinVersionReqd = [Version]::new($vsMinVersionReqdStr) # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.6.0-2 - $defaultXCopyMSBuildVersion = '17.6.0-2' + # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/RoslynTools.MSBuild/versions/17.7.2-1 + $defaultXCopyMSBuildVersion = '17.7.2-1' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { diff --git a/global.json b/global.json index 24ad944a079..f62056df938 100644 --- a/global.json +++ b/global.json @@ -16,7 +16,7 @@ "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.2.0", - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23463.1", - "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23463.1" + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23505.1", + "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23505.1" } } From 380709126a65c4d8237c7b5fb4982ea8becc2a1e Mon Sep 17 00:00:00 2001 From: Martin Taillefer Date: Mon, 16 Oct 2023 12:54:54 -0700 Subject: [PATCH 20/28] Add comments around privacy-sensitive info (#4559) Co-authored-by: Martin Taillefer --- .../ExceptionSummary.cs | 16 +++++++++++++--- .../IExceptionSummaryProvider.cs | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/ExceptionSummary.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/ExceptionSummary.cs index fc3178248c6..f47a6a2c9d2 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/ExceptionSummary.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/ExceptionSummary.cs @@ -13,7 +13,10 @@ namespace Microsoft.Extensions.Diagnostics.ExceptionSummarization; /// Metric tags typically support a limited number of distinct values, and as such they are not suitable /// to represent values which are highly variable, such as the result of . /// An exception summary represents a low-cardinality version of an exception's information, suitable for such -/// cases. The summary never includes sensitive information. +/// cases. +/// +/// The property never include sensitive information. +/// But the property might contain sensitive information and should thus not be used in telemetry. /// public readonly struct ExceptionSummary : IEquatable { @@ -42,14 +45,18 @@ public ExceptionSummary(string exceptionType, string description, string additio /// /// Gets the summary description of the exception. /// + /// + /// This is a low cardinality string suitable for use as a metric dimension, and it is guaranteed not to + /// contain privacy-sensitive information. + /// public string Description { get; } /// /// Gets the additional details of the exception. /// /// - /// This string can have a relatively high cardinality and is therefore not suitable as a metric dimension. It - /// is primarily intended for use in low-level diagnostics. + /// This string can have a relatively high cardinality and may contain privacy-sensitive information and is therefore not suitable + /// as a metric dimension. It is primarily intended for use in low-level diagnostics. /// public string AdditionalDetails { get; } @@ -66,6 +73,9 @@ public override int GetHashCode() => HashCode.Combine( /// Gets a string representation of this object. ///
/// A string representing this object. + /// + /// The string returned by this call is not suitable to be used in telemetry as it may contain privacy-sensitive information. + /// public override string ToString() { return AdditionalDetails.Length == 0 diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/IExceptionSummaryProvider.cs b/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/IExceptionSummaryProvider.cs index 7f75f7ac7d9..24cfd0d079e 100644 --- a/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/IExceptionSummaryProvider.cs +++ b/src/Libraries/Microsoft.Extensions.Diagnostics.ExceptionSummarization/IExceptionSummaryProvider.cs @@ -20,7 +20,7 @@ public interface IExceptionSummaryProvider /// Provides the index of the description for the exception along with optional additional data. ///
/// The exception. - /// The additional details of the given exception, if any. + /// The additional details of the given exception, if any. Ideally, this string should not contain any privacy-sensitive information. /// The index of the description. /// /// This method should only get invoked with an exception which is type compatible with a type From abb0301a79e44ceca054668d514bd8c7705835b2 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Mon, 16 Oct 2023 16:59:47 -0700 Subject: [PATCH 21/28] Fixing Package imports for projects targeting net6.0 or net7.0 (#4573) --- .../Microsoft.Extensions.Diagnostics.ExtraAbstractions.props | 0 .../Microsoft.Extensions.Diagnostics.ExtraAbstractions.targets | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/{net8.0 => net6.0}/Microsoft.Extensions.Diagnostics.ExtraAbstractions.props (100%) rename src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/{net8.0 => net6.0}/Microsoft.Extensions.Diagnostics.ExtraAbstractions.targets (100%) diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net8.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.props b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net6.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.props similarity index 100% rename from src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net8.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.props rename to src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net6.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.props diff --git a/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net8.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.targets b/src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net6.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.targets similarity index 100% rename from src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net8.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.targets rename to src/Libraries/Microsoft.Extensions.Diagnostics.ExtraAbstractions/buildTransitive/net6.0/Microsoft.Extensions.Diagnostics.ExtraAbstractions.targets From b2f5960994d90669fc4eb86bb747f761cc3940ee Mon Sep 17 00:00:00 2001 From: Igor Velikorossov Date: Tue, 17 Oct 2023 11:50:05 +1100 Subject: [PATCH 22/28] Correct CoC Resolves https://github.com/dotnet/org-policy-violations/issues/5035 --- CODE_OF_CONDUCT.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f9ba8cf65f3..775f221c98e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,9 +1,6 @@ -# Microsoft Open Source Code of Conduct +# Code of Conduct -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +This project has adopted the code of conduct defined by the Contributor Covenant +to clarify expected behavior in our community. -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). From 0409f97fb34ba111e16e1206c2bf24206c0d3ff8 Mon Sep 17 00:00:00 2001 From: Nikita Balabaev Date: Tue, 17 Oct 2023 10:48:57 +0200 Subject: [PATCH 23/28] remove leftover file --- .../ApiLifecycle/AnalysisModelTest.cs | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100644 test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs diff --git a/test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs b/test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs deleted file mode 100644 index 2501dc537c9..00000000000 --- a/test/Analyzers/Microsoft.Analyzers.Local.Tests/ApiLifecycle/AnalysisModelTest.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Extensions.LocalAnalyzers.ApiLifecycle.Model; -using Xunit; - -namespace Microsoft.Extensions.LocalAnalyzers.ApiLifecycle.Test; - -public class AnalysisModelTest -{ - [Fact] - public void Field_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() - { - var field = new Field([]); - - Assert.Equal(string.Empty, field.Member); - Assert.Equal(Stage.Experimental, field.Stage); - } - - [Fact] - public void PublicMember_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() - { - var member = new TypeDef([]); - - Assert.Equal(string.Empty, member.ModifiersAndName); - Assert.Equal(Stage.Experimental, member.Stage); - Assert.Equal(Array.Empty(), member.Fields); - Assert.Equal(Array.Empty(), member.BaseTypes); - Assert.Equal(Array.Empty(), member.Constraints); - Assert.Equal(Array.Empty(), member.Methods); - Assert.Equal(Array.Empty(), member.Properties); - } - - [Fact] - public void Prop_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() - { - var prop = new Prop([]); - - Assert.Equal(string.Empty, prop.Member); - Assert.Equal(Stage.Experimental, prop.Stage); - } - - [Fact] - public void PackageAnalysis_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() - { - var analysis = new AssemblyAnalysis(Assembly.Empty); - - Assert.Equal(Assembly.Empty, analysis.Assembly); - Assert.Empty(analysis.MissingProperties); - Assert.Empty(analysis.MissingBaseTypes); - Assert.Empty(analysis.FoundInBaseline); - Assert.Empty(analysis.NotFoundInBaseline); - Assert.Empty(analysis.MissingTypes); - Assert.Empty(analysis.MissingConstraints); - Assert.Empty(analysis.MissingFields); - Assert.Empty(analysis.MissingMethods); - Assert.Empty(analysis.MissingProperties); - } - - [Fact] - public void Package_Fallbacks_To_NotNull_Defaults_When_Value_Not_Found_In_Json() - { - var package = new Assembly([]); - - Assert.Equal(Array.Empty(), package.Types); - Assert.Equal(string.Empty, package.Name); - } - - [Fact] - public void Method_FallbacksTo_NotNull_Defaults_When_Value_Not_Found_In_Json() - { - var method = new Method([]); - - Assert.Equal(string.Empty, method.Member); - Assert.Equal(Stage.Experimental, method.Stage); - } -} From 7d8c644e100bdcd4a4406daa8914b68a228f7fbb Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Tue, 17 Oct 2023 13:30:52 -0700 Subject: [PATCH 24/28] Delete src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs --- .../Logging/HttpLoggingMiddleware.cs | 459 ------------------ 1 file changed, 459 deletions(-) delete mode 100644 src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs diff --git a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs b/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs deleted file mode 100644 index 0b4ca6aa107..00000000000 --- a/src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingMiddleware.cs +++ /dev/null @@ -1,459 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Buffers; -using System.Collections.Frozen; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.Logging; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Mvc.Formatters; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Compliance.Classification; -using Microsoft.Extensions.Compliance.Redaction; -using Microsoft.Extensions.Http.Diagnostics; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.ObjectPool; -using Microsoft.Extensions.Options; -using Microsoft.Shared.Diagnostics; -using Microsoft.Shared.Pools; - -namespace Microsoft.AspNetCore.Diagnostics.Logging; - -internal sealed class HttpLoggingMiddleware : IMiddleware -{ - // These three fields are "internal" solely for testing purposes: - internal int BodyReadSizeLimit; - internal TimeProvider TimeProvider = TimeProvider.System; - internal Func> GetResponseBodyInterceptedData = static stream => stream.GetInterceptedSequence(); - internal TimeSpan RequestBodyReadTimeout; - - private readonly bool _logRequestStart; - private readonly bool _logRequestBody; - private readonly bool _logResponseBody; - private readonly bool _logRequestHeaders; - private readonly bool _logResponseHeaders; - - private readonly IncomingPathLoggingMode _requestPathLogMode; - private readonly HttpRouteParameterRedactionMode _parameterRedactionMode; - private readonly ILogger _logger; - private readonly IHttpRouteParser _httpRouteParser; - private readonly IHttpRouteFormatter _httpRouteFormatter; - private readonly IIncomingHttpRouteUtility _httpRouteUtility; - private readonly HeaderReader _requestHeadersReader; - private readonly HeaderReader _responseHeadersReader; - private readonly string[] _excludePathStartsWith; - private readonly IHttpLogEnricher[] _enrichers; - private readonly MediaType[] _requestMediaTypes; - private readonly MediaType[] _responseMediaTypes; - private readonly FrozenDictionary _parametersToRedactMap; - - private readonly ObjectPool _logRecordPool = - PoolFactory.CreatePool(); - - private readonly ObjectPool>> _headersPool = - PoolFactory.CreateListPool>(); - - [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Technical debt accepted.")] - public HttpLoggingMiddleware( - IOptions options, - ILogger logger, - IEnumerable httpLogEnrichers, - IHttpRouteParser httpRouteParser, - IHttpRouteFormatter httpRouteFormatter, - IRedactorProvider redactorProvider, - IIncomingHttpRouteUtility httpRouteUtility, - IDebuggerState? debugger = null) - { - var optionsValue = options.Value; - _logger = logger; - _httpRouteParser = httpRouteParser; - _httpRouteFormatter = httpRouteFormatter; - _httpRouteUtility = httpRouteUtility; - _logRequestStart = optionsValue.LogRequestStart; - if (optionsValue.LogBody) - { - _logRequestBody = optionsValue.RequestBodyContentTypes.Count > 0; - _logResponseBody = optionsValue.ResponseBodyContentTypes.Count > 0; - } - - _parametersToRedactMap = optionsValue.RouteParameterDataClasses.ToFrozenDictionary(StringComparer.Ordinal); - - _requestPathLogMode = EnsureRequestPathLoggingModeIsValid(optionsValue.RequestPathLoggingMode); - _parameterRedactionMode = optionsValue.RequestPathParameterRedactionMode; - - BodyReadSizeLimit = optionsValue.BodySizeLimit; - - debugger ??= DebuggerState.System; - RequestBodyReadTimeout = debugger.IsAttached - ? Timeout.InfiniteTimeSpan - : optionsValue.RequestBodyReadTimeout; - - _requestMediaTypes = optionsValue.RequestBodyContentTypes - .Select(static x => new MediaType(x)) - .ToArray(); - - _responseMediaTypes = optionsValue.ResponseBodyContentTypes - .Select(static x => new MediaType(x)) - .ToArray(); - - _logRequestHeaders = optionsValue.RequestHeadersDataClasses.Count > 0; - _logResponseHeaders = optionsValue.ResponseHeadersDataClasses.Count > 0; - _requestHeadersReader = new(optionsValue.RequestHeadersDataClasses, redactorProvider); - _responseHeadersReader = new(optionsValue.ResponseHeadersDataClasses, redactorProvider); - - _excludePathStartsWith = optionsValue.ExcludePathStartsWith.ToArray(); - - _enrichers = httpLogEnrichers.ToArray(); - - // There's no need to use this middleware, - // so log a warning and hope that "LogLevel.Warning" is enabled: - if (!_logger.IsEnabled(Log.DefaultLogLevel)) - { - _logger.MiddlewareIsMisused(Log.DefaultLogLevel, nameof(HttpLoggingApplicationBuilderExtensions.UseHttpLoggingMiddleware)); - } - } - - public Task InvokeAsync(HttpContext context, RequestDelegate next) - { - if (ShouldExcludePath(context.Request.Path)) - { - return next(context); - } - else - { - return InvokeAsyncWithPathAsync(context, next); - } - } - - private static IncomingPathLoggingMode EnsureRequestPathLoggingModeIsValid(IncomingPathLoggingMode mode) - => mode switch - { - IncomingPathLoggingMode.Structured or IncomingPathLoggingMode.Formatted => mode, - _ => throw new InvalidOperationException($"Unsupported value '{mode}' for enum type '{nameof(IncomingPathLoggingMode)}'"), - }; - - private async Task InvokeAsyncWithPathAsync(HttpContext context, RequestDelegate next) - { - ResponseInterceptingStream? bufferingResponseStream = null; - string? requestBody = null; - var timestamp = TimeProvider.GetTimestamp(); - try - { - if (_logResponseBody) - { - // Swapping response stream: - var oldFeature = context.Features.Get()!; - bufferingResponseStream = ResponseInterceptingStreamPool.Get(oldFeature, BodyReadSizeLimit); - context.Features.Set(bufferingResponseStream); - } - - requestBody = _logRequestBody - ? await GetRequestBodyAsync(context.Request, context.RequestAborted).ConfigureAwait(false) - : null; - - if (_logRequestStart) - { - LogRequest(context, timestamp, isRequestStart: true, requestBody, responseBody: null); - } - - await next(context).ConfigureAwait(false); - - var responseBody = _logResponseBody - ? GetResponseBody(context.Response, bufferingResponseStream!) - : null; - - context.Response.OnCompleted(() => - { - LogRequest(context, timestamp, isRequestStart: false, requestBody, responseBody); - - return Task.CompletedTask; - }); - } - catch (Exception ex) - { - // Even if the response body has been already read, we can re-read it safely: - var responseBody = _logResponseBody - ? GetResponseBody(context.Response, bufferingResponseStream!) - : null; - - LogRequest(context, timestamp, isRequestStart: false, requestBody, responseBody, ex); - - throw; - } - finally - { - if (bufferingResponseStream is not null) - { - context.Features.Set(bufferingResponseStream.InnerBodyFeature); - ResponseInterceptingStreamPool.Return(bufferingResponseStream); - } - } - } - - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Intentional")] - private string? GetResponseBody(HttpResponse response, ResponseInterceptingStream stream) - { - if (!MediaTypeSetExtensions.Covers(_responseMediaTypes, response.ContentType)) - { - return null; - } - - try - { - var sequenceSpan = GetResponseBodyInterceptedData(stream).Span; - - return Encoding.UTF8.GetString(sequenceSpan); - } - catch (Exception ex) - { - // We are intentionally catching and logging any exceptions which may happen. - _logger.ErrorReadingResponseBody(ex); - return null; - } - } - - private void LogRequest( - HttpContext context, - long timestamp, - bool isRequestStart, - string? requestBody, - string? responseBody, - Exception? exception = null) - { - const int StatusCodeOnException = 0; - const int LowestUnsuccessfulStatusCode = 400; - - // Don't get a tag collector for "RequestStart" log record: - var collector = isRequestStart || _enrichers.Length == 0 - ? null - : LogMethodHelper.GetHelper(); - - var requestHeaders = _logRequestHeaders - ? _headersPool.Get() - : null; - - // Checking response headers, since we can possibly don't have a response: - var responseHeaders = _logResponseHeaders && context.Response.Headers.Count > 0 - ? _headersPool.Get() - : null; - - var logRecord = _logRecordPool.Get(); - try - { - logRecord.RequestBody = requestBody; - logRecord.ResponseBody = responseBody; - logRecord.RequestHeaders = requestHeaders; - logRecord.ResponseHeaders = responseHeaders; - - if (requestHeaders != null) - { - _requestHeadersReader.Read(context.Request.Headers, requestHeaders); - } - - if (responseHeaders != null) - { - _responseHeadersReader.Read(context.Response.Headers, responseHeaders); - } - - FillLogRecord(logRecord, context, collector); - - if (isRequestStart) - { - // Don't emit both status code and duration tags on request start: - logRecord.Duration = null; - logRecord.StatusCode = null; - } - else - { - // Catching duration at the end: - logRecord.Duration = (long)TimeProvider.GetElapsedTime(timestamp).TotalMilliseconds; - } - - if (exception == null) - { - _logger.IncomingRequest(logRecord); - } - else - { - // Logging status code == 0 when exception occurs and no middleware has set a meaningful status code: - if (logRecord.StatusCode < LowestUnsuccessfulStatusCode) - { - logRecord.StatusCode = StatusCodeOnException; - } - - _logger.RequestProcessingError(exception, logRecord); - } - } - finally - { - if (logRecord.PathParameters != null) - { - ArrayPool.Shared.Return(logRecord.PathParameters); - logRecord.PathParameters = null; - } - - if (collector != null) - { - LogMethodHelper.ReturnHelper(collector); - logRecord.EnrichmentPropertyBag = null; - } - - if (requestHeaders != null) - { - _headersPool.Return(requestHeaders); - logRecord.RequestHeaders = null; - } - - if (responseHeaders != null) - { - _headersPool.Return(responseHeaders); - logRecord.ResponseHeaders = null; - } - - // Return log record at the end: - _logRecordPool.Return(logRecord); - } - } - - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Intentional")] - private async Task GetRequestBodyAsync(HttpRequest request, CancellationToken token) - { - if (!MediaTypeSetExtensions.Covers(_requestMediaTypes, request.ContentType)) - { - return null; - } - - try - { - var sequence = - await request.ReadBodyAsync(RequestBodyReadTimeout, BodyReadSizeLimit, token) - .ConfigureAwait(false); - -#if NET5_0_OR_GREATER - var stringifiedBody = Encoding.UTF8.GetString(in sequence); -#else - string stringifiedBody; - if (sequence.IsSingleSegment) - { - stringifiedBody = Encoding.UTF8.GetString(sequence.FirstSpan); - } - else - { - var buffer = ArrayPool.Shared.Rent((int)sequence.Length); - try - { - sequence.CopyTo(buffer); - stringifiedBody = Encoding.UTF8.GetString(buffer.AsSpan(0, (int)sequence.Length)); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } -#endif - - return stringifiedBody; - } - catch (OperationCanceledException) - { - // Rethrow cancellation exceptions. - throw; - } - catch (Exception ex) - { - // We are intentionally catching and logging any exceptions which may happen. - _logger.ErrorReadingRequestBody(ex); - return null; - } - } - - private void FillLogRecord( - IncomingRequestLogRecord logRecord, - HttpContext context, - LogMethodHelper? collector) - { - var request = context.Request; - var response = context.Response; - - string path = TelemetryConstants.Unknown; - var pathParamsCount = 0; - - if (_parameterRedactionMode != HttpRouteParameterRedactionMode.None) - { - var endpoint = context.GetEndpoint() as RouteEndpoint; - - if (endpoint?.RoutePattern.RawText != null) - { - var httpRoute = endpoint.RoutePattern.RawText; - var paramsToRedact = _httpRouteUtility.GetSensitiveParameters(httpRoute, request, _parametersToRedactMap); - - var routeSegments = _httpRouteParser.ParseRoute(httpRoute); - - if (_requestPathLogMode == IncomingPathLoggingMode.Formatted) - { - path = _httpRouteFormatter.Format(in routeSegments, request.Path, _parameterRedactionMode, paramsToRedact); - logRecord.PathParameters = null; - } - else - { - // Case when logging mode is IncomingPathLoggingMode.Structured - path = httpRoute; - var routeParams = ArrayPool.Shared.Rent(routeSegments.ParameterCount); - - // Setting this value right away to be able to return it back to pool in a callee's "finally" block: - logRecord.PathParameters = routeParams; - if (_httpRouteParser.TryExtractParameters(request.Path, in routeSegments, _parameterRedactionMode, paramsToRedact, ref routeParams)) - { - pathParamsCount = routeSegments.ParameterCount; - } - } - } - else - { - logRecord.PathParameters = null; - } - } - else if (request.Path.HasValue) - { - path = request.Path.Value!; - } - - // We need to set all the values (logRecord was taken from the pool): - logRecord.Path = path; - logRecord.PathParametersCount = pathParamsCount; - logRecord.Method = request.Method; - logRecord.StatusCode = response.StatusCode; - logRecord.Host = request.Host.Value; - - if (collector != null) - { - foreach (var enricher in _enrichers) - { - enricher.Enrich(collector, request, response); - } - } - - logRecord.EnrichmentPropertyBag = collector; - } - - private bool ShouldExcludePath(string path) - { - foreach (var excludedPath in _excludePathStartsWith) - { - if (path.StartsWith(excludedPath, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } -} From aed84325ddb71151222b22dab8856923c676f413 Mon Sep 17 00:00:00 2001 From: Igor Velikorossov Date: Wed, 18 Oct 2023 09:03:42 +1100 Subject: [PATCH 25/28] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index df85cc0f432..133a82b9c5d 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,6 @@ This project has adopted the code of conduct defined by the [Contributor Covenan General .NET OSS discussions: [.NET Foundation Discussions](https://github.com/dotnet-foundation/Home/discussions) -## Code of Conduct - -This project uses the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct) to define expected conduct in our community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at conduct@dotnetfoundation.org. - ## License .NET (including the runtime repo) is licensed under the [MIT](LICENSE.TXT) license. From d83193c495220958ab959de947c8e0fc6d8b12ce Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 18 Oct 2023 14:06:56 -0700 Subject: [PATCH 26/28] Update sdk, runtimes --- global.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 31931fe03d2..0e713251600 100644 --- a/global.json +++ b/global.json @@ -1,16 +1,16 @@ { "sdk": { - "version": "9.0.100-alpha.1.23504.14" + "version": "9.0.100-alpha.1.23517.3" }, "tools": { - "dotnet": "9.0.100-alpha.1.23504.14", + "dotnet": "9.0.100-alpha.1.23517.3", "runtimes": { "dotnet/x64": [ - "8.0.0-rtm.23475.7", + "8.0.0-rtm.23516.15", "$(MicrosoftNETCoreAppRuntimewinx64Version)" ], "aspnetcore/x64": [ - "8.0.0-rtm.23475.5", + "8.0.0-rtm.23517.6", "$(MicrosoftAspNetCoreAppRuntimewinx64Version)" ] } From 7ee92d8ee433e1e09c493f3686ec2df73584c6fa Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 23 Oct 2023 11:28:34 -0700 Subject: [PATCH 27/28] Update SDK, fix warnings --- global.json | 4 +- src/Shared/Pools/PooledListPolicy.cs | 2 +- .../Logging/AcceptanceTests.cs | 53 +++---------------- .../Logging/HttpRequestReaderTest.cs | 4 +- 4 files changed, 13 insertions(+), 50 deletions(-) diff --git a/global.json b/global.json index 0e713251600..13ee5dc2b0d 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "9.0.100-alpha.1.23517.3" + "version": "9.0.100-alpha.1.23523.1" }, "tools": { - "dotnet": "9.0.100-alpha.1.23517.3", + "dotnet": "9.0.100-alpha.1.23523.1", "runtimes": { "dotnet/x64": [ "8.0.0-rtm.23516.15", diff --git a/src/Shared/Pools/PooledListPolicy.cs b/src/Shared/Pools/PooledListPolicy.cs index b74469a0abe..8ad58dd5bb3 100644 --- a/src/Shared/Pools/PooledListPolicy.cs +++ b/src/Shared/Pools/PooledListPolicy.cs @@ -19,7 +19,7 @@ private PooledListPolicy() { } - public override List Create() => new(); + public override List Create() => []; public override bool Return(List obj) { diff --git a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs index 67479d3ee46..738bdb9b3e1 100644 --- a/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs +++ b/test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs @@ -266,41 +266,6 @@ await RunAsync( }); } - [Fact] - public async Task HttpLogging_WhenMultiSegmentRequestPipe_LogRequestBody() - { - await RunAsync( - LogLevel.Information, - services => services.AddHttpLogging(x => - { - x.RequestBodyContentTypes.Add("text/*"); - x.LogBody = true; - }), - async (logCollector, client) => - { - const string Content = "Whatever..."; - - using var content = new StringContent(Content, null, MediaTypeNames.Text.Plain); - using var response = await client.PostAsync("/multi-segment-pipe", content); - Assert.True(response.IsSuccessStatusCode); - - await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); - - Assert.Equal(1, logCollector.Count); - Assert.Null(logCollector.LatestRecord.Exception); - Assert.Equal(LogLevel.Information, logCollector.LatestRecord.Level); - Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category); - - var responseStatus = ((int)response.StatusCode).ToInvariantString(); - var state = logCollector.LatestRecord.StructuredState; - - Assert.Equal(7, state!.Count); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString()); - Assert.Single(state, x => x.Key == HttpLoggingTagNames.RequestBody && x.Value == "Test Segment"); - }); - } - [Fact] public async Task HttpLogging_WhenLogLevelInfo_LogRequestStart() { @@ -562,20 +527,18 @@ async static (logCollector, client) => var sixthRecord = logRecords[5].StructuredState; Assert.Equal(5, firstRecord!.Count); - Assert.Equal(1, secondRecord!.Count); + Assert.Single(secondRecord!); Assert.Equal(5, fourthRecord!.Count); - Assert.Equal(1, fithRecord!.Count); + Assert.Single(fithRecord!); Assert.DoesNotContain(firstRecord, x => x.Key == HttpLoggingTagNames.StatusCode); Assert.DoesNotContain(firstRecord, x => x.Key == HttpLoggingTagNames.Duration); - Assert.DoesNotContain(secondRecord, x => x.Key == HttpLoggingTagNames.Duration); + Assert.DoesNotContain(secondRecord!, x => x.Key == HttpLoggingTagNames.Duration); Assert.DoesNotContain(fourthRecord, x => x.Key == HttpLoggingTagNames.StatusCode); Assert.DoesNotContain(fourthRecord, x => x.Key == HttpLoggingTagNames.Duration); - Assert.DoesNotContain(fithRecord, x => x.Key == HttpLoggingTagNames.Duration); + Assert.DoesNotContain(fithRecord!, x => x.Key == HttpLoggingTagNames.Duration); - Assert.Equal(1, secondRecord!.Count); - Assert.Equal(1, fithRecord!.Count); - Assert.Single(secondRecord, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); - Assert.Single(fithRecord, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); + Assert.Single(secondRecord!, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); + Assert.Single(fithRecord!, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus); Assert.Equal(2, thirdRecord!.Count); Assert.Equal(2, sixthRecord!.Count); @@ -755,7 +718,7 @@ await RunAsync( }), async (logCollector, client) => { - using var response = await client.GetAsync("").ConfigureAwait(false); + using var response = await client.GetAsync(""); Assert.True(response.IsSuccessStatusCode); Assert.Equal(0, logCollector.Count); @@ -771,7 +734,7 @@ await RunAsync( .AddHttpLogEnricher(), async (logCollector, client) => { - using var response = await client.GetAsync("").ConfigureAwait(false); + using var response = await client.GetAsync(""); Assert.True(response.IsSuccessStatusCode); await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout); diff --git a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs index 4084cae4ad4..726000a41f9 100644 --- a/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs +++ b/test/Libraries/Microsoft.Extensions.Http.Diagnostics.Tests/Logging/HttpRequestReaderTest.cs @@ -162,8 +162,8 @@ public async Task ReadAsync_NoHost_ReturnsLogRecordWithoutHost() var actualRecord = new LogRecord(); var requestHeadersBuffer = new List>(); var responseHeadersBuffer = new List>(); - await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None).ConfigureAwait(false); - await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None).ConfigureAwait(false); + await reader.ReadRequestAsync(actualRecord, httpRequestMessage, requestHeadersBuffer, CancellationToken.None); + await reader.ReadResponseAsync(actualRecord, httpResponseMessage, responseHeadersBuffer, CancellationToken.None); actualRecord.Should().BeEquivalentTo( expectedRecord, From 1f0c77f2e070905433d559ad9d9627e697486eee Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Fri, 27 Oct 2023 10:15:51 -0700 Subject: [PATCH 28/28] Apply suggestions from code review --- global.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/global.json b/global.json index 13ee5dc2b0d..f918d558863 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "9.0.100-alpha.1.23523.1" + "version": "9.0.100-alpha.1.23524.8" }, "tools": { - "dotnet": "9.0.100-alpha.1.23523.1", + "dotnet": "9.0.100-alpha.1.23524.8", "runtimes": { "dotnet/x64": [ "8.0.0-rtm.23516.15",