Skip to content

Commit

Permalink
Add error logging to DashboardViewModelService
Browse files Browse the repository at this point in the history
Resolves #602
  • Loading branch information
smitpatel committed Nov 1, 2023
1 parent 0c04133 commit 53a1835
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 44 deletions.
5 changes: 3 additions & 2 deletions src/Aspire.Hosting/Dashboard/ContainerViewModelCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Dcp;
using Aspire.Hosting.Dcp.Model;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Dashboard;

internal sealed class ContainerViewModelCache : ViewModelCache<Container, ContainerViewModel>
{
public ContainerViewModelCache(
KubernetesService kubernetesService, DistributedApplicationModel applicationModel, CancellationToken cancellationToken)
: base(kubernetesService, applicationModel, cancellationToken)
KubernetesService kubernetesService, DistributedApplicationModel applicationModel, ILoggerFactory loggerFactory, CancellationToken cancellationToken)
: base(kubernetesService, applicationModel, loggerFactory.CreateLogger<ContainerViewModelCache>(), cancellationToken)
{
}

Expand Down
7 changes: 6 additions & 1 deletion src/Aspire.Hosting/Dashboard/DashboardViewModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Aspire.Hosting.Dcp;
using Aspire.Hosting.Dcp.Model;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NamespacedName = Aspire.Dashboard.Model.NamespacedName;

namespace Aspire.Hosting.Dashboard;
Expand All @@ -23,21 +24,25 @@ internal sealed partial class DashboardViewModelService : IDashboardViewModelSer
private readonly ViewModelCache<Executable, ExecutableViewModel> _executableViewModelCache;
private readonly ViewModelCache<Executable, ProjectViewModel> _projectViewModelCache;

public DashboardViewModelService(DistributedApplicationModel applicationModel, KubernetesService kubernetesService, IHostEnvironment hostEnvironment)
public DashboardViewModelService(
DistributedApplicationModel applicationModel, KubernetesService kubernetesService, IHostEnvironment hostEnvironment, ILoggerFactory loggerFactory)
{
_applicationModel = applicationModel;
_applicationName = ComputeApplicationName(hostEnvironment.ApplicationName);
_containerViewModelCache = new ContainerViewModelCache(
kubernetesService,
_applicationModel,
loggerFactory,
_cancellationTokenSource.Token);
_executableViewModelCache = new ExecutableViewModelCache(
kubernetesService,
_applicationModel,
loggerFactory,
_cancellationTokenSource.Token);
_projectViewModelCache = new ProjectViewModelCache(
kubernetesService,
_applicationModel,
loggerFactory,
_cancellationTokenSource.Token);
}

Expand Down
5 changes: 3 additions & 2 deletions src/Aspire.Hosting/Dashboard/ExecutableViewModelCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Dcp;
using Aspire.Hosting.Dcp.Model;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Dashboard;

internal sealed class ExecutableViewModelCache : ViewModelCache<Executable, ExecutableViewModel>
{
public ExecutableViewModelCache(
KubernetesService kubernetesService, DistributedApplicationModel applicationModel, CancellationToken cancellationToken)
: base(kubernetesService, applicationModel, cancellationToken)
KubernetesService kubernetesService, DistributedApplicationModel applicationModel, ILoggerFactory loggerFactory, CancellationToken cancellationToken)
: base(kubernetesService, applicationModel, loggerFactory.CreateLogger<ExecutableViewModelCache>(), cancellationToken)
{
}

Expand Down
5 changes: 3 additions & 2 deletions src/Aspire.Hosting/Dashboard/ProjectViewModelCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Dcp;
using Aspire.Hosting.Dcp.Model;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Dashboard;

internal sealed class ProjectViewModelCache : ViewModelCache<Executable, ProjectViewModel>
{
public ProjectViewModelCache(
KubernetesService kubernetesService, DistributedApplicationModel applicationModel, CancellationToken cancellationToken)
: base(kubernetesService, applicationModel, cancellationToken)
KubernetesService kubernetesService, DistributedApplicationModel applicationModel, ILoggerFactory loggerFactory, CancellationToken cancellationToken)
: base(kubernetesService, applicationModel, loggerFactory.CreateLogger<ProjectViewModelCache>(), cancellationToken)
{
}

Expand Down
102 changes: 65 additions & 37 deletions src/Aspire.Hosting/Dashboard/ViewModelCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Aspire.Hosting.Utils;
using k8s;
using NamespacedName = Aspire.Dashboard.Model.NamespacedName;
using Microsoft.Extensions.Logging;

namespace Aspire.Hosting.Dashboard;

Expand All @@ -34,6 +35,7 @@ internal abstract class ViewModelCache<TResource, TViewModel>
protected ViewModelCache(
KubernetesService kubernetesService,
DistributedApplicationModel applicationModel,
ILogger logger,
CancellationToken cancellationToken)
{
_kubernetesService = kubernetesService;
Expand All @@ -42,58 +44,73 @@ protected ViewModelCache(

Task.Run(async () =>
{
// Start an enumerator which combines underlying kubernetes watches
// And return stream of changes in view model in publishing channel
var enumerator = new ViewModelGeneratingEnumerator(
_kubernetesService,
_applicationModel,
FilterResource,
ConvertToViewModel,
cancellationToken);
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
try
{
var (objectChangeType, resource) = enumerator.Current;
switch (objectChangeType)
// Start an enumerator which combines underlying kubernetes watches
// And return stream of changes in view model in publishing channel
var enumerator = new ViewModelGeneratingEnumerator(
_kubernetesService,
_applicationModel,
logger,
FilterResource,
ConvertToViewModel,
cancellationToken);
while (await enumerator.MoveNextAsync().ConfigureAwait(false))
{
case ObjectChangeType.Added:
_resourcesMap.Add(resource.Name, resource);
break;
var (objectChangeType, resource) = enumerator.Current;
switch (objectChangeType)
{
case ObjectChangeType.Added:
_resourcesMap.Add(resource.Name, resource);
break;
case ObjectChangeType.Modified:
_resourcesMap[resource.Name] = resource;
break;
case ObjectChangeType.Modified:
_resourcesMap[resource.Name] = resource;
break;
case ObjectChangeType.Deleted:
_resourcesMap.Remove(resource.Name);
break;
}
case ObjectChangeType.Deleted:
_resourcesMap.Remove(resource.Name);
break;
}
await _publishingChannel.Writer.WriteAsync(enumerator.Current, cancellationToken).ConfigureAwait(false);
await _publishingChannel.Writer.WriteAsync(enumerator.Current, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception ex)
{
logger.LogError(ex, "Task to write view model change terminated for resource type: {resourceType}", typeof(TViewModel).Name);
}
}, cancellationToken);

Task.Run(async () =>
{
// Receive data from publishing channel
// Update snapshot and send data to other subscribers
await foreach (var change in _publishingChannel.Reader.ReadAllAsync(cancellationToken))
try
{
Channel<ResourceChanged<TViewModel>>?[] listeningChannels = [];
lock (_syncLock)
// Receive data from publishing channel
// Update snapshot and send data to other subscribers
await foreach (var change in _publishingChannel.Reader.ReadAllAsync(cancellationToken))
{
_resourceChanges.Add(change);
listeningChannels = _subscribedChannels.ToArray();
}
Channel<ResourceChanged<TViewModel>>?[] listeningChannels = [];
lock (_syncLock)
{
_resourceChanges.Add(change);
listeningChannels = _subscribedChannels.ToArray();
}
foreach (var channel in listeningChannels)
{
if (channel is not null)
foreach (var channel in listeningChannels)
{
await channel.Writer.WriteAsync(change, cancellationToken).ConfigureAwait(false);
if (channel is not null)
{
await channel.Writer.WriteAsync(change, cancellationToken).ConfigureAwait(false);
}
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "Task to publish view model changes to subscribers terminated for resource type: {resourceType}", typeof(TViewModel).Name);
}
}
, cancellationToken);
}
Expand Down Expand Up @@ -243,6 +260,7 @@ private sealed class ViewModelGeneratingEnumerator : IAsyncEnumerator<ResourceCh
private readonly ConcurrentDictionary<string, List<EnvVar>> _additionalEnvVarsMap = [];

private readonly DistributedApplicationModel _applicationModel;
private readonly ILogger _logger;
private readonly Func<TResource, bool> _filterResource;
private readonly Func<DistributedApplicationModel, IEnumerable<Service>, IEnumerable<Endpoint>, TResource, List<EnvVar>?, TViewModel> _convertToViewModel;
private readonly CancellationToken _cancellationToken;
Expand All @@ -253,11 +271,13 @@ private sealed class ViewModelGeneratingEnumerator : IAsyncEnumerator<ResourceCh
public ViewModelGeneratingEnumerator(
KubernetesService kubernetesService,
DistributedApplicationModel applicationModel,
ILogger logger,
Func<TResource, bool> _filterResource,
Func<DistributedApplicationModel, IEnumerable<Service>, IEnumerable<Endpoint>, TResource, List<EnvVar>?, TViewModel> convertToViewModel,
CancellationToken cancellationToken)
{
_applicationModel = applicationModel;
_logger = logger;
this._filterResource = _filterResource;
_convertToViewModel = convertToViewModel;
_cancellationToken = cancellationToken;
Expand Down Expand Up @@ -474,9 +494,17 @@ private void RunWatchTask<T>(KubernetesService kubernetesService, CancellationTo
{
_ = Task.Run(async () =>
{
await foreach (var tuple in kubernetesService.WatchAsync<T>(cancellationToken: cancellationToken))
try
{
await foreach (var tuple in kubernetesService.WatchAsync<T>(cancellationToken: cancellationToken))
{
await _channel.Writer.WriteAsync((tuple.Item1, tuple.Item2.Metadata.Name, tuple.Item2), _cancellationToken).ConfigureAwait(false);
}
}
catch (Exception ex)
{
await _channel.Writer.WriteAsync((tuple.Item1, tuple.Item2.Metadata.Name, tuple.Item2), _cancellationToken).ConfigureAwait(false);
_logger.LogError(ex, "Task to watch kubernetes changes terminated for resource: {resource} for view model: {viewModel}",
typeof(T).Name, typeof(TViewModel).Name);
}
}, cancellationToken);
}
Expand Down

0 comments on commit 53a1835

Please sign in to comment.