| File: Model\TracesViewModel.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 Aspire.Dashboard.Model.Otlp; using Aspire.Dashboard.Otlp.Model; using Aspire.Dashboard.Otlp.Storage; namespace Aspire.Dashboard.Model; public class TracesViewModel { private readonly TelemetryRepository _telemetryRepository; private readonly List<FieldTelemetryFilter> _filters = new(); private PagedResult<OtlpTrace>? _traces; private ResourceKey? _resourceKey; private string _filterText = string.Empty; private int _startIndex; private int _count; private bool _currentDataHasErrors; private SpanType? _spanType; public TracesViewModel(TelemetryRepository telemetryRepository) { _telemetryRepository = telemetryRepository; } public ResourceKey? ResourceKey { get => _resourceKey; set => SetValue(ref _resourceKey, value); } public SpanType? SpanType { get => _spanType; set => SetValue(ref _spanType, value); } public string FilterText { get => _filterText; set => SetValue(ref _filterText, value); } public int StartIndex { get => _startIndex; set => SetValue(ref _startIndex, value); } public int Count { get => _count; set => SetValue(ref _count, value); } public TimeSpan MaxDuration { get; private set; } public IReadOnlyList<FieldTelemetryFilter> Filters => _filters; public void ClearFilters() { _filters.Clear(); _traces = null; } public void AddFilter(FieldTelemetryFilter filter) { // Don't add duplicate filters. foreach (var existingFilter in _filters) { if (existingFilter.Equals(filter)) { return; } } _filters.Add(filter); _traces = null; } public bool RemoveFilter(FieldTelemetryFilter filter) { if (_filters.Remove(filter)) { _traces = null; return true; } return false; } private void SetValue<T>(ref T field, T value) { if (EqualityComparer<T>.Default.Equals(field, value)) { return; } field = value; _traces = null; } public PagedResult<OtlpTrace> GetTraces() { var traces = _traces; if (traces == null) { var filters = GetFilters(); var result = _telemetryRepository.GetTraces(new GetTracesRequest { ResourceKey = ResourceKey, FilterText = FilterText, StartIndex = StartIndex, Count = Count, Filters = filters }); traces = result.PagedResult; MaxDuration = result.MaxDuration; _currentDataHasErrors = result.PagedResult.Items.Any(t => t.Spans.Any(s => s.Status == OtlpSpanStatusCode.Error)); } return traces; } // First check if there were any errors in already available data. Avoid fetching data again. public bool HasErrors() => _currentDataHasErrors || GetErrorTraces(count: 0).TotalItemCount > 0; public PagedResult<OtlpTrace> GetErrorTraces(int count) { var filters = Filters.Cast<TelemetryFilter>().ToList(); if (SpanType?.Filter is { } typeFilter) { filters.Add(typeFilter); } filters.Add(new FieldTelemetryFilter { Field = KnownTraceFields.StatusField, Condition = FilterCondition.Equals, Value = OtlpSpanStatusCode.Error.ToString() }); var errorTraces = _telemetryRepository.GetTraces(new GetTracesRequest { ResourceKey = ResourceKey, FilterText = FilterText, StartIndex = 0, Count = count, Filters = filters }); return errorTraces.PagedResult; } private List<TelemetryFilter> GetFilters() { var filters = Filters.Cast<TelemetryFilter>().ToList(); if (SpanType?.Filter is { } typeFilter) { filters.Add(typeFilter); } return filters; } public void ClearData() { _traces = null; } }