diff --git a/src/Aspire.Dashboard/Components/Interactions/InteractionsProvider.cs b/src/Aspire.Dashboard/Components/Interactions/InteractionsProvider.cs index a2a24fde76d..471b8b2d9f2 100644 --- a/src/Aspire.Dashboard/Components/Interactions/InteractionsProvider.cs +++ b/src/Aspire.Dashboard/Components/Interactions/InteractionsProvider.cs @@ -28,14 +28,14 @@ internal record InteractionMessageBarReference(int InteractionId, Message Messag { public void Dispose() { - TelemetryContext?.Dispose(); + TelemetryContext.Dispose(); } } internal record InteractionDialogReference(int InteractionId, IDialogReference Dialog, ComponentTelemetryContext TelemetryContext) : IDisposable { public void Dispose() { - TelemetryContext?.Dispose(); + TelemetryContext.Dispose(); } } diff --git a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs index 3584084ff3d..46b1619afb4 100644 --- a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs @@ -261,9 +261,22 @@ private void UpdateSubscription() _tracesSubscription?.Dispose(); _tracesSubscription = TelemetryRepository.OnNewTraces(_trace.FirstSpan.Source.ApplicationKey, SubscriptionType.Read, () => InvokeAsync(async () => { - UpdateDetailViewData(); - await InvokeAsync(StateHasChanged); - await _dataGrid.SafeRefreshDataAsync(); + if (_trace == null) + { + return; + } + + // Only update trace if required. + if (TelemetryRepository.HasUpdatedTrace(_trace)) + { + UpdateDetailViewData(); + StateHasChanged(); + await _dataGrid.SafeRefreshDataAsync(); + } + else + { + Logger.LogTrace("Trace '{TraceId}' is unchanged.", TraceId); + } })); } } diff --git a/src/Aspire.Dashboard/Otlp/Model/OtlpSpan.cs b/src/Aspire.Dashboard/Otlp/Model/OtlpSpan.cs index ce47dce949b..6651937478b 100644 --- a/src/Aspire.Dashboard/Otlp/Model/OtlpSpan.cs +++ b/src/Aspire.Dashboard/Otlp/Model/OtlpSpan.cs @@ -42,12 +42,18 @@ public class OtlpSpan public OtlpScope Scope { get; } public TimeSpan Duration => EndTime - StartTime; - public OtlpApplication? UninstrumentedPeer { get; internal set; } + public OtlpApplication? UninstrumentedPeer { get => _uninstrumentedPeer; init => _uninstrumentedPeer = value; } public IEnumerable GetChildSpans() => GetChildSpans(this, Trace.Spans); public static IEnumerable GetChildSpans(OtlpSpan parentSpan, OtlpSpanCollection spans) => spans.Where(s => s.ParentSpanId == parentSpan.SpanId); private string? _cachedDisplaySummary; + private OtlpApplication? _uninstrumentedPeer; + + public void SetUninstrumentedPeer(OtlpApplication? peer) + { + _uninstrumentedPeer = peer; + } public OtlpSpan? GetParentSpan() { diff --git a/src/Aspire.Dashboard/Otlp/Model/OtlpTrace.cs b/src/Aspire.Dashboard/Otlp/Model/OtlpTrace.cs index 01b035b52e0..9d89065c2cd 100644 --- a/src/Aspire.Dashboard/Otlp/Model/OtlpTrace.cs +++ b/src/Aspire.Dashboard/Otlp/Model/OtlpTrace.cs @@ -36,6 +36,7 @@ public TimeSpan Duration } public OtlpSpanCollection Spans { get; } = new OtlpSpanCollection(); + public DateTime LastUpdatedDate { get; private set; } public int CalculateDepth(OtlpSpan span) { @@ -51,7 +52,7 @@ public int CalculateDepth(OtlpSpan span) public int CalculateMaxDepth() => Spans.Max(CalculateDepth); - public void AddSpan(OtlpSpan span) + public void AddSpan(OtlpSpan span, bool skipLastUpdatedDate = false) { if (Spans.Contains(span.SpanId)) { @@ -99,6 +100,11 @@ public void AddSpan(OtlpSpan span) FullName = BuildFullName(span); } + if (!skipLastUpdatedDate) + { + LastUpdatedDate = DateTime.UtcNow; + } + AssertSpanOrder(); static string BuildFullName(OtlpSpan existingSpan) @@ -148,19 +154,20 @@ private void AssertSpanOrder() } } - public OtlpTrace(ReadOnlyMemory traceId) + public OtlpTrace(ReadOnlyMemory traceId, DateTime lastUpdatedDate) { Key = traceId; TraceId = OtlpHelpers.ToHexString(traceId); FullName = string.Empty; + LastUpdatedDate = lastUpdatedDate; } public static OtlpTrace Clone(OtlpTrace trace) { - var newTrace = new OtlpTrace(trace.Key); + var newTrace = new OtlpTrace(trace.Key, trace.LastUpdatedDate); foreach (var item in trace.Spans) { - newTrace.AddSpan(OtlpSpan.Clone(item, newTrace)); + newTrace.AddSpan(OtlpSpan.Clone(item, newTrace), skipLastUpdatedDate: true); } return newTrace; @@ -171,6 +178,17 @@ private string DebuggerToString() return $@"TraceId = ""{TraceId}"", Spans = {Spans.Count}, StartDate = {FirstSpan?.StartTime.ToLocalTime():yyyy:MM:dd}, StartTime = {FirstSpan?.StartTime.ToLocalTime():h:mm:ss.fff tt}, Duration = {Duration}"; } + public void SetSpanUninstrumentedPeer(OtlpSpan span, OtlpApplication? app) + { + if (span.Trace != this) + { + throw new ArgumentException("Span does not belong to this trace.", nameof(span)); + } + + span.SetUninstrumentedPeer(app); + LastUpdatedDate = DateTime.UtcNow; + } + private sealed class SpanStartDateComparer : IComparer { public static readonly SpanStartDateComparer Instance = new SpanStartDateComparer(); diff --git a/src/Aspire.Dashboard/Otlp/Storage/TelemetryRepository.cs b/src/Aspire.Dashboard/Otlp/Storage/TelemetryRepository.cs index 22ae3cd1ce9..59da7230036 100644 --- a/src/Aspire.Dashboard/Otlp/Storage/TelemetryRepository.cs +++ b/src/Aspire.Dashboard/Otlp/Storage/TelemetryRepository.cs @@ -765,13 +765,34 @@ public Dictionary GetLogsFieldValues(string attributeName) return attributesValues; } + public bool HasUpdatedTrace(OtlpTrace trace) + { + _tracesLock.EnterReadLock(); + + try + { + var latestTrace = GetTraceUnsynchronized(trace.TraceId); + if (latestTrace == null) + { + // Trace must have been removed. Technically there is an update (nothing). + return true; + } + + return latestTrace.LastUpdatedDate > trace.LastUpdatedDate; + } + finally + { + _tracesLock.ExitReadLock(); + } + } + public OtlpTrace? GetTrace(string traceId) { _tracesLock.EnterReadLock(); try { - return GetTraceUnsynchronized(traceId); + return GetTraceAndCloneUnsynchronized(traceId); } finally { @@ -787,18 +808,28 @@ public Dictionary GetLogsFieldValues(string attributeName) { if (OtlpHelpers.MatchTelemetryId(traceId, trace.TraceId)) { - return OtlpTrace.Clone(trace); + return trace; } } return null; } - private OtlpSpan? GetSpanUnsynchronized(string traceId, string spanId) + private OtlpTrace? GetTraceAndCloneUnsynchronized(string traceId) { - Debug.Assert(_tracesLock.IsReadLockHeld || _tracesLock.IsWriteLockHeld, $"Must get lock before calling {nameof(GetSpanUnsynchronized)}."); + Debug.Assert(_tracesLock.IsReadLockHeld || _tracesLock.IsWriteLockHeld, $"Must get lock before calling {nameof(GetTraceAndCloneUnsynchronized)}."); var trace = GetTraceUnsynchronized(traceId); + + return trace != null ? OtlpTrace.Clone(trace) : null; + } + + private OtlpSpan? GetSpanAndCloneUnsynchronized(string traceId, string spanId) + { + Debug.Assert(_tracesLock.IsReadLockHeld || _tracesLock.IsWriteLockHeld, $"Must get lock before calling {nameof(GetSpanAndCloneUnsynchronized)}."); + + // Trace and its spans are cloned here. + var trace = GetTraceAndCloneUnsynchronized(traceId); if (trace != null) { foreach (var span in trace.Spans) @@ -819,7 +850,7 @@ public Dictionary GetLogsFieldValues(string attributeName) try { - return GetSpanUnsynchronized(traceId, spanId); + return GetSpanAndCloneUnsynchronized(traceId, spanId); } finally { @@ -938,7 +969,7 @@ internal void AddTracesCore(AddContext context, OtlpApplicationView applicationV { if (!TryGetTraceById(_traces, span.TraceId.Memory, out trace)) { - trace = new OtlpTrace(span.TraceId.Memory); + trace = new OtlpTrace(span.TraceId.Memory, DateTime.UtcNow); newTrace = true; } } @@ -961,7 +992,7 @@ internal void AddTracesCore(AddContext context, OtlpApplicationView applicationV { _spanLinks.Add(link); - var linkedSpan = GetSpanUnsynchronized(link.TraceId, link.SpanId); + var linkedSpan = GetSpanAndCloneUnsynchronized(link.TraceId, link.SpanId); linkedSpan?.BackLinks.Add(link); } @@ -1090,11 +1121,11 @@ private void CalculateTraceUninstrumentedPeers(OtlpTrace trace) var appKey = ApplicationKey.Create(name: uninstrumentedPeer.DisplayName, instanceId: uninstrumentedPeer.Name); var (app, _) = GetOrAddApplication(appKey, uninstrumentedPeer: true); - span.UninstrumentedPeer = app; + trace.SetSpanUninstrumentedPeer(span, app); } else { - span.UninstrumentedPeer = null; + trace.SetSpanUninstrumentedPeer(span, null); } } } diff --git a/tests/Aspire.Dashboard.Components.Tests/Pages/TraceDetailsTests.cs b/tests/Aspire.Dashboard.Components.Tests/Pages/TraceDetailsTests.cs index 3e8985e1da7..336d5e5953d 100644 --- a/tests/Aspire.Dashboard.Components.Tests/Pages/TraceDetailsTests.cs +++ b/tests/Aspire.Dashboard.Components.Tests/Pages/TraceDetailsTests.cs @@ -19,6 +19,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Options; using Microsoft.FluentUI.AspNetCore.Components; using OpenTelemetry.Proto.Trace.V1; @@ -158,6 +159,168 @@ await AsyncTestHelpers.AssertIsTrueRetryAsync(() => }, "Expected rows to be rendered.", logger); } + [Fact] + public async Task Render_TraceUpdateWithNewSpans_RowsRendered() + { + // Arrange + var loggerFactory = IntegrationTestHelpers.CreateLoggerFactory(_testOutputHelper); + var logger = loggerFactory.CreateLogger(nameof(Render_ChangeTrace_RowsRendered)); + + SetupTraceDetailsServices(loggerFactory: loggerFactory); + + var viewport = new ViewportInformation(IsDesktop: true, IsUltraLowHeight: false, IsUltraLowWidth: false); + + var dimensionManager = Services.GetRequiredService(); + dimensionManager.InvokeOnViewportInformationChanged(viewport); + + var telemetryRepository = Services.GetRequiredService(); + telemetryRepository.AddTraces(new AddContext(), new RepeatedField + { + new ResourceSpans + { + Resource = CreateResource(), + ScopeSpans = + { + new ScopeSpans + { + Scope = CreateScope(), + Spans = + { + CreateSpan(traceId: "1", spanId: "1-1", startTime: s_testTime.AddMinutes(1), endTime: s_testTime.AddMinutes(10)), + CreateSpan(traceId: "1", spanId: "1-2", startTime: s_testTime.AddMinutes(5), endTime: s_testTime.AddMinutes(10), parentSpanId: "1-1"), + CreateSpan(traceId: "2", spanId: "2-1", startTime: s_testTime.AddMinutes(6), endTime: s_testTime.AddMinutes(10)) + } + } + } + } + }); + + // Act + var traceId = Convert.ToHexString(Encoding.UTF8.GetBytes("1")); + var cut = RenderComponent(builder => + { + builder.Add(p => p.TraceId, traceId); + builder.AddCascadingValue(viewport); + }); + + // Assert + logger.LogInformation($"Assert row count for '{traceId}'"); + await AsyncTestHelpers.AssertIsTrueRetryAsync(() => + { + var grid = cut.FindComponent>(); + var rows = grid.FindAll(".fluent-data-grid-row"); + return rows.Count == 3; + }, "Expected rows to be rendered.", logger); + + telemetryRepository.AddTraces(new AddContext(), new RepeatedField + { + new ResourceSpans + { + Resource = CreateResource(), + ScopeSpans = + { + new ScopeSpans + { + Scope = CreateScope(), + Spans = + { + CreateSpan(traceId: "1", spanId: "1-3", startTime: s_testTime.AddMinutes(6), endTime: s_testTime.AddMinutes(10), parentSpanId: "1-2"), + } + } + } + } + }); + + logger.LogInformation($"Assert updated row count for '{traceId}'"); + await AsyncTestHelpers.AssertIsTrueRetryAsync(() => + { + var grid = cut.FindComponent>(); + var rows = grid.FindAll(".fluent-data-grid-row"); + return rows.Count == 4; + }, "Expected rows to be rendered.", logger); + } + + [Fact] + public async Task Render_UpdateDifferentTrace_TraceNotUpdated() + { + // Arrange + var testSink = new TestSink(); + var loggerFactory = IntegrationTestHelpers.CreateLoggerFactory(_testOutputHelper, testSink: testSink); + var logger = loggerFactory.CreateLogger(nameof(Render_ChangeTrace_RowsRendered)); + + SetupTraceDetailsServices(loggerFactory: loggerFactory); + + var viewport = new ViewportInformation(IsDesktop: true, IsUltraLowHeight: false, IsUltraLowWidth: false); + + var dimensionManager = Services.GetRequiredService(); + dimensionManager.InvokeOnViewportInformationChanged(viewport); + + var telemetryRepository = Services.GetRequiredService(); + telemetryRepository.AddTraces(new AddContext(), new RepeatedField + { + new ResourceSpans + { + Resource = CreateResource(), + ScopeSpans = + { + new ScopeSpans + { + Scope = CreateScope(), + Spans = + { + CreateSpan(traceId: "1", spanId: "1-1", startTime: s_testTime.AddMinutes(1), endTime: s_testTime.AddMinutes(10)), + CreateSpan(traceId: "1", spanId: "1-2", startTime: s_testTime.AddMinutes(5), endTime: s_testTime.AddMinutes(10), parentSpanId: "1-1"), + CreateSpan(traceId: "2", spanId: "2-1", startTime: s_testTime.AddMinutes(6), endTime: s_testTime.AddMinutes(10)) + } + } + } + } + }); + + // Act + var traceId = Convert.ToHexString(Encoding.UTF8.GetBytes("1")); + var cut = RenderComponent(builder => + { + builder.Add(p => p.TraceId, traceId); + builder.AddCascadingValue(viewport); + }); + + // Assert + logger.LogInformation($"Assert row count for '{traceId}'"); + await AsyncTestHelpers.AssertIsTrueRetryAsync(() => + { + var grid = cut.FindComponent>(); + var rows = grid.FindAll(".fluent-data-grid-row"); + return rows.Count == 3; + }, "Expected rows to be rendered.", logger); + + logger.LogInformation($"Adding span for difference trace"); + telemetryRepository.AddTraces(new AddContext(), new RepeatedField + { + new ResourceSpans + { + Resource = CreateResource(), + ScopeSpans = + { + new ScopeSpans + { + Scope = CreateScope(), + Spans = + { + CreateSpan(traceId: "2", spanId: "2-2", startTime: s_testTime.AddMinutes(7), endTime: s_testTime.AddMinutes(10), parentSpanId: "2-1"), + } + } + } + } + }); + + logger.LogInformation($"Assert not updated for '{traceId}'"); + await AsyncTestHelpers.AssertIsTrueRetryAsync(() => + { + return testSink.Writes.Any(w => w.Message?.Contains($"Trace '{traceId}' is unchanged.") ?? false); + }, "Expected trace not updated.", logger); + } + [Fact] public async Task Render_SpansOrderedByStartTime_RowsRenderedInCorrectOrder() { diff --git a/tests/Aspire.Dashboard.Tests/Model/SpanWaterfallViewModelTests.cs b/tests/Aspire.Dashboard.Tests/Model/SpanWaterfallViewModelTests.cs index a31f3928331..49e8e351d71 100644 --- a/tests/Aspire.Dashboard.Tests/Model/SpanWaterfallViewModelTests.cs +++ b/tests/Aspire.Dashboard.Tests/Model/SpanWaterfallViewModelTests.cs @@ -22,7 +22,7 @@ public void Create_HasChildren_ChildrenPopulated() var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); var app2 = new OtlpApplication("app2", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "1", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc))); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app2, trace, scope, spanId: "1-1", parentSpanId: "1", startDate: new DateTime(2001, 1, 1, 1, 1, 3, DateTimeKind.Utc))); @@ -53,7 +53,7 @@ public void Create_RootSpanZeroDuration_ZeroPercentage() var app1View = new OtlpApplicationView(app1, new RepeatedField()); var date = new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "31", parentSpanId: null, startDate: date, endDate: date)); var log = new OtlpLogEntry(TelemetryTestHelpers.CreateLogRecord(traceId: trace.TraceId, spanId: "1"), app1View, scope, context); @@ -82,7 +82,7 @@ public void Create_OutgoingPeers_BrowserLink() var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); var app2 = new OtlpApplication("app2", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "1", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc), kind: OtlpSpanKind.Client, attributes: [KeyValuePair.Create("http.url", "http://localhost:59267/6eed7c2dedc14419901b813e8fe87a86/getScriptTag"), KeyValuePair.Create("server.address", "localhost")])); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app2, trace, scope, spanId: "2", parentSpanId: null, startDate: new DateTime(2001, 2, 1, 1, 1, 2, DateTimeKind.Utc), kind: OtlpSpanKind.Client)); @@ -115,7 +115,7 @@ public void MatchesFilter_VariousCases_ReturnsExpected(string filter, bool expec // Arrange var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() }; var app = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); // Create a span with an attribute that simulates uninstrumented peer @@ -156,7 +156,7 @@ public void MatchesFilter_ParentSpanIncludedWhenChildMatched() // Arrange var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() }; var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); var parentSpan = TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "parent", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc)); var childSpan = TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "child", parentSpanId: "parent", startDate: new DateTime(2001, 1, 1, 1, 1, 3, DateTimeKind.Utc)); @@ -178,7 +178,7 @@ public void MatchesFilter_ChildSpanIncludedWhenParentMatched() // Arrange var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() }; var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); var parentSpan = TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "parent", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc)); var childSpan = TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "child", parentSpanId: "parent", startDate: new DateTime(2001, 1, 1, 1, 1, 3, DateTimeKind.Utc)); diff --git a/tests/Aspire.Dashboard.Tests/Model/TraceHelpersTests.cs b/tests/Aspire.Dashboard.Tests/Model/TraceHelpersTests.cs index 4e011520119..2c147267aa5 100644 --- a/tests/Aspire.Dashboard.Tests/Model/TraceHelpersTests.cs +++ b/tests/Aspire.Dashboard.Tests/Model/TraceHelpersTests.cs @@ -17,7 +17,7 @@ public void GetOrderedApplications_SingleSpan_GroupedResult() // Arrange var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() }; var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "1", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 1, DateTimeKind.Utc))); @@ -39,7 +39,7 @@ public void GetOrderedApplications_MultipleUnparentedSpans_GroupedResult() var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() }; var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); var app2 = new OtlpApplication("app2", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app2, trace, scope, spanId: "1-2", parentSpanId: "1", startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc))); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "1-1", parentSpanId: "1", startDate: new DateTime(2001, 1, 1, 1, 1, 1, DateTimeKind.Utc))); @@ -66,7 +66,7 @@ public void GetOrderedApplications_ChildSpanAfterParentSpan_GroupedResult() var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() }; var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); var app2 = new OtlpApplication("app2", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "1", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc))); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app2, trace, scope, spanId: "1-1", parentSpanId: "1", startDate: new DateTime(2001, 1, 1, 1, 1, 1, DateTimeKind.Utc))); @@ -94,7 +94,7 @@ public void GetOrderedApplications_ChildSpanDifferentStartTime_GroupedResult() var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); var app2 = new OtlpApplication("app2", "instance", uninstrumentedPeer: false, context); var app3 = new OtlpApplication("app3", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "1", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc))); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app2, trace, scope, spanId: "1-1", parentSpanId: "1", startDate: new DateTime(2001, 1, 1, 1, 1, 3, DateTimeKind.Utc))); @@ -128,7 +128,7 @@ public void GetOrderedApplications_HasUninstrumentedPeer_AddedToResults() var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); var app2 = new OtlpApplication("app2", "instance", uninstrumentedPeer: false, context); var app3 = new OtlpApplication("app3", "instance", uninstrumentedPeer: true, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "1", parentSpanId: null, startDate: new DateTime(2001, 1, 1, 1, 1, 2, DateTimeKind.Utc))); trace.AddSpan(TelemetryTestHelpers.CreateOtlpSpan(app2, trace, scope, spanId: "1-1", parentSpanId: "1", startDate: new DateTime(2001, 1, 1, 1, 1, 3, DateTimeKind.Utc), uninstrumentedPeer: app3)); diff --git a/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/OtlpSpanTests.cs b/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/OtlpSpanTests.cs index eede2740ced..993a7c4fc90 100644 --- a/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/OtlpSpanTests.cs +++ b/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/OtlpSpanTests.cs @@ -19,7 +19,7 @@ public void AllProperties() // Arrange var context = new OtlpContext { Logger = NullLogger.Instance, Options = new() }; var app1 = new OtlpApplication("app1", "instance", uninstrumentedPeer: false, context); - var trace = new OtlpTrace(new byte[] { 1, 2, 3 }); + var trace = new OtlpTrace(new byte[] { 1, 2, 3 }, DateTime.MinValue); var scope = TelemetryTestHelpers.CreateOtlpScope(context); var span = TelemetryTestHelpers.CreateOtlpSpan(app1, trace, scope, spanId: "abc", parentSpanId: null, startDate: s_testTime,