File: Components\Controls\ResourcePaneInsights.razor.cs
Web Access
Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using Aspire.Dashboard.Components.Layout;
using Aspire.Dashboard.Extensions;
using Aspire.Dashboard.Model;
using Aspire.Dashboard.Model.Otlp;
using Aspire.Dashboard.Otlp.Model;
using Aspire.Dashboard.Otlp.Storage;
using Aspire.Dashboard.Utils;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
 
namespace Aspire.Dashboard.Components;
 
public partial class ResourcePaneInsights
{
    private ResourcePaneActivitySnapshot? _snapshot;
 
    [Parameter, EditorRequired]
    public required ResourcesLayout Layout { get; set; }
 
    [Parameter, EditorRequired]
    public required ResourceKey ResourceKey { get; set; }
 
    [Parameter, EditorRequired]
    public required string ResourceName { get; set; }
 
    [Inject]
    public required IStringLocalizer<Resources.Layout> Loc { get; init; }
 
    [Inject]
    public required BrowserTimeProvider TimeProvider { get; init; }
 
    protected override void OnInitialized() => Layout.PaneActivityChanged += OnActivityChanged;
 
    protected override void OnParametersSet() => _snapshot = Layout.GetPaneActivitySnapshot(ResourceKey);
 
    private void OnActivityChanged()
    {
        var snapshot = Layout.GetPaneActivitySnapshot(ResourceKey);
        if (snapshot != _snapshot)
        {
            _snapshot = snapshot;
            StateHasChanged();
        }
    }
 
    private string GetContext(ResourcePaneActivitySnapshot snapshot)
    {
        var context = string.Format(CultureInfo.CurrentCulture, Loc[nameof(Resources.Layout.ResourcePaneActivityContext)],
            ResourceName,
            FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, snapshot.WindowStart),
            FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, snapshot.WindowEnd),
            FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, snapshot.Activity.LatestStartTime),
            FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, snapshot.Refreshed));
        var samples = string.Format(CultureInfo.CurrentCulture, Loc[nameof(Resources.Layout.ResourcePaneActivitySamples)], snapshot.Activity.DurationSampleCount);
        return snapshot.Sampled
            ? $"{context} {samples} {Loc[nameof(Resources.Layout.ResourcePaneActivitySampled)]}"
            : $"{context} {samples}";
    }
 
    private string GetTracesUrl(ResourcePaneActivitySnapshot snapshot, bool failed)
    {
        List<FieldTelemetryFilter> filters =
        [
            new() { Field = KnownTraceFields.TimestampField, Condition = FilterCondition.GreaterThanOrEqual, Value = snapshot.WindowStart.ToString("O", CultureInfo.InvariantCulture) },
            new() { Field = KnownTraceFields.TimestampField, Condition = FilterCondition.LessThan, Value = snapshot.WindowEnd.ToString("O", CultureInfo.InvariantCulture) }
        ];
        if (failed)
        {
            filters.Add(new() { Field = KnownTraceFields.StatusField, Condition = FilterCondition.Equals, Value = nameof(OtlpSpanStatusCode.Error) });
        }
 
        // The timestamp filter grammar escapes ':' before URL encoding. Preserve both
        // layers (e.g. "%253A"); the traces page decodes them in the reverse order.
        return DashboardUrls.TracesUrl(ResourceKey.GetCompositeName(), filters: TelemetryFilterFormatter.SerializeFiltersToString(filters));
    }
 
    public void Dispose() => Layout.PaneActivityChanged -= OnActivityChanged;
}