Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
13fc5e7
Add pause/resume button
adamint Mar 19, 2025
78b42e8
Allow pause/resume inside console logs
adamint Mar 19, 2025
d1bb548
Add pause/resume button to structured logs
adamint Mar 19, 2025
290d129
Add pause/resume button to traces, allow applying timestamp filter to…
adamint Mar 19, 2025
813d211
Add pause/resume to metrics
adamint Mar 19, 2025
876be03
don't resume when switching console log apps
adamint Mar 19, 2025
d1530b1
Add unit test for console log pause/resume
adamint Mar 19, 2025
a2ab30a
Fix added test
adamint Mar 19, 2025
54b64d8
slightly improve test
adamint Mar 19, 2025
85051b5
Use singleton
adamint Mar 20, 2025
356b96b
update dashboard tests
adamint Mar 20, 2025
ccb568b
Add title to clear signals button
adamint Mar 21, 2025
4f5fa2c
Remove unused parameter
adamint Mar 21, 2025
378266b
Rename button to PauseIncomingDataSwitch, remove disable parameter
adamint Mar 21, 2025
e49ca8c
add telemetry repository test, make console log pause range private
adamint Mar 21, 2025
97a34fc
Change button appearance, show pauses in the log viewer
adamint Mar 22, 2025
e000223
clean up pause display
adamint Mar 22, 2025
efc0a2c
Move 2 strings to consolelogs.resx, add a few tests
adamint Mar 22, 2025
0795490
Fix line number problem, add additional tests for pause manager, upda…
adamint Mar 22, 2025
cc75a50
Persist metric pause time so that start time is identical when naviga…
adamint Mar 22, 2025
8c1f019
clean up
adamint Mar 22, 2025
dbb98d5
Merge branch 'main' into dev/adamint/pause-resume-buttons
adamint Mar 22, 2025
51de86c
run custom tool after merge
adamint Mar 22, 2025
247868c
use LogEntry instead of Timestamp as the entry in PauseManager becaus…
adamint Mar 22, 2025
da5693c
Filter out pauses when downloading logs
adamint Mar 22, 2025
c89e5fe
remove debug assrt
adamint Mar 22, 2025
4757bf0
Update pause strings
adamint Mar 25, 2025
28ef562
Avoid pluralizing in TotalItemsFooterText
adamint Mar 25, 2025
9fb557a
include pause info in trace/structured log footers
adamint Mar 25, 2025
f75c553
fix test
adamint Mar 25, 2025
2ab4c29
Text and style changes
JamesNK Mar 26, 2025
7651112
Merge branch 'main' into dev/adamint/pause-resume-buttons
adamint Mar 27, 2025
8706873
Switch to vm in LogEntry
adamint Mar 28, 2025
709089a
Fix skipping line numbers
adamint Mar 28, 2025
e53dc6d
Clean up, fix removing console logs while paused
JamesNK Mar 29, 2025
d0f8ef5
Clean up
JamesNK Mar 29, 2025
98bf5a1
Add lock to console logs page
JamesNK Mar 29, 2025
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 src/Aspire.Dashboard/Aspire.Dashboard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@
<Compile Include="$(SharedDir)StringUtils.cs" Link="Utils\StringUtils.cs" />
<Compile Include="$(SharedDir)ConsoleLogs\LogEntries.cs" Link="Utils\ConsoleLogs\LogEntries.cs" />
<Compile Include="$(SharedDir)ConsoleLogs\LogEntry.cs" Link="Utils\ConsoleLogs\LogEntry.cs" />
<Compile Include="$(SharedDir)ConsoleLogs\LogPauseViewModel.cs" Link="Utils\ConsoleLogs\LogPauseViewModel.cs" />
<Compile Include="$(SharedDir)ConsoleLogs\TimestampParser.cs" Link="Utils\ConsoleLogs\TimestampParser.cs" />
<Compile Include="..\Shared\KnownUrls.cs" Link="Utils\KnownUrls.cs" />
</ItemGroup>
Expand Down
7 changes: 5 additions & 2 deletions src/Aspire.Dashboard/Components/Controls/Chart/ChartBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public abstract class ChartBase : ComponentBase, IAsyncDisposable
[Inject]
public required TelemetryRepository TelemetryRepository { get; init; }

[Inject]
public required PauseManager PauseManager { get; init; }

[Parameter, EditorRequired]
public required InstrumentViewModel InstrumentViewModel { get; set; }

Expand All @@ -66,7 +69,7 @@ protected override void OnInitialized()
{
// Copy the token so there is no chance it is accessed on CTS after it is disposed.
CancellationToken = _cts.Token;
_currentDataStartTime = GetCurrentDataTime();
_currentDataStartTime = PauseManager.AreMetricsPaused(out var pausedAt) ? pausedAt.Value : GetCurrentDataTime();
InstrumentViewModel.DataUpdateSubscriptions.Add(OnInstrumentDataUpdate);
}

Expand All @@ -80,7 +83,7 @@ InstrumentViewModel.MatchedDimensions is null ||
return;
}

var inProgressDataTime = GetCurrentDataTime();
var inProgressDataTime = PauseManager.AreMetricsPaused(out var pausedAt) ? pausedAt.Value : GetCurrentDataTime();

while (_currentDataStartTime.Add(_tickDuration) < inProgressDataTime)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ else
<p>@_instrument.Summary.Description</p>
@if (_instrument.HasOverflow)
{
<div class="overflow-warning">
<div class="overflow-warning-icon">
<div class="block-warning">
<div class="block-warning-icon">
<FluentIcon Value="new Icons.Filled.Size16.ErrorCircle()" Color="Color.Error" />
</div>

<div class="overflow-warning-message">
<div class="block-warning-message">
<span class="title">@Loc[nameof(ControlsStrings.ChartContainerOverflowTitle)]</span>
@Loc[nameof(ControlsStrings.ChartContainerOverflowDescription)]<br/>
@((MarkupString)string.Format(ControlsStrings.ChartContainerOverflowLink, "https://aka.ms/dotnet/aspire/cardinality-limits"))
Expand Down Expand Up @@ -53,14 +53,3 @@ else
</FluentTabs>
</div>
}

@code {
[Parameter, EditorRequired]
public required Metrics.MetricViewKind ActiveView { get; set; }

[Parameter, EditorRequired]
public required Func<Metrics.MetricViewKind, Task> OnViewChangedAsync { get; set; }

[Parameter]
public required List<OtlpApplication> Applications { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ public partial class ChartContainer : ComponentBase, IAsyncDisposable
[Parameter, EditorRequired]
public required TimeSpan Duration { get; set; }

[Parameter, EditorRequired]
public required Pages.Metrics.MetricViewKind ActiveView { get; set; }

[Parameter, EditorRequired]
public required Func<Pages.Metrics.MetricViewKind, Task> OnViewChangedAsync { get; set; }

[Parameter, EditorRequired]
public required List<OtlpApplication> Applications { get; set; }

[Inject]
public required TelemetryRepository TelemetryRepository { get; init; }

Expand All @@ -43,6 +52,9 @@ public partial class ChartContainer : ComponentBase, IAsyncDisposable
[Inject]
public required ThemeManager ThemeManager { get; init; }

[Inject]
public required PauseManager PauseManager { get; init; }

public ImmutableList<DimensionFilterViewModel> DimensionFilters { get; set; } = [];
public string? PreviousMeterName { get; set; }
public string? PreviousInstrumentName { get; set; }
Expand Down Expand Up @@ -79,7 +91,7 @@ private async Task UpdateDataAsync()
while (await timer!.WaitForNextTickAsync())
{
_instrument = GetInstrument();
if (_instrument == null)
if (_instrument == null || PauseManager.AreMetricsPaused(out _))
{
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,3 @@
::deep .metric-tab {
margin-top: 10px;
}

.overflow-warning {
font-family: var(--body-font);
border: 1px solid var(--messagebar-warning-border-color);
background-color: var(--messagebar-warning-background-color);
color: var(--neutral-foreground-rest);
display: grid;
grid-template-columns: 24px auto;
width: fit-content;
align-items: center;
min-height: 36px;
border-radius: calc(var(--control-corner-radius)* 1px);
padding: 0 12px;
column-gap: 8px;
}

.overflow-warning-icon {
grid-column: 1;
display: flex;
justify-content: center;
}

.overflow-warning-message {
grid-column: 2;
padding: 10px 0;
align-self: center;
font-size: 12px;
font-weight: 400;
line-height: 16px;
}

.overflow-warning-message .title {
font-weight: 600;
padding: 0 4px 0 0;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
@namespace Aspire.Dashboard.Components.Controls
@using Aspire.Dashboard.Resources
@using Aspire.Dashboard.Resources
@namespace Aspire.Dashboard.Components.Controls

@inject IStringLocalizer<ControlsStrings> Loc

<AspireMenuButton IconStart="@(new Icons.Regular.Size16.Delete())"
Icon="@(new Icons.Regular.Size12.ChevronDown())"
IconColor="Color.FillInverse"
Items="@_clearMenuItems"
ButtonAppearance="Appearance.Neutral"
ButtonClass="clear-button" />
ButtonClass="clear-button"
Title="@Loc[nameof(ControlsStrings.ClearSignalsButtonTitle)]" />
84 changes: 60 additions & 24 deletions src/Aspire.Dashboard/Components/Controls/LogViewer.razor
Original file line number Diff line number Diff line change
@@ -1,39 +1,75 @@
@namespace Aspire.Dashboard.Components
@using System.Globalization
@using Aspire.Dashboard.Model
@using Aspire.Dashboard.Resources
@using Aspire.Dashboard.Utils
@using Aspire.Hosting.ConsoleLogs

@inject IJSRuntime JS
@inject IStringLocalizer<ConsoleLogs> Loc

@implements IAsyncDisposable

<div class="log-overflow console-overflow continuous-scroll-overflow">
<div class="log-container console-container" id="logContainer">
@if (LogEntries is {} logEntries)
@if (LogEntries is { } logEntries)
{
<Virtualize Items="logEntries.GetEntries()" ItemSize="20" OverscanCount="100" TItem="LogEntry">
<div class="log-line-row-container">
<div class="log-line-row console-line-row">
<span class="log-line-area" role="log">
<span class="log-line-number">@context.LineNumber</span>
<span class="log-content">
@{
var hasPrefix = false;
}
@if (ShowTimestamp && context.Timestamp is { } timestamp)
{
hasPrefix = true;
<span class="timestamp" title="@FormatHelpers.FormatDateTime(TimeProvider, timestamp, MillisecondsDisplay.Full, CultureInfo.CurrentCulture)">@GetDisplayTimestamp(timestamp)</span>
}
@if (context.Type == LogEntryType.Error)
{
hasPrefix = true;
<fluent-badge appearance="accent">stderr</fluent-badge>
}
@((MarkupString)((hasPrefix ? "&#32;" : string.Empty) + (context.Content ?? string.Empty)))
<Virtualize Items="@logEntries.GetEntries()" ItemSize="20" OverscanCount="200" TItem="LogEntry">
@if (context.Pause is { } pause)
{
// If this is a previous pause but no logs were obtained during the pause, we don't need to show anything.
if (pause is { FilteredCount: 0, EndTime: not null })
{
return;
}

var text = pause.EndTime is null
? string.Format(
CultureInfo.CurrentCulture,
Loc[nameof(ConsoleLogs.ConsoleLogsPauseActive)],
FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, pause.StartTime, MillisecondsDisplay.Truncated),
pause.FilteredCount)
: string.Format(
CultureInfo.CurrentCulture,
Loc[nameof(ConsoleLogs.ConsoleLogsPauseDetails)],
FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, pause.StartTime, MillisecondsDisplay.Truncated),
FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, pause.EndTime.Value, MillisecondsDisplay.Truncated),
pause.FilteredCount);

<div class="log-line-row-container">
<div class="log-line-row console-line-row">
<div class="log-line-area" role="log">
<span class="log-line-number"></span>
<span class="log-content log-pause">@text</span>
</div>
</div>
</div>
}
else
{
<div class="log-line-row-container">
<div class="log-line-row console-line-row">
<span class="log-line-area" role="log">
<span class="log-line-number">@context.LineNumber</span>
<span class="log-content">
@{
var hasPrefix = false;
}
@if (ShowTimestamp && context.Timestamp is { } timestamp)
{
hasPrefix = true;
<span class="timestamp" title="@FormatHelpers.FormatDateTime(TimeProvider, timestamp, MillisecondsDisplay.Full, CultureInfo.CurrentCulture)">@GetDisplayTimestamp(timestamp)</span>
}
@if (context.Type == LogEntryType.Error)
{
hasPrefix = true;
<fluent-badge appearance="accent">stderr</fluent-badge>
}
@((MarkupString)((hasPrefix ? "&#32;" : string.Empty) + (context.Content ?? string.Empty)))
</span>
</span>
</span>
</div>
</div>
</div>
}
</Virtualize>
}
</div>
Expand Down
6 changes: 6 additions & 0 deletions src/Aspire.Dashboard/Components/Controls/LogViewer.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public sealed partial class LogViewer
[Inject]
public required ILogger<LogViewer> Logger { get; init; }

[Inject]
public required PauseManager PauseManager { get; init; }

[Parameter]
public LogEntries? LogEntries { get; set; } = null!;

Expand All @@ -36,6 +39,9 @@ public sealed partial class LogViewer
[Parameter]
public bool IsTimestampUtc { get; set; }

[Parameter]
public string? ApplicationName { get; set; }

protected override void OnParametersSet()
{
if (_logEntries != LogEntries)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@using Aspire.Dashboard.Resources
@inject IStringLocalizer<ControlsStrings> Loc

<FluentButton Appearance="Appearance.Neutral"
Title="@(IsPaused ? Loc[nameof(ControlsStrings.ResumeButtonTitle)] : Loc[nameof(ControlsStrings.PauseButtonTitle)])"
OnClick="@OnTogglePauseCoreAsync">
@if (IsPaused)
{
<FluentIcon Value="@(new Icons.Regular.Size16.PauseOff())" />
}
else
{
<FluentIcon Value="@(new Icons.Regular.Size16.Pause())" />
}
</FluentButton>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Components;

namespace Aspire.Dashboard.Components.Controls;

public partial class PauseIncomingDataSwitch : ComponentBase
{
[Parameter]
public bool IsPaused { get; set; }

[Parameter]
public EventCallback<bool> IsPausedChanged { get; set; }

private async Task OnTogglePauseCoreAsync()
{
IsPaused = !IsPaused;
await IsPausedChanged.InvokeAsync(IsPaused);
}
}

23 changes: 22 additions & 1 deletion src/Aspire.Dashboard/Components/Controls/TotalItemsFooter.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,25 @@
@namespace Aspire.Dashboard.Components
@inject IStringLocalizer<ControlsStrings> Loc

<div class="total-items-footer">@((MarkupString)string.Format(Loc[nameof(ControlsStrings.TotalItemsFooterText)], TotalItemCount))</div>
<div class="items-footer">
<span class="result-count">
@((MarkupString)string.Format(Loc[nameof(ControlsStrings.TotalItemsFooterText)], TotalItemCount))
</span>

@if (PauseText is not null)
{
<div class="block-warning">
<div class="block-warning-icon">
<FluentIcon Value="new Icons.Filled.Size16.ErrorCircle()" Color="Color.Error" />
</div>

<div class="block-warning-message">
<span class="title">@Loc[nameof(ControlsStrings.TotalItemsFooterCapturePaused)]</span>
@PauseText
</div>
</div>
}
</div>

@code {
// Total item count can be set via the parameter or via method.
Expand All @@ -15,6 +33,9 @@
[Parameter]
public int TotalItemCount { get; set; }

[Parameter]
public string? PauseText { get; set; }

/// <summary>
/// Called when data grid data is refreshed. This sets the count explicitly and forces the control to re-render.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
{
<div class="log-overflow">
<div class="log-container" id="@_logContainerId">
<Virtualize Items="@FormattedLines" ItemSize="20" OverscanCount="100">
<Virtualize Items="@FormattedLines" ItemSize="20" OverscanCount="200">
<div class="log-line-row-container">
<div class="log-line-row">
<span class="log-line-area">
Expand Down
Loading
Loading