From 1ffbb4ba14f38271e23c61dffe3963e8d7b6534f Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 4 Sep 2024 10:30:11 +0800 Subject: [PATCH 1/2] Suppress gRPC resource service error in host console --- .../Dashboard/DashboardService.cs | 66 +++++++++++-------- .../DistributedApplicationBuilder.cs | 1 + 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/Aspire.Hosting/Dashboard/DashboardService.cs b/src/Aspire.Hosting/Dashboard/DashboardService.cs index 393e4f1077e..7ce67013d96 100644 --- a/src/Aspire.Hosting/Dashboard/DashboardService.cs +++ b/src/Aspire.Hosting/Dashboard/DashboardService.cs @@ -6,6 +6,7 @@ using Grpc.Core; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; namespace Aspire.Hosting.Dashboard; @@ -17,7 +18,7 @@ namespace Aspire.Hosting.Dashboard; /// required beyond a single request. Longer-scoped data is stored in . /// [Authorize(Policy = ResourceServiceApiKeyAuthorization.PolicyName)] -internal sealed partial class DashboardService(DashboardServiceData serviceData, IHostEnvironment hostEnvironment, IHostApplicationLifetime hostApplicationLifetime) +internal sealed partial class DashboardService(DashboardServiceData serviceData, IHostEnvironment hostEnvironment, IHostApplicationLifetime hostApplicationLifetime, ILogger logger) : Aspire.ResourceService.Proto.V1.DashboardService.DashboardServiceBase { // Calls that consume or produce streams must create a linked cancellation token @@ -53,18 +54,11 @@ public override async Task WatchResources( IServerStreamWriter responseStream, ServerCallContext context) { - using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, context.CancellationToken); + await ExecuteAsync( + WatchResourcesInternal, + context.CancellationToken).ConfigureAwait(false); - try - { - await WatchResourcesInternal().ConfigureAwait(false); - } - catch (Exception ex) when (ex is OperationCanceledException or IOException && cts.Token.IsCancellationRequested) - { - // Ignore cancellation and just return. Note that cancelled writes throw IOException. - } - - async Task WatchResourcesInternal() + async Task WatchResourcesInternal(CancellationToken cancellationToken) { var (initialData, updates) = serviceData.SubscribeResources(); @@ -75,9 +69,9 @@ async Task WatchResourcesInternal() data.Resources.Add(Resource.FromSnapshot(resource)); } - await responseStream.WriteAsync(new() { InitialData = data }).ConfigureAwait(false); + await responseStream.WriteAsync(new() { InitialData = data }, cancellationToken).ConfigureAwait(false); - await foreach (var batch in updates.WithCancellation(cts.Token).ConfigureAwait(false)) + await foreach (var batch in updates.WithCancellation(cancellationToken).ConfigureAwait(false)) { WatchResourcesChanges changes = new(); @@ -101,7 +95,7 @@ async Task WatchResourcesInternal() changes.Value.Add(change); } - await responseStream.WriteAsync(new() { Changes = changes }, cts.Token).ConfigureAwait(false); + await responseStream.WriteAsync(new() { Changes = changes }, cancellationToken).ConfigureAwait(false); } } } @@ -111,18 +105,11 @@ public override async Task WatchResourceConsoleLogs( IServerStreamWriter responseStream, ServerCallContext context) { - using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, context.CancellationToken); + await ExecuteAsync( + WatchResourceConsoleLogsInternal, + context.CancellationToken).ConfigureAwait(false); - try - { - await WatchResourceConsoleLogsInternal().ConfigureAwait(false); - } - catch (Exception ex) when (ex is OperationCanceledException or IOException && cts.Token.IsCancellationRequested) - { - // Ignore cancellation and just return. Note that cancelled writes throw IOException. - } - - async Task WatchResourceConsoleLogsInternal() + async Task WatchResourceConsoleLogsInternal(CancellationToken cancellationToken) { var subscription = serviceData.SubscribeConsoleLogs(request.ResourceName); @@ -131,7 +118,7 @@ async Task WatchResourceConsoleLogsInternal() return; } - await foreach (var group in subscription.WithCancellation(cts.Token).ConfigureAwait(false)) + await foreach (var group in subscription.WithCancellation(cancellationToken).ConfigureAwait(false)) { var update = new WatchResourceConsoleLogsUpdate(); @@ -140,8 +127,31 @@ async Task WatchResourceConsoleLogsInternal() update.LogLines.Add(new ConsoleLogLine() { LineNumber = lineNumber, Text = content, IsStdErr = isErrorMessage }); } - await responseStream.WriteAsync(update, cts.Token).ConfigureAwait(false); + await responseStream.WriteAsync(update, cancellationToken).ConfigureAwait(false); } } } + + private async Task ExecuteAsync(Func execute, CancellationToken cancellationToken) + { + using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, cancellationToken); + + try + { + await execute(cts.Token).ConfigureAwait(false); + } + catch (OperationCanceledException) when (cts.Token.IsCancellationRequested) + { + // Ignore cancellation and just return. + } + catch (IOException) when (cts.Token.IsCancellationRequested) + { + // Ignore cancellation and just return. Cancelled writes throw IOException. + } + catch (Exception ex) + { + logger.LogDebug(ex, "Error executing service method."); + throw; + } + } } diff --git a/src/Aspire.Hosting/DistributedApplicationBuilder.cs b/src/Aspire.Hosting/DistributedApplicationBuilder.cs index 73b9a68c624..5c8b03299de 100644 --- a/src/Aspire.Hosting/DistributedApplicationBuilder.cs +++ b/src/Aspire.Hosting/DistributedApplicationBuilder.cs @@ -127,6 +127,7 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options) _innerBuilder.Logging.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.Warning); _innerBuilder.Logging.AddFilter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Error); _innerBuilder.Logging.AddFilter("Aspire.Hosting.Dashboard", LogLevel.Error); + _innerBuilder.Logging.AddFilter("Grpc.AspNetCore.Server.ServerCallHandler", LogLevel.Error); // This is so that we can see certificate errors in the resource server in the console logs. // See: https://github.com/dotnet/aspire/issues/2914 From 3f473402b76fae037657a9c9d04e439220007eb8 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 4 Sep 2024 10:40:59 +0800 Subject: [PATCH 2/2] Update --- src/Aspire.Hosting/Dashboard/DashboardService.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Aspire.Hosting/Dashboard/DashboardService.cs b/src/Aspire.Hosting/Dashboard/DashboardService.cs index 7ce67013d96..b57d519b1c9 100644 --- a/src/Aspire.Hosting/Dashboard/DashboardService.cs +++ b/src/Aspire.Hosting/Dashboard/DashboardService.cs @@ -56,7 +56,7 @@ public override async Task WatchResources( { await ExecuteAsync( WatchResourcesInternal, - context.CancellationToken).ConfigureAwait(false); + context).ConfigureAwait(false); async Task WatchResourcesInternal(CancellationToken cancellationToken) { @@ -107,7 +107,7 @@ public override async Task WatchResourceConsoleLogs( { await ExecuteAsync( WatchResourceConsoleLogsInternal, - context.CancellationToken).ConfigureAwait(false); + context).ConfigureAwait(false); async Task WatchResourceConsoleLogsInternal(CancellationToken cancellationToken) { @@ -132,9 +132,9 @@ async Task WatchResourceConsoleLogsInternal(CancellationToken cancellationToken) } } - private async Task ExecuteAsync(Func execute, CancellationToken cancellationToken) + private async Task ExecuteAsync(Func execute, ServerCallContext serverCallContext) { - using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, cancellationToken); + using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, serverCallContext.CancellationToken); try { @@ -150,7 +150,7 @@ private async Task ExecuteAsync(Func execute, Cancellat } catch (Exception ex) { - logger.LogDebug(ex, "Error executing service method."); + logger.LogError(ex, $"Error executing service method '{serverCallContext.Method}'."); throw; } }