From 5af7e9cf40920e93e4385f9ec5a4dd4e44487925 Mon Sep 17 00:00:00 2001 From: John Korsnes Date: Sun, 16 Feb 2025 19:56:36 +0100 Subject: [PATCH] .NET 9 +semver:major --- .editorconfig | 14 ++-- .github/workflows/CI.yml | 16 ++--- .github/workflows/PreRelease.yml | 19 ++---- .github/workflows/Release.yml | 9 ++- README.md | 20 +++--- samples/With/Program.cs | 5 +- samples/With/Properties/launchSettings.json | 2 +- samples/With/With.csproj | 10 +-- samples/With/Worker.cs | 2 +- samples/Without/Program.cs | 10 +-- samples/Without/Without.csproj | 8 +-- samples/Without/Worker.cs | 2 +- source/MinimalHttpLogger/MinimalHttpLogger.cs | 65 ++++++++++--------- .../MinimalHttpLogger.csproj | 29 ++++----- 14 files changed, 99 insertions(+), 112 deletions(-) diff --git a/.editorconfig b/.editorconfig index c93d988..e3e6e54 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,7 +16,6 @@ indent_style = space tab_width = 4 # New line preferences -insert_final_newline = true trim_trailing_whitespace = true @@ -67,9 +66,9 @@ dotnet_code_quality_unused_parameters = all:suggestion #### C# Coding Conventions #### # var preferences -csharp_style_var_elsewhere = false:silent -csharp_style_var_for_built_in_types = false:silent -csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = true +csharp_style_var_for_built_in_types = true +csharp_style_var_when_type_is_apparent = true # Expression-bodied members csharp_style_expression_bodied_accessors = true:silent @@ -89,7 +88,7 @@ csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_conditional_delegate_call = true:suggestion # Modifier preferences -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async +csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async # Code-block preferences csharp_prefer_braces = true:silent @@ -103,7 +102,10 @@ csharp_style_unused_value_assignment_preference = discard_variable:suggestion csharp_style_unused_value_expression_statement_preference = discard_variable:silent # C# 10 -csharp_style_namespace_declarations = file_scoped:warning +csharp_style_namespace_declarations = file_scoped:error +csharp_style_prefer_primary_constructors = true +dotnet_diagnostic.IDE0290.severity = error + #### C# Formatting Rules #### diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6b32f82..4a51353 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -7,14 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x - - name: Restore dependencies - run: dotnet restore source - - name: Build - run: dotnet build --no-restore source - - name: Test - run: dotnet test source /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --logger "GitHubActions;report-warnings=false" + dotnet-version: 9.0.x + - run: dotnet restore source + - run: dotnet build --no-restore source + - run: dotnet test source /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --logger "GitHubActions;report-warnings=false" diff --git a/.github/workflows/PreRelease.yml b/.github/workflows/PreRelease.yml index 1bb8545..f725d48 100644 --- a/.github/workflows/PreRelease.yml +++ b/.github/workflows/PreRelease.yml @@ -8,24 +8,19 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - run: echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0 + - uses: gittools/actions/gitversion/setup@v0 with: versionSpec: "5.x" - - name: Determine Version + - uses: gittools/actions/gitversion/execute@v0 id: gitversion - uses: gittools/actions/gitversion/execute@v0 with: useConfigFile: true - - name: Setup .NET - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x - - name: Pack - run: dotnet pack source /p:Version=${{ steps.gitversion.outputs.NuGetVersionV2 }}-${{ steps.gitversion.outputs.ShortSha }} /p:InformationalVersion=${{ steps.gitversion.outputs.informationalVersion }} /p:PackageReleaseNotes="https://github.com/$GITHUB_REPOSITORY/releases/tag/${{ steps.gitversion.outputs.NuGetVersionV2 }}" -o ./releases - - name: Publish - run: dotnet nuget push ./releases/**/*.nupkg -k=${{ secrets.NUGETORGAPIKEY }} -s=nuget.org + dotnet-version: 9.0.x + - run: dotnet pack source /p:Version=${{ steps.gitversion.outputs.NuGetVersionV2 }}-${{ steps.gitversion.outputs.ShortSha }} /p:InformationalVersion=${{ steps.gitversion.outputs.informationalVersion }} /p:PackageReleaseNotes="https://github.com/$GITHUB_REPOSITORY/releases/tag/${{ steps.gitversion.outputs.NuGetVersionV2 }}" -o ./releases + - run: dotnet nuget push ./releases/**/*.nupkg -k=${{ secrets.NUGETORGAPIKEY }} -s=nuget.org diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index 6e5badb..26c0bad 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -8,12 +8,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - run: echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0 + - uses: gittools/actions/gitversion/setup@v0 with: versionSpec: "5.x" - name: Determine Version @@ -22,9 +21,9 @@ jobs: with: useConfigFile: true - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Pack run: dotnet pack source /p:Version=${{ steps.gitversion.outputs.majorMinorPatch }} /p:InformationalVersion=${{ steps.gitversion.outputs.informationalVersion }} /p:PackageReleaseNotes="https://github.com/$GITHUB_REPOSITORY/releases/tag/${{ steps.gitversion.outputs.majorMinorPatch }}" -o ./releases - name: Publish diff --git a/README.md b/README.md index 1896038..1de33bb 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,28 @@ # MinimalHttpLogger [![Build](https://github.com/johnkors/MinimalHttpLogger/workflows/CI/badge.svg)](https://github.com/johnkors/MinimalHttpLogger/actions) - [![NuGet](https://img.shields.io/nuget/v/MinimalHttpLogger.svg)](https://www.nuget.org/packages/MinimalHttpLogger/) +[![NuGet](https://img.shields.io/nuget/v/MinimalHttpLogger.svg)](https://www.nuget.org/packages/MinimalHttpLogger/) [![NuGet](https://img.shields.io/nuget/dt/MinimalHttpLogger.svg)](https://www.nuget.org/packages/MinimalHttpLogger/) - ## Why? -My logs were -* hard to read -* filling up space(*) + +My logs were + +* hard to read +* filling up space(*) ## What is this? -It's not possible to configure the log pattern of the Microsoft.Extensions.Http based HttpClient loggers. To modify, one has to replace them. This package replaces the default loggers with a logger that: +It's not possible to configure the log pattern of the Microsoft.Extensions.Http based HttpClient loggers. To modify, one +has to replace them. This package replaces the default loggers with a logger that: -1. Reduces the number of log statements on httpclient requests from 4 to 1 +1. Reduces the number of log statements on httpclient requests from 4 to 1 2. Logs 1 aggregated log statement: `{Method} {Uri} - {StatusCode} {StatusCodeLiteral} in {Time}ms` - ### Change in output Before: + ```log info: Start processing HTTP request GET https://www.google.com/ info: Sending HTTP request GET https://www.google.com/ @@ -29,11 +31,11 @@ info: End processing HTTP request after 188.8026ms - 200 ``` After: + ```log info: GET https://www.google.com/ - 200 OK in 186.4883ms ``` - ## Install ```sh diff --git a/samples/With/Program.cs b/samples/With/Program.cs index b919309..50839ec 100644 --- a/samples/With/Program.cs +++ b/samples/With/Program.cs @@ -1,6 +1,6 @@ using With; -IHost host = Host.CreateDefaultBuilder(args) +var host = Host.CreateDefaultBuilder(args) .ConfigureLogging((c, b) => { b.AddSimpleConsole(o => { o.SingleLine = true; }); @@ -18,6 +18,3 @@ .Build(); await host.RunAsync(); - - - diff --git a/samples/With/Properties/launchSettings.json b/samples/With/Properties/launchSettings.json index bc15687..3a46e50 100644 --- a/samples/With/Properties/launchSettings.json +++ b/samples/With/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "Without": { + "With": { "commandName": "Project", "dotnetRunMessages": true, "environmentVariables": { diff --git a/samples/With/With.csproj b/samples/With/With.csproj index 249348e..02fa076 100644 --- a/samples/With/With.csproj +++ b/samples/With/With.csproj @@ -1,17 +1,17 @@ - net8.0 + net9.0 enable - - - + + + - + diff --git a/samples/With/Worker.cs b/samples/With/Worker.cs index 8a83971..8ef7c48 100644 --- a/samples/With/Worker.cs +++ b/samples/With/Worker.cs @@ -24,7 +24,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Timeout!"); } - catch(Exception e) + catch (Exception e) { _logger.LogError(e.Message); } diff --git a/samples/Without/Program.cs b/samples/Without/Program.cs index b364bfd..9903d08 100644 --- a/samples/Without/Program.cs +++ b/samples/Without/Program.cs @@ -1,7 +1,9 @@ using Without; -IHost host = Host.CreateDefaultBuilder(args) - .ConfigureLogging((c, b) => +AppContext.SetSwitch("System.Net.Http.DisableUriRedaction", true); + +var host = Host.CreateDefaultBuilder(args) + .ConfigureLogging((_, b) => { b.AddSimpleConsole(o => { o.SingleLine = true; }); }) @@ -12,11 +14,9 @@ hostOptions.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.Ignore; }); services.AddHttpClient(); + services.AddHostedService(); }) .Build(); await host.RunAsync(); - - - diff --git a/samples/Without/Without.csproj b/samples/Without/Without.csproj index 3a33a36..85cf14a 100644 --- a/samples/Without/Without.csproj +++ b/samples/Without/Without.csproj @@ -1,13 +1,13 @@ - net6.0 + net9.0 enable - - - + + + diff --git a/samples/Without/Worker.cs b/samples/Without/Worker.cs index 470942e..ffe1f0e 100644 --- a/samples/Without/Worker.cs +++ b/samples/Without/Worker.cs @@ -24,7 +24,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Timeout!"); } - catch(Exception e) + catch (Exception e) { _logger.LogError(e.Message); } diff --git a/source/MinimalHttpLogger/MinimalHttpLogger.cs b/source/MinimalHttpLogger/MinimalHttpLogger.cs index b2cf07e..ef9e9f3 100644 --- a/source/MinimalHttpLogger/MinimalHttpLogger.cs +++ b/source/MinimalHttpLogger/MinimalHttpLogger.cs @@ -11,20 +11,15 @@ public static class ServiceCollectionExtensions { public static IServiceCollection UseMinimalHttpLogger(this IServiceCollection services) { - services.Replace(ServiceDescriptor.Singleton()); + services.Replace(ServiceDescriptor + .Singleton()); return services; } } -internal class ReplaceLoggingHttpMessageHandlerBuilderFilter : IHttpMessageHandlerBuilderFilter +internal class ReplaceLoggingHttpMessageHandlerBuilderFilter(ILoggerFactory loggerFactory) + : IHttpMessageHandlerBuilderFilter { - private readonly ILoggerFactory _loggerFactory; - - public ReplaceLoggingHttpMessageHandlerBuilderFilter(ILoggerFactory loggerFactory) - { - _loggerFactory = loggerFactory; - } - public Action Configure(Action next) { return builder => @@ -32,52 +27,56 @@ public Action Configure(Action (h is LoggingHttpMessageHandler) || h is LoggingScopeHttpMessageHandler).Select(h => h).ToList(); + var innerLogger = loggerFactory.CreateLogger($"System.Net.Http.HttpClient.{loggerName}.ClientHandler"); + var toRemove = builder.AdditionalHandlers + .Where(h => h is LoggingHttpMessageHandler or LoggingScopeHttpMessageHandler) + .ToList(); foreach (var delegatingHandler in toRemove) { builder.AdditionalHandlers.Remove(delegatingHandler); } + builder.AdditionalHandlers.Add(new RequestEndOnlyLogger(innerLogger)); }; } } -internal class RequestEndOnlyLogger : DelegatingHandler +internal class RequestEndOnlyLogger(ILogger logger) : DelegatingHandler { - private readonly ILogger _logger; - - public RequestEndOnlyLogger(ILogger logger) - { - _logger = logger; - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override async Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) { if (request == null) { throw new ArgumentNullException(nameof(request)); } - var requestUri = request.RequestUri?.ToString(); //SendAsync modifies req uri in case of redirects (?!), so making a local copy + + var + requestUri = + request.RequestUri + ?.ToString(); //SendAsync modifies req uri in case of redirects (?!), so making a local copy var stopwatch = ValueStopwatch.StartNew(); try { var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); - _logger.LogInformation("{Method} {Uri} - {StatusCode} {StatusCodeLiteral} in {Time}ms", request.Method, requestUri, $"{(int)response.StatusCode}", $"{response.StatusCode}", stopwatch.GetElapsedTime().TotalMilliseconds); + logger.LogInformation("{Method} {Uri} - {StatusCode} {StatusCodeLiteral} in {Time}ms", request.Method, + requestUri, $"{(int)response.StatusCode}", $"{response.StatusCode}", + stopwatch.GetElapsedTime().TotalMilliseconds); return response; } - catch(Exception) + catch (Exception) { - _logger.LogInformation("{Method} {Uri} failed to respond in {Time}ms", request.Method, requestUri, stopwatch.GetElapsedTime().TotalMilliseconds); + logger.LogInformation("{Method} {Uri} failed to respond in {Time}ms", request.Method, requestUri, + stopwatch.GetElapsedTime().TotalMilliseconds); throw; } } - internal struct ValueStopwatch + internal readonly struct ValueStopwatch { private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency; - private long _startTimestamp; + private readonly long _startTimestamp; public bool IsActive => _startTimestamp != 0; @@ -86,18 +85,22 @@ private ValueStopwatch(long startTimestamp) _startTimestamp = startTimestamp; } - public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp()); + public static ValueStopwatch StartNew() + { + return new ValueStopwatch(Stopwatch.GetTimestamp()); + } public TimeSpan GetElapsedTime() { if (!IsActive) { - throw new InvalidOperationException("An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time."); + throw new InvalidOperationException( + "An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time."); } - long end = Stopwatch.GetTimestamp(); - long timestampDelta = end - _startTimestamp; - long ticks = (long)(TimestampToTicks * timestampDelta); + var end = Stopwatch.GetTimestamp(); + var timestampDelta = end - _startTimestamp; + var ticks = (long)(TimestampToTicks * timestampDelta); return new TimeSpan(ticks); } } diff --git a/source/MinimalHttpLogger/MinimalHttpLogger.csproj b/source/MinimalHttpLogger/MinimalHttpLogger.csproj index eb0877a..4e67efd 100644 --- a/source/MinimalHttpLogger/MinimalHttpLogger.csproj +++ b/source/MinimalHttpLogger/MinimalHttpLogger.csproj @@ -1,7 +1,7 @@ - net7.0;net8.0 + net9.0 enable latest enable @@ -17,28 +17,21 @@ - 7.0.0 - 8.0.0 + 9.0.0 - - - - - - - - - - - - + + + + + + - - - + + +