From 0fe3dc68de1ac091a88a6a23472c9623be612ed8 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 16 Aug 2024 21:45:51 +0000 Subject: [PATCH 1/2] [tests] Add resource logging to EndToEnd tests --- tests/Aspire.EndToEnd.Tests/Aspire.EndToEnd.Tests.csproj | 2 ++ tests/testproject/TestProject.AppHost/TestProgram.cs | 2 ++ .../TestProject.AppHost/TestProject.AppHost.csproj | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/tests/Aspire.EndToEnd.Tests/Aspire.EndToEnd.Tests.csproj b/tests/Aspire.EndToEnd.Tests/Aspire.EndToEnd.Tests.csproj index 01aa87ce9a..f8a914bf16 100644 --- a/tests/Aspire.EndToEnd.Tests/Aspire.EndToEnd.Tests.csproj +++ b/tests/Aspire.EndToEnd.Tests/Aspire.EndToEnd.Tests.csproj @@ -33,6 +33,8 @@ + + diff --git a/tests/testproject/TestProject.AppHost/TestProgram.cs b/tests/testproject/TestProject.AppHost/TestProgram.cs index e12c5071c7..d9fe03fbc8 100644 --- a/tests/testproject/TestProject.AppHost/TestProgram.cs +++ b/tests/testproject/TestProject.AppHost/TestProgram.cs @@ -5,6 +5,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using Aspire.Hosting.Lifecycle; +using Aspire.Hosting.Testing; using Aspire.TestProject; using Microsoft.Extensions.DependencyInjection; @@ -101,6 +102,7 @@ private TestProgram( } } + AppBuilder.Services.AddHostedService(); AppBuilder.Services.AddLifecycleHook(); AppBuilder.Services.AddHttpClient(); } diff --git a/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj b/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj index b55b013379..f1d7563b41 100644 --- a/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj +++ b/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj @@ -23,6 +23,10 @@ + + + From fbed5044703a1c34df54cfab48da934dc2a3d90f Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 16 Aug 2024 19:40:46 -0400 Subject: [PATCH 2/2] ResourceLoggerForwarderService: don't propogate TaskCanceledException when the token was canceled It caused failures like: ``` Failed Aspire.Hosting.Tests.ManifestGenerationTests.PublishingRedisResourceAsContainerResultsInConnectionStringProperty [70 ms] Error Message: System.AggregateException : One or more errors occurred. (A task was canceled.) ---- System.Threading.Tasks.TaskCanceledException : A task was canceled. Stack Trace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Aspire.Hosting.DistributedApplication.Run() in /_/src/Aspire.Hosting/DistributedApplication.cs:line 339 at TestProgram.Run() in /_/tests/testproject/TestProject.AppHost/TestProgram.cs:line 150 at Aspire.Hosting.Tests.ManifestGenerationTests.PublishingRedisResourceAsContainerResultsInConnectionStringProperty() in /_/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs:line 258 at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) ----- Inner Stack Trace ----- at System.Threading.Tasks.Task.GetExceptions(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Aspire.Hosting.DistributedApplication.Run() in /_/src/Aspire.Hosting/DistributedApplication.cs:line 339 at TestProgram.Run() in /_/tests/testproject/TestProject.AppHost/TestProgram.cs:line 150 at Aspire.Hosting.Tests.ManifestGenerationTests.PublishingRedisResourceAsContainerResultsInConnectionStringProperty() in /_/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs:line 258 at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at System.Threading.Tasks.Task`1.InnerInvoke() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) at System.Threading.Tasks.Task.ExecuteEntry() at System.Threading.Tasks.SynchronizationContextTaskScheduler.<>c.<.cctor>b__8_0(Object s) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) --- End of stack trace from previous location --- ``` --- .../ResourceLoggerForwarderService.cs | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/Aspire.Hosting.Testing/ResourceLoggerForwarderService.cs b/src/Aspire.Hosting.Testing/ResourceLoggerForwarderService.cs index 5a47b55fbe..65eb24b3ff 100644 --- a/src/Aspire.Hosting.Testing/ResourceLoggerForwarderService.cs +++ b/src/Aspire.Hosting.Testing/ResourceLoggerForwarderService.cs @@ -34,40 +34,54 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) private async Task WatchNotifications(CancellationToken cancellationToken) { - var loggingResourceIds = new HashSet(); - var logWatchTasks = new List(); - - await foreach (var resourceEvent in resourceNotificationService.WatchAsync(cancellationToken).ConfigureAwait(false)) + try { - var resourceId = resourceEvent.ResourceId; + var loggingResourceIds = new HashSet(); + var logWatchTasks = new List(); - if (loggingResourceIds.Add(resourceId)) + await foreach (var resourceEvent in resourceNotificationService.WatchAsync(cancellationToken).ConfigureAwait(false)) { - // Start watching the logs for this resource ID - logWatchTasks.Add(WatchResourceLogs(resourceEvent.Resource, resourceId, cancellationToken)); + var resourceId = resourceEvent.ResourceId; + + if (loggingResourceIds.Add(resourceId)) + { + // Start watching the logs for this resource ID + logWatchTasks.Add(WatchResourceLogs(resourceEvent.Resource, resourceId, cancellationToken)); + } } - } - await Task.WhenAll(logWatchTasks).ConfigureAwait(false); + await Task.WhenAll(logWatchTasks).ConfigureAwait(false); + } + catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested) + { + // this was expected as the token was canceled + } } private async Task WatchResourceLogs(IResource resource, string resourceId, CancellationToken cancellationToken) { - var applicationName = hostEnvironment.ApplicationName; - var logger = loggerFactory.CreateLogger($"{applicationName}.Resources.{resource.Name}"); - await foreach (var logEvent in resourceLoggerService.WatchAsync(resourceId).WithCancellation(cancellationToken).ConfigureAwait(false)) + try { - foreach (var line in logEvent) + var applicationName = hostEnvironment.ApplicationName; + var logger = loggerFactory.CreateLogger($"{applicationName}.Resources.{resource.Name}"); + await foreach (var logEvent in resourceLoggerService.WatchAsync(resourceId).WithCancellation(cancellationToken).ConfigureAwait(false)) { - var logLevel = line.IsErrorMessage ? LogLevel.Error : LogLevel.Information; - - if (logger.IsEnabled(logLevel)) + foreach (var line in logEvent) { - // Log message format here approximates the format shown in the dashboard - logger.Log(logLevel, "{LineNumber}: {LineContent}", line.LineNumber, line.Content); - OnResourceLog?.Invoke(resourceId); + var logLevel = line.IsErrorMessage ? LogLevel.Error : LogLevel.Information; + + if (logger.IsEnabled(logLevel)) + { + // Log message format here approximates the format shown in the dashboard + logger.Log(logLevel, "{LineNumber}: {LineContent}", line.LineNumber, line.Content); + OnResourceLog?.Invoke(resourceId); + } } } } + catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested) + { + // this was expected as the token was canceled + } } }