diff --git a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor index 07d95732f4..f85022b5b6 100644 --- a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor +++ b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor @@ -141,8 +141,9 @@
+ @* First column starts at 0. We don't want to display the smallest unit (0μs) because that looks odd. Use the unit from the next column *@
- @DurationFormatter.FormatDuration(TimeSpan.Zero) + @($"0{DurationFormatter.GetUnit(trace.Duration / 4)}")
@DurationFormatter.FormatDuration(trace.Duration / 4) diff --git a/src/Aspire.Dashboard/Otlp/Model/DurationFormatter.cs b/src/Aspire.Dashboard/Otlp/Model/DurationFormatter.cs index d36f6a21c5..7c79c27dea 100644 --- a/src/Aspire.Dashboard/Otlp/Model/DurationFormatter.cs +++ b/src/Aspire.Dashboard/Otlp/Model/DurationFormatter.cs @@ -1,10 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; + namespace Aspire.Dashboard.Otlp.Model; public static class DurationFormatter { + [DebuggerDisplay("Unit = {Unit}, Ticks = {Ticks}, IsDecimal = {IsDecimal}")] private sealed class UnitStep { public required string Unit { get; init; } @@ -28,12 +31,6 @@ public static string FormatDuration(TimeSpan duration) var ofPrevious = primaryUnit.Ticks / secondaryUnit.Ticks; var ticks = (double)duration.Ticks; - // Special case time 0 to not display any unit, as "0μs" looks quirky - if (ticks == 0) - { - return "0"; - } - if (primaryUnit.IsDecimal) { // If the unit is decimal based, display as a decimal @@ -48,6 +45,16 @@ public static string FormatDuration(TimeSpan duration) return secondaryValue == 0 ? primaryUnitString : $"{primaryUnitString} {secondaryUnitString}"; } + public static string GetUnit(TimeSpan duration) + { + var (primaryUnit, secondaryUnit) = ResolveUnits(duration.Ticks); + if (primaryUnit.IsDecimal) + { + return primaryUnit.Unit; + } + return secondaryUnit.Unit; + } + private static (UnitStep, UnitStep) ResolveUnits(long ticks) { for (var i = 0; i < s_unitSteps.Count; i++) diff --git a/tests/Aspire.Dashboard.Tests/DurationFormatterTests.cs b/tests/Aspire.Dashboard.Tests/DurationFormatterTests.cs index f3b177a46f..3338078cf3 100644 --- a/tests/Aspire.Dashboard.Tests/DurationFormatterTests.cs +++ b/tests/Aspire.Dashboard.Tests/DurationFormatterTests.cs @@ -9,6 +9,20 @@ namespace Aspire.Dashboard.Tests; public class DurationFormatterTests { + [Theory] + [InlineData(0, "μs")] + [InlineData(1, "μs")] + [InlineData(1_000, "μs")] + [InlineData(1_000_000, "ms")] + [InlineData(1_000_000_000, "s")] + [InlineData(1_000_000_000_000, "h")] + [InlineData(1_000_000_000_000_000, "h")] + [InlineData(1_000_000_000_000_000_000, "h")] + public void GetUnit(long ticks, string unit) + { + Assert.Equal(unit, DurationFormatter.GetUnit(TimeSpan.FromTicks(ticks))); + } + [Fact] public void KeepsMicrosecondsTheSame() { @@ -68,6 +82,6 @@ public void DisplaysTimesLessThanMicroseconds() public void DisplaysTimesOf0() { var input = 0; - Assert.Equal("0", DurationFormatter.FormatDuration(TimeSpan.FromTicks(input))); + Assert.Equal("0μs", DurationFormatter.FormatDuration(TimeSpan.FromTicks(input))); } }