Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
6c22161
Model per-type resource data as extension data
drewnoakes Dec 6, 2023
d76c649
Split snapshots from view models
drewnoakes Dec 15, 2023
2e95d13
Remove obsolete TODOs
drewnoakes Dec 15, 2023
57d6ca5
Fix tests
drewnoakes Dec 16, 2023
8cfbb32
Merge branch 'main' into split-dto
drewnoakes Dec 16, 2023
23a3c03
WIP
drewnoakes Dec 17, 2023
783bd47
Rename ResourceDataKeys to KnownProperties
drewnoakes Dec 17, 2023
5020ded
WIP
drewnoakes Dec 17, 2023
0930841
Reorder members
drewnoakes Dec 17, 2023
f340299
Move types to their own source files
drewnoakes Dec 17, 2023
f5de0e7
Split endpoint snapshot from view model
drewnoakes Dec 17, 2023
6cd0d8d
Consistent naming in razor files
drewnoakes Dec 17, 2023
d3c1441
Add ctor to EnvironmentVariableViewModel
drewnoakes Dec 17, 2023
88a5cda
Complete ResourceViewModel construction from Resource message
drewnoakes Dec 17, 2023
f1b1e51
Make types internal
drewnoakes Dec 18, 2023
3b6ec12
Add words to dictionary
drewnoakes Dec 20, 2023
fab31f3
Use KnownResourceTypes
drewnoakes Dec 20, 2023
62e346e
Pass correct type to logger factory
drewnoakes Dec 20, 2023
6c749c1
Rename ResourceService to DashboardClient
drewnoakes Dec 20, 2023
1637d27
Refactor
drewnoakes Dec 20, 2023
a5bcaf5
Dashboard receives data via gRPC
drewnoakes Dec 20, 2023
58f3018
Merge branch 'main' into add-grpc
drewnoakes Dec 20, 2023
c7f88cc
Fix ports display in resource grid
drewnoakes Dec 20, 2023
5734de1
Use extension methods to check resource type
drewnoakes Dec 20, 2023
a7e298c
Remove obsolete TODO
drewnoakes Dec 20, 2023
56c2dd2
Document use of Math.Round
drewnoakes Dec 20, 2023
5d97fa3
Consolidate types
drewnoakes Dec 21, 2023
96668bc
Rename comparer
drewnoakes Dec 21, 2023
9424de2
Remove ResourceId
drewnoakes Dec 21, 2023
e7c02f0
Add API doc
drewnoakes Dec 21, 2023
4b45b37
Logging
drewnoakes Dec 21, 2023
041ac74
Don't treat cancellations as errors
drewnoakes Dec 21, 2023
8c9a16d
Join async connection management on dispose
drewnoakes Dec 21, 2023
bd7e5da
Throw on unexpected values
drewnoakes Dec 21, 2023
2d6ae0f
Log when dashboard is disabled
drewnoakes Dec 21, 2023
9f33076
Remove redundant route on gRPC endpoint
drewnoakes Dec 21, 2023
23598b6
Log reason when dashboard hosting is skipped
drewnoakes Dec 21, 2023
57281fb
Merge branch 'main' into add-grpc
drewnoakes Dec 22, 2023
85fb426
Fix build
drewnoakes Dec 22, 2023
a3c556e
Fix errors following merge
drewnoakes Dec 22, 2023
7e66671
Formatting
drewnoakes Dec 22, 2023
1a6cacc
Handle null container ID
drewnoakes Dec 22, 2023
42e0301
Extract common check
drewnoakes Dec 22, 2023
37e5b43
Specify assembly name for eShopLite's app host
drewnoakes Dec 22, 2023
f21bdcb
Ensure the page title displays
drewnoakes Dec 22, 2023
aee49d3
Remove application version from protocol
drewnoakes Dec 22, 2023
854f2f9
Improve parsing of application name
drewnoakes Dec 22, 2023
8654657
Fix potential ANE due to unannotated gRPC code
drewnoakes Dec 22, 2023
7dedb62
Configure logging for dashboard web application
drewnoakes Dec 22, 2023
70161b1
gRPC channel and client will reconnect themselves
drewnoakes Jan 4, 2024
78c3fff
Review feedback
drewnoakes Jan 4, 2024
a3bf4c7
Use concurrent dictionaries
drewnoakes Jan 4, 2024
b9f8362
More spelling words
drewnoakes Jan 5, 2024
aeb5755
Refactor connection code
drewnoakes Jan 5, 2024
fe87351
Fix missing process ID
drewnoakes Jan 5, 2024
f9a9fe9
Fix resource type checks
drewnoakes Jan 5, 2024
418693a
Handle connection exception
drewnoakes Jan 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions samples/eShopLite/AppHost/AppHost.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<UserSecretsId>1bf0740a-0dfc-45aa-9002-def9b2b17da0</UserSecretsId>
<ImplicitUsings>enable</ImplicitUsings>
<IsAspireHost>true</IsAspireHost>
<AssemblyName>eShopLite.AppHost</AssemblyName>
</PropertyGroup>

<ItemGroup>
Expand Down
9 changes: 9 additions & 0 deletions spelling.dic
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
ansi
apigateway
apiserver
blazor
brightblack
brightblue
brightcyan
Expand All @@ -8,16 +11,22 @@ brightred
brightwhite
brightyellow
dbug
dcpctrl
dylib
equatable
grpc
kubeconfig
Kubelet
kubernetes
libc
noreferrer
noopener
oninput
otlp
protoc
redis
runtimeconfig
trce
upsert
uris
urls
1 change: 1 addition & 0 deletions src/Aspire.Dashboard/Aspire.Dashboard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
<Protobuf Include="..\Aspire.Hosting\Dashboard\proto\resource_service.proto" GrpcServices="Client" Access="Internal">
<Link>Protos\resource_service.proto</Link>
</Protobuf>
<Compile Include="..\Aspire.Hosting\Extensions\ChannelExtensions.cs" Link="Extensions\ChannelExtensions.cs" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/Aspire.Dashboard/Components/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<FluentHeader>
<div class="header-title">
<FluentAnchor IconStart="@(new AspireIcons.Size32.Logo())" Appearance="Appearance.Stealth" Href="/" Class="logo">
@string.Format(Loc[nameof(Layout.MainLayoutDashboardName)], ResourceService.ApplicationName)
@DashboardClient.FormatApplicationName(Loc[nameof(Layout.MainLayoutDashboardName)], () => InvokeAsync(StateHasChanged))
</FluentAnchor>
</div>
<div class="header-right">
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public partial class MainLayout : IDisposable
public required IStringLocalizer<Resources.Layout> Loc { get; set; }

[Inject]
public required IResourceService ResourceService { get; set; }
public required IDashboardClient DashboardClient { get; set; }

[Inject]
public required IDialogService DialogService { get; set; }
Expand Down
3 changes: 2 additions & 1 deletion src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
@page "/ConsoleLogs/{resourceName?}"
@namespace Aspire.Dashboard.Components.Pages
@using Aspire.Dashboard.Model
@inject IStringLocalizer<Dashboard.Resources.ConsoleLogs> Loc

<PageTitle>@string.Format(Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsPageTitle)], ResourceService.ApplicationName)</PageTitle>
<PageTitle>@DashboardClient.FormatApplicationName(Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsPageTitle)], () => InvokeAsync(StateHasChanged))</PageTitle>

<div class="resource-logs-layout">
<h1 class="page-header">@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsHeader)]</h1>
Expand Down
16 changes: 8 additions & 8 deletions src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Aspire.Dashboard.Components.Pages;
public sealed partial class ConsoleLogs : ComponentBase, IAsyncDisposable
{
[Inject]
public required IResourceService ResourceService { get; init; }
public required IDashboardClient DashboardClient { get; init; }
[Inject]
public required IJSRuntime JS { get; init; }
[Inject]
Expand Down Expand Up @@ -50,7 +50,7 @@ protected override void OnInitialized()

void TrackResources()
{
var (snapshot, subscription) = ResourceService.SubscribeResources();
var (snapshot, subscription) = DashboardClient.SubscribeResources();

foreach (var resource in snapshot)
{
Expand Down Expand Up @@ -150,13 +150,13 @@ private async ValueTask LoadLogsAsync()
{
var cancellationToken = await _logSubscriptionCancellationSeries.NextAsync();

var subscription = ResourceService.SubscribeConsoleLogs(_selectedResource.Name, cancellationToken);
var subscription = DashboardClient.SubscribeConsoleLogs(_selectedResource.Name, cancellationToken);

if (subscription is not null)
{
var task = _logViewer.SetLogSourceAsync(
subscription,
convertTimestampsFromUtc: _selectedResource is ContainerViewModel);
convertTimestampsFromUtc: _selectedResource.IsContainer());

_initialisedSuccessfully = true;
_status = Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsWatchingLogs)];
Expand All @@ -171,7 +171,7 @@ private async ValueTask LoadLogsAsync()
else
{
_initialisedSuccessfully = false;
_status = Loc[_selectedResource is ContainerViewModel
_status = Loc[_selectedResource.IsContainer()
? nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsFailedToInitialize)
: nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsLogsNotYetAvailable)];
}
Expand All @@ -185,9 +185,9 @@ private async Task HandleSelectedOptionChangedAsync()
NavigationManager.NavigateTo($"/ConsoleLogs/{_selectedOption?.Value}");
}

private async Task OnResourceChanged(ResourceChangeType changeType, ResourceViewModel resource)
private async Task OnResourceChanged(ResourceViewModelChangeType changeType, ResourceViewModel resource)
{
if (changeType == ResourceChangeType.Upsert)
if (changeType == ResourceViewModelChangeType.Upsert)
{
_resourceByName[resource.Name] = resource;

Expand All @@ -206,7 +206,7 @@ private async Task OnResourceChanged(ResourceChangeType changeType, ResourceView
}
}
}
else if (changeType == ResourceChangeType.Delete)
else if (changeType == ResourceViewModelChangeType.Delete)
{
var removed = _resourceByName.TryRemove(resource.Name, out _);
Debug.Assert(removed, "Cannot remove unknown resource.");
Expand Down
3 changes: 2 additions & 1 deletion src/Aspire.Dashboard/Components/Pages/Metrics.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
@page "/Metrics/{applicationInstanceId}/Meter/{meterName}"
@page "/Metrics/{applicationInstanceId}/Meter/{meterName}/Instrument/{instrumentName}"

@using Aspire.Dashboard.Model
@using Aspire.Dashboard.Model.Otlp
@using Aspire.Dashboard.Resources
@inject IStringLocalizer<Dashboard.Resources.Metrics> Loc
@inject IStringLocalizer<ControlsStrings> ControlsStringsLoc

<PageTitle>@string.Format(Loc[nameof(Dashboard.Resources.Metrics.MetricsPageTitle)], ResourceService.ApplicationName)</PageTitle>
<PageTitle>@DashboardClient.FormatApplicationName(Loc[nameof(Dashboard.Resources.Metrics.MetricsPageTitle)], () => InvokeAsync(StateHasChanged))</PageTitle>

<div class="metrics-layout">
<h1 class="page-header">@Loc[nameof(Dashboard.Resources.Metrics.MetricsHeader)]</h1>
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Dashboard/Components/Pages/Metrics.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public partial class Metrics : IDisposable
public required NavigationManager NavigationManager { get; set; }

[Inject]
public required IResourceService ResourceService { get; set; }
public required IDashboardClient DashboardClient { get; set; }

[Inject]
public required ProtectedSessionStorage ProtectedSessionStore { get; set; }
Expand Down
3 changes: 2 additions & 1 deletion src/Aspire.Dashboard/Components/Pages/Resources.razor
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
@page "/"
@using Aspire.Dashboard.Components.ResourcesGridColumns
@using Aspire.Dashboard.Model
@using Aspire.Dashboard.Resources
@inject IStringLocalizer<Dashboard.Resources.Resources> Loc
@inject IStringLocalizer<ControlsStrings> ControlsStringsLoc

<PageTitle>@string.Format(Loc[nameof(Dashboard.Resources.Resources.ResourcesPageTitle)], ResourceService.ApplicationName)</PageTitle>
<PageTitle>@DashboardClient.FormatApplicationName(Loc[nameof(Dashboard.Resources.Resources.ResourcesPageTitle)], () => InvokeAsync(StateHasChanged))</PageTitle>

<div class="content-layout-with-toolbar">
<FluentToolbar Orientation="Orientation.Horizontal">
Expand Down
61 changes: 14 additions & 47 deletions src/Aspire.Dashboard/Components/Pages/Resources.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public partial class Resources : ComponentBase, IDisposable
private Dictionary<OtlpApplication, int>? _applicationUnviewedErrorCounts;

[Inject]
public required IResourceService ResourceService { get; init; }
public required IDashboardClient DashboardClient { get; init; }
[Inject]
public required TelemetryRepository TelemetryRepository { get; init; }
[Inject]
Expand All @@ -31,7 +31,7 @@ public partial class Resources : ComponentBase, IDisposable
private readonly CancellationTokenSource _watchTaskCancellationTokenSource = new();
private readonly ConcurrentDictionary<string, ResourceViewModel> _resourceByName = new(StringComparers.ResourceName);
// TODO populate resource types from server data
private readonly ImmutableArray<string> _allResourceTypes = ["Project", "Executable", "Container"];
private readonly ImmutableArray<string> _allResourceTypes = [KnownResourceTypes.Project, KnownResourceTypes.Executable, KnownResourceTypes.Container];
private readonly HashSet<string> _visibleResourceTypes;
private string _filter = "";
private bool _isTypeFilterVisible;
Expand Down Expand Up @@ -87,7 +87,7 @@ protected override void OnInitialized()
{
_applicationUnviewedErrorCounts = TelemetryRepository.GetApplicationUnviewedErrorLogsCount();

var (snapshot, subscription) = ResourceService.SubscribeResources();
var (snapshot, subscription) = DashboardClient.SubscribeResources();

foreach (var resource in snapshot)
{
Expand All @@ -99,7 +99,17 @@ protected override void OnInitialized()
{
await foreach (var (changeType, resource) in subscription.WithCancellation(_watchTaskCancellationTokenSource.Token))
{
await OnResourceListChanged(changeType, resource);
if (changeType == ResourceViewModelChangeType.Upsert)
{
_resourceByName[resource.Name] = resource;
}
else if (changeType == ResourceViewModelChangeType.Delete)
{
var removed = _resourceByName.TryRemove(resource.Name, out _);
Debug.Assert(removed, "Cannot remove unknown resource.");
}

await InvokeAsync(StateHasChanged);
}
});

Expand All @@ -110,27 +120,6 @@ protected override void OnInitialized()
});
}

private int GetUnviewedErrorCount(ResourceViewModel resource)
{
if (_applicationUnviewedErrorCounts is null)
{
return 0;
}

var application = TelemetryRepository.GetApplication(resource.Uid);
if (application is null)
{
return 0;
}

if (!_applicationUnviewedErrorCounts.TryGetValue(application, out var count))
{
return 0;
}

return count;
}

private void ShowEnvironmentVariables(ResourceViewModel resource)
{
if (SelectedResource == resource)
Expand All @@ -150,23 +139,6 @@ private void ClearSelectedResource()
SelectedResource = null;
}

private async Task OnResourceListChanged(ResourceChangeType changeType, ResourceViewModel resource)
{
switch (changeType)
{
case ResourceChangeType.Upsert:
_resourceByName[resource.Name] = resource;
break;

case ResourceChangeType.Delete:
var removed = _resourceByName.TryRemove(resource.Name, out _);
Debug.Assert(removed, "Cannot remove unknown resource.");
break;
}

await InvokeAsync(StateHasChanged);
}

private string GetResourceName(ResourceViewModel resource) => ResourceViewModel.GetResourceName(resource, _resourceByName.Values);

private bool HasMultipleReplicas(ResourceViewModel resource)
Expand Down Expand Up @@ -203,11 +175,6 @@ public void Dispose()
GC.SuppressFinalize(this);
}

private void ViewErrorStructuredLogs(ResourceViewModel resource)
{
NavigationManager.NavigateTo($"/StructuredLogs/{resource.Uid}?level=error");
}

private string? GetRowClass(ResourceViewModel resource)
=> resource == SelectedResource ? "selected-row" : null;
}
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
@using System.Web
@using Aspire.Dashboard.Resources
@inject NavigationManager NavigationManager
@inject IResourceService resourceService
@inject IDashboardClient DashboardClient
@inject IJSRuntime JS
@implements IDisposable
@inject IStringLocalizer<Dashboard.Resources.StructuredLogs> Loc
@inject IStringLocalizer<ControlsStrings> ControlsStringsLoc

<PageTitle>@string.Format(Loc[nameof(Dashboard.Resources.StructuredLogs.StructuredLogsPageTitle)], resourceService.ApplicationName)</PageTitle>
<PageTitle>@DashboardClient.FormatApplicationName(Loc[nameof(Dashboard.Resources.StructuredLogs.StructuredLogsPageTitle)], () => InvokeAsync(StateHasChanged))</PageTitle>

<div class="logs-layout">
<h1 class="page-header">@Loc[nameof(Dashboard.Resources.StructuredLogs.StructuredLogsHeader)]</h1>
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/Components/Pages/TraceDetail.razor
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
@using Aspire.Dashboard.Resources
@inject IStringLocalizer<Dashboard.Resources.TraceDetail> Loc
@inject IStringLocalizer<ControlsStrings> ControlStringsLoc
@inject IResourceService DashboardService
@inject IDashboardClient DashboardClient

<PageTitle>@string.Format(Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailPageTitle)], DashboardService.ApplicationName)</PageTitle>
<PageTitle>@DashboardClient.FormatApplicationName(Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailPageTitle)], () => InvokeAsync(StateHasChanged))</PageTitle>

@if (_trace != null)
{
Expand Down
5 changes: 2 additions & 3 deletions src/Aspire.Dashboard/Components/Pages/Traces.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
@using Aspire.Dashboard.Otlp.Model
@using Aspire.Dashboard.Resources
@inject NavigationManager NavigationManager
@inject IResourceService ResourceService
@inject IDashboardClient DashboardClient
@inject IJSRuntime JS
@inject IStringLocalizer<Dashboard.Resources.Traces> Loc
@inject IStringLocalizer<ControlsStrings> ControlsStringsLoc
@inject IStringLocalizer<TraceDetail> TraceDetailLoc
@inject IStringLocalizer<Dashboard.Resources.StructuredLogs> StructuredLogsLoc
@implements IDisposable

<PageTitle>@string.Format(Loc[nameof(Dashboard.Resources.Traces.TracesPageTitle)], ResourceService.ApplicationName)</PageTitle>

<PageTitle>@DashboardClient.FormatApplicationName(Loc[nameof(Dashboard.Resources.Traces.TracesPageTitle)], () => InvokeAsync(StateHasChanged))</PageTitle>

<div class="traces-layout">
<h1 class="page-header">@Loc[nameof(Dashboard.Resources.Traces.TracesHeader)]</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@

<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center">
<span><FluentHighlighter HighlightedText="@FilterText" Text="@FormatName(Resource)" /></span>
@if (Resource is ContainerViewModel containerViewModel)
@if (Resource.TryGetContainerId(out var containerId))
{
<div class="subtext">
<GridValue Value="@containerViewModel.ContainerId"
<GridValue Value="@containerId"
MaxDisplayLength="8"
EnableHighlighting="false"
PreCopyToolTip="@Loc[nameof(Columns.ResourceNameDisplayCopyContainerIdText)]"
ToolTip="@string.Format(Loc[nameof(Columns.ResourceNameDisplayContainerIdText)], containerViewModel.ContainerId)"/>
ToolTip="@string.Format(Loc[nameof(Columns.ResourceNameDisplayContainerIdText)], containerId)"/>
</div>
}
else if (Resource is ExecutableViewModel executableViewModel)
else if (Resource.TryGetProcessId(out int processId))
{
// NOTE projects are also executables, so this will handle both
var title = string.Format(Loc[nameof(Columns.ResourceNameDisplayProcessIdText)], executableViewModel.ProcessId);
<span class="subtext" title="@title" aria-label="@title">@executableViewModel.ProcessId</span>
var title = string.Format(Loc[nameof(Columns.ResourceNameDisplayProcessIdText)], processId);
<span class="subtext" title="@title" aria-label="@title">@processId</span>
}
</FluentStack>

Expand Down
Loading