Skip to content

Commit b9bd67c

Browse files
authored
Suppress gRPC resource service error in host console (#5534)
1 parent d6f411a commit b9bd67c

File tree

2 files changed

+39
-28
lines changed

2 files changed

+39
-28
lines changed

src/Aspire.Hosting/Dashboard/DashboardService.cs

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Grpc.Core;
77
using Microsoft.AspNetCore.Authorization;
88
using Microsoft.Extensions.Hosting;
9+
using Microsoft.Extensions.Logging;
910

1011
namespace Aspire.Hosting.Dashboard;
1112

@@ -17,7 +18,7 @@ namespace Aspire.Hosting.Dashboard;
1718
/// required beyond a single request. Longer-scoped data is stored in <see cref="DashboardServiceData"/>.
1819
/// </remarks>
1920
[Authorize(Policy = ResourceServiceApiKeyAuthorization.PolicyName)]
20-
internal sealed partial class DashboardService(DashboardServiceData serviceData, IHostEnvironment hostEnvironment, IHostApplicationLifetime hostApplicationLifetime)
21+
internal sealed partial class DashboardService(DashboardServiceData serviceData, IHostEnvironment hostEnvironment, IHostApplicationLifetime hostApplicationLifetime, ILogger<DashboardService> logger)
2122
: Aspire.ResourceService.Proto.V1.DashboardService.DashboardServiceBase
2223
{
2324
// Calls that consume or produce streams must create a linked cancellation token
@@ -53,18 +54,11 @@ public override async Task WatchResources(
5354
IServerStreamWriter<WatchResourcesUpdate> responseStream,
5455
ServerCallContext context)
5556
{
56-
using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, context.CancellationToken);
57+
await ExecuteAsync(
58+
WatchResourcesInternal,
59+
context).ConfigureAwait(false);
5760

58-
try
59-
{
60-
await WatchResourcesInternal().ConfigureAwait(false);
61-
}
62-
catch (Exception ex) when (ex is OperationCanceledException or IOException && cts.Token.IsCancellationRequested)
63-
{
64-
// Ignore cancellation and just return. Note that cancelled writes throw IOException.
65-
}
66-
67-
async Task WatchResourcesInternal()
61+
async Task WatchResourcesInternal(CancellationToken cancellationToken)
6862
{
6963
var (initialData, updates) = serviceData.SubscribeResources();
7064

@@ -75,9 +69,9 @@ async Task WatchResourcesInternal()
7569
data.Resources.Add(Resource.FromSnapshot(resource));
7670
}
7771

78-
await responseStream.WriteAsync(new() { InitialData = data }).ConfigureAwait(false);
72+
await responseStream.WriteAsync(new() { InitialData = data }, cancellationToken).ConfigureAwait(false);
7973

80-
await foreach (var batch in updates.WithCancellation(cts.Token).ConfigureAwait(false))
74+
await foreach (var batch in updates.WithCancellation(cancellationToken).ConfigureAwait(false))
8175
{
8276
WatchResourcesChanges changes = new();
8377

@@ -101,7 +95,7 @@ async Task WatchResourcesInternal()
10195
changes.Value.Add(change);
10296
}
10397

104-
await responseStream.WriteAsync(new() { Changes = changes }, cts.Token).ConfigureAwait(false);
98+
await responseStream.WriteAsync(new() { Changes = changes }, cancellationToken).ConfigureAwait(false);
10599
}
106100
}
107101
}
@@ -111,18 +105,11 @@ public override async Task WatchResourceConsoleLogs(
111105
IServerStreamWriter<WatchResourceConsoleLogsUpdate> responseStream,
112106
ServerCallContext context)
113107
{
114-
using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, context.CancellationToken);
108+
await ExecuteAsync(
109+
WatchResourceConsoleLogsInternal,
110+
context).ConfigureAwait(false);
115111

116-
try
117-
{
118-
await WatchResourceConsoleLogsInternal().ConfigureAwait(false);
119-
}
120-
catch (Exception ex) when (ex is OperationCanceledException or IOException && cts.Token.IsCancellationRequested)
121-
{
122-
// Ignore cancellation and just return. Note that cancelled writes throw IOException.
123-
}
124-
125-
async Task WatchResourceConsoleLogsInternal()
112+
async Task WatchResourceConsoleLogsInternal(CancellationToken cancellationToken)
126113
{
127114
var subscription = serviceData.SubscribeConsoleLogs(request.ResourceName);
128115

@@ -131,7 +118,7 @@ async Task WatchResourceConsoleLogsInternal()
131118
return;
132119
}
133120

134-
await foreach (var group in subscription.WithCancellation(cts.Token).ConfigureAwait(false))
121+
await foreach (var group in subscription.WithCancellation(cancellationToken).ConfigureAwait(false))
135122
{
136123
var update = new WatchResourceConsoleLogsUpdate();
137124

@@ -140,8 +127,31 @@ async Task WatchResourceConsoleLogsInternal()
140127
update.LogLines.Add(new ConsoleLogLine() { LineNumber = lineNumber, Text = content, IsStdErr = isErrorMessage });
141128
}
142129

143-
await responseStream.WriteAsync(update, cts.Token).ConfigureAwait(false);
130+
await responseStream.WriteAsync(update, cancellationToken).ConfigureAwait(false);
144131
}
145132
}
146133
}
134+
135+
private async Task ExecuteAsync(Func<CancellationToken, Task> execute, ServerCallContext serverCallContext)
136+
{
137+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(hostApplicationLifetime.ApplicationStopping, serverCallContext.CancellationToken);
138+
139+
try
140+
{
141+
await execute(cts.Token).ConfigureAwait(false);
142+
}
143+
catch (OperationCanceledException) when (cts.Token.IsCancellationRequested)
144+
{
145+
// Ignore cancellation and just return.
146+
}
147+
catch (IOException) when (cts.Token.IsCancellationRequested)
148+
{
149+
// Ignore cancellation and just return. Cancelled writes throw IOException.
150+
}
151+
catch (Exception ex)
152+
{
153+
logger.LogError(ex, $"Error executing service method '{serverCallContext.Method}'.");
154+
throw;
155+
}
156+
}
147157
}

src/Aspire.Hosting/DistributedApplicationBuilder.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options)
127127
_innerBuilder.Logging.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.Warning);
128128
_innerBuilder.Logging.AddFilter("Microsoft.AspNetCore.Server.Kestrel", LogLevel.Error);
129129
_innerBuilder.Logging.AddFilter("Aspire.Hosting.Dashboard", LogLevel.Error);
130+
_innerBuilder.Logging.AddFilter("Grpc.AspNetCore.Server.ServerCallHandler", LogLevel.Error);
130131

131132
// This is so that we can see certificate errors in the resource server in the console logs.
132133
// See: https://github.com/dotnet/aspire/issues/2914

0 commit comments

Comments
 (0)