Skip to content

Commit 84a7df1

Browse files
committed
Squashed commit of the following:
commit 9071c84 Author: Ankit Jain <radical@gmail.com> Date: Fri Jul 12 19:04:26 2024 -0400 fix e2e commit 68a7ae0 Merge: 09acf2d 681f2e7 Author: Ankit Jain <radical@gmail.com> Date: Fri Jul 12 18:57:33 2024 -0400 Merge remote-tracking branch 'origin/main' into tests-out-of-repo commit 681f2e7 Author: Dan Moseley <danmose@microsoft.com> Date: Fri Jul 12 14:04:57 2024 -0600 Make credscan run clean (microsoft#4876) * suppressions * more * typo commit a133411 Author: Dan Moseley <danmose@microsoft.com> Date: Fri Jul 12 08:42:40 2024 -0600 add TSA config (microsoft#4860) * tsaconfig * more * remove the config per Matt * add policheck in 1ES template * policheck exclusions commit cf61a58 Author: James Newton-King <james@newtonking.com> Date: Fri Jul 12 13:00:02 2024 +0800 Load plotly as a module on metrics page (microsoft#4857) commit 09acf2d Author: Ankit Jain <radical@gmail.com> Date: Fri Jul 12 00:34:04 2024 -0400 fix archive path for e2e commit da382ff Merge: 18b92c3 de35684 Author: Ankit Jain <radical@gmail.com> Date: Fri Jul 12 00:12:02 2024 -0400 Merge remote-tracking branch 'origin/main' into tests-out-of-repo commit 18b92c3 Author: Ankit Jain <radical@gmail.com> Date: Fri Jul 12 00:07:52 2024 -0400 merge from playground branch commit de35684 Author: James Newton-King <james@newtonking.com> Date: Fri Jul 12 09:43:58 2024 +0800 Add support for exemplars in metrics UI (microsoft#4629) commit e23cd4c Merge: ef3158b 9421fc8 Author: Ankit Jain <radical@gmail.com> Date: Thu Jul 11 18:00:20 2024 -0400 Merge remote-tracking branch 'origin/main' into tests-out-of-repo commit ef3158b Author: Ankit Jain <radical@gmail.com> Date: Thu Jul 11 03:27:52 2024 -0400 wip commit 7c61df1 Author: Ankit Jain <radical@gmail.com> Date: Thu Jul 11 02:48:20 2024 -0400 fixies mores commit d6e21ab Author: Ankit Jain <radical@gmail.com> Date: Thu Jul 11 03:10:21 2024 -0400 share targets commit 01e419d Merge: 0741157 421f8b1 Author: Ankit Jain <radical@gmail.com> Date: Thu Jul 11 03:00:16 2024 -0400 Merge remote-tracking branch 'origin/main' into tests-out-of-repo # Conflicts: # tests/helix/send-to-helix-inner.proj commit 0741157 Merge: 3406514 f647a66 Author: Ankit Jain <radical@gmail.com> Date: Tue Jul 9 18:43:36 2024 -0400 Merge remote-tracking branch 'origin/main' into tests-out-of-repo commit 3406514 Author: Ankit Jain <radical@gmail.com> Date: Mon Jul 8 21:18:23 2024 -0400 fix build commit 3489ff3 Author: Ankit Jain <radical@gmail.com> Date: Mon Jul 8 20:23:10 2024 -0400 fix props generation commit 6a6315f Author: Ankit Jain <radical@gmail.com> Date: Mon Jul 8 20:14:53 2024 -0400 fix commit 92642ea Author: Ankit Jain <radical@gmail.com> Date: Mon Jul 8 19:36:17 2024 -0400 rename commit 4bea9f4 Author: Ankit Jain <radical@gmail.com> Date: Mon Jul 8 19:21:36 2024 -0400 cleanup # Conflicts: # tests/Aspire.EndToEnd.Tests/Aspire.EndToEnd.Tests.csproj # tests/Shared/RepoTesting/Aspire.Testing.Repo.targets
1 parent d06754f commit 84a7df1

74 files changed

Lines changed: 2294 additions & 333 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.config/CredScanSuppressions.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
{
2525
"placeholder": "thisIsAFakeSecret",
2626
"_justification": "This isn't a real secret, it's used in one of the playground applications for testing purposes."
27+
},
28+
{
29+
"file": "\\tests\\Shared\\TestCertificates\\eku.client.pfx",
30+
"_justification": "Legitimate UT certificate file with private key, from dotnet/aspnetcore"
31+
},
32+
{
33+
"file": "\\tests\\Shared\\TestCertificates\\testCert.pfx",
34+
"_justification": "Legitimate UT certificate file with private key, from dotnet/aspnetcore"
2735
}
2836
]
2937
}

.config/PoliCheckExclusions.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<PoliCheckExclusions>
2+
3+
</PoliCheckExclusions>

.config/tsaoptions.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"instanceUrl": "https://devdiv.visualstudio.com/",
3+
"template": "TFSDEVDIV",
4+
"projectName": "DEVDIV",
5+
"areaPath": "DevDiv\\ASP.NET Core\\Policy Violations",
6+
"iterationPath": "DevDiv",
7+
"notificationAliases": [ "aspnetcore-build@microsoft.com" ],
8+
"repositoryName": "aspire",
9+
"codebaseName": "aspire"
10+
}

eng/pipelines/azure-pipelines.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ extends:
9595
template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
9696
parameters:
9797
sdl:
98+
policheck:
99+
enabled: true
100+
exclusionsFile: $(Build.SourcesDirectory)\.config\PoliCheckExclusions.xml
98101
eslint:
99102
enabled: false
100103
justificationForDisabling: 'see https://portal.microsofticm.com/imp/v3/incidents/incident/482258316/summary'

src/Aspire.Dashboard/Components/App.razor

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,5 @@
2828
<Routes @rendermode="@(new InteractiveServerRenderMode(prerender: false))" />
2929
<script src="_framework/blazor.web.js"></script>
3030
<script src="js/app.js"></script>
31-
<script src="js/theme.js" type="module"></script>
32-
<script src="js/plotly-2.32.0.min.js"></script>
3331
</body>
3432
</html>

src/Aspire.Dashboard/Components/Controls/Chart/ChartBase.cs

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Aspire.Dashboard.Model;
99
using Aspire.Dashboard.Otlp.Model;
1010
using Aspire.Dashboard.Otlp.Model.MetricValues;
11+
using Aspire.Dashboard.Otlp.Storage;
1112
using Aspire.Dashboard.Resources;
1213
using Aspire.Dashboard.Utils;
1314
using Microsoft.AspNetCore.Components;
@@ -30,18 +31,34 @@ public abstract class ChartBase : ComponentBase
3031
[Inject]
3132
public required IStringLocalizer<ControlsStrings> Loc { get; init; }
3233

34+
[Inject]
35+
public required IStringLocalizer<Resources.Dialogs> DialogsLoc { get; init; }
36+
3337
[Inject]
3438
public required IInstrumentUnitResolver InstrumentUnitResolver { get; init; }
3539

3640
[Inject]
3741
public required BrowserTimeProvider TimeProvider { get; init; }
3842

43+
[Inject]
44+
public required TelemetryRepository TelemetryRepository { get; init; }
45+
3946
[Parameter, EditorRequired]
4047
public required InstrumentViewModel InstrumentViewModel { get; set; }
4148

4249
[Parameter, EditorRequired]
4350
public required TimeSpan Duration { get; set; }
4451

52+
[Parameter]
53+
public required List<OtlpApplication> Applications { get; set; }
54+
55+
// Stores a cache of the last set of spans returned as exemplars.
56+
// This dictionary is replaced each time the chart is updated.
57+
private Dictionary<SpanKey, OtlpSpan> _currentCache = new Dictionary<SpanKey, OtlpSpan>();
58+
private Dictionary<SpanKey, OtlpSpan> _newCache = new Dictionary<SpanKey, OtlpSpan>();
59+
60+
private readonly record struct SpanKey(string TraceId, string SpanId);
61+
4562
protected override void OnInitialized()
4663
{
4764
_currentDataStartTime = GetCurrentDataTime();
@@ -50,7 +67,7 @@ protected override void OnInitialized()
5067

5168
protected override async Task OnAfterRenderAsync(bool firstRender)
5269
{
53-
if (InstrumentViewModel.Instrument is null || InstrumentViewModel.MatchedDimensions is null)
70+
if (InstrumentViewModel.Instrument is null || InstrumentViewModel.MatchedDimensions is null || !ReadyForData())
5471
{
5572
return;
5673
}
@@ -93,7 +110,7 @@ private Task OnInstrumentDataUpdate()
93110
return InvokeAsync(StateHasChanged);
94111
}
95112

96-
private (List<ChartTrace> Y, List<DateTimeOffset> X) CalculateHistogramValues(List<DimensionScope> dimensions, int pointCount, bool tickUpdate, DateTimeOffset inProgressDataTime, string yLabel)
113+
private (List<ChartTrace> Y, List<DateTimeOffset> X, List<ChartExemplar> Exemplars) CalculateHistogramValues(List<DimensionScope> dimensions, int pointCount, bool tickUpdate, DateTimeOffset inProgressDataTime, string yLabel)
97114
{
98115
var pointDuration = Duration / pointCount;
99116
var traces = new Dictionary<int, ChartTrace>
@@ -103,8 +120,10 @@ private Task OnInstrumentDataUpdate()
103120
[99] = new() { Name = $"P99 {yLabel}", Percentile = 99 }
104121
};
105122
var xValues = new List<DateTimeOffset>();
123+
var exemplars = new List<ChartExemplar>();
106124
var startDate = _currentDataStartTime;
107125
DateTimeOffset? firstPointEndTime = null;
126+
DateTimeOffset? lastPointStartTime = null;
108127

109128
// Generate the points in reverse order so that the chart is drawn from right to left.
110129
// Add a couple of extra points to the end so that the chart is drawn all the way to the right edge.
@@ -113,10 +132,11 @@ private Task OnInstrumentDataUpdate()
113132
var start = CalcOffset(pointIndex, startDate, pointDuration);
114133
var end = CalcOffset(pointIndex - 1, startDate, pointDuration);
115134
firstPointEndTime ??= end;
135+
lastPointStartTime = start;
116136

117137
xValues.Add(TimeProvider.ToLocalDateTimeOffset(end));
118138

119-
if (!TryCalculateHistogramPoints(dimensions, start, end, traces))
139+
if (!TryCalculateHistogramPoints(dimensions, start, end, traces, exemplars))
120140
{
121141
foreach (var trace in traces)
122142
{
@@ -131,7 +151,7 @@ private Task OnInstrumentDataUpdate()
131151
}
132152
xValues.Reverse();
133153

134-
if (tickUpdate && TryCalculateHistogramPoints(dimensions, firstPointEndTime!.Value, inProgressDataTime, traces))
154+
if (tickUpdate && TryCalculateHistogramPoints(dimensions, firstPointEndTime!.Value, inProgressDataTime, traces, exemplars))
135155
{
136156
xValues.Add(TimeProvider.ToLocalDateTimeOffset(inProgressDataTime));
137157
}
@@ -161,12 +181,15 @@ private Task OnInstrumentDataUpdate()
161181

162182
previousValues = currentTrace;
163183
}
164-
return (traces.Select(kvp => kvp.Value).ToList(), xValues);
184+
185+
exemplars = exemplars.Where(p => p.Start <= startDate && p.Start >= lastPointStartTime!.Value).OrderBy(p => p.Start).ToList();
186+
187+
return (traces.Select(kvp => kvp.Value).ToList(), xValues, exemplars);
165188
}
166189

167190
private string FormatTooltip(string name, double yValue, DateTimeOffset xValue)
168191
{
169-
return $"<b>{HttpUtility.HtmlEncode(InstrumentViewModel.Instrument?.Name)}</b><br />{HttpUtility.HtmlEncode(name)}: {FormatHelpers.FormatNumberWithOptionalDecimalPlaces(yValue, CultureInfo.CurrentCulture)}<br />Time: {FormatHelpers.FormatTime(TimeProvider, TimeProvider.ToLocal(xValue))}";
192+
return $"<b>{HttpUtility.HtmlEncode(InstrumentViewModel.Instrument?.Name)}</b><br />{HttpUtility.HtmlEncode(name)}: {FormatHelpers.FormatNumberWithOptionalDecimalPlaces(yValue, maxDecimalPlaces: 6, CultureInfo.CurrentCulture)}<br />Time: {FormatHelpers.FormatTime(TimeProvider, TimeProvider.ToLocal(xValue))}";
170193
}
171194

172195
private static HistogramValue GetHistogramValue(MetricValueBase metric)
@@ -179,7 +202,7 @@ private static HistogramValue GetHistogramValue(MetricValueBase metric)
179202
throw new InvalidOperationException("Unexpected metric type: " + metric.GetType());
180203
}
181204

182-
internal static bool TryCalculateHistogramPoints(List<DimensionScope> dimensions, DateTimeOffset start, DateTimeOffset end, Dictionary<int, ChartTrace> traces)
205+
internal bool TryCalculateHistogramPoints(List<DimensionScope> dimensions, DateTimeOffset start, DateTimeOffset end, Dictionary<int, ChartTrace> traces, List<ChartExemplar> exemplars)
183206
{
184207
var hasValue = false;
185208

@@ -199,6 +222,8 @@ internal static bool TryCalculateHistogramPoints(List<DimensionScope> dimensions
199222
{
200223
var histogramValue = GetHistogramValue(metric);
201224

225+
AddExemplars(exemplars, metric);
226+
202227
// Only use the first recorded entry if it is the beginning of data.
203228
// We can verify the first entry is the beginning of data by checking if the number of buckets equals the total count.
204229
if (i == 0 && CountBuckets(histogramValue) != histogramValue.Count)
@@ -247,6 +272,57 @@ internal static bool TryCalculateHistogramPoints(List<DimensionScope> dimensions
247272
return hasValue;
248273
}
249274

275+
private void AddExemplars(List<ChartExemplar> exemplars, MetricValueBase metric)
276+
{
277+
if (metric.HasExemplars)
278+
{
279+
foreach (var exemplar in metric.Exemplars)
280+
{
281+
// TODO: Exemplars are duplicated on metrics in some scenarios.
282+
// This is a quick fix to ensure a distinct collection of metrics are displayed in the UI.
283+
// Investigation is needed into why there are duplicates.
284+
var exists = false;
285+
foreach (var existingExemplar in exemplars)
286+
{
287+
if (exemplar.Start == existingExemplar.Start &&
288+
exemplar.Value == existingExemplar.Value &&
289+
exemplar.SpanId == existingExemplar.SpanId &&
290+
exemplar.TraceId == existingExemplar.TraceId)
291+
{
292+
exists = true;
293+
break;
294+
}
295+
}
296+
if (exists)
297+
{
298+
continue;
299+
}
300+
301+
// Try to find span the the local cache first.
302+
// This is done to avoid scanning a potentially large trace collection in repository.
303+
var key = new SpanKey(exemplar.TraceId, exemplar.SpanId);
304+
if (!_currentCache.TryGetValue(key, out var span))
305+
{
306+
span = GetSpan(exemplar.TraceId, exemplar.SpanId);
307+
}
308+
if (span != null)
309+
{
310+
_newCache[key] = span;
311+
}
312+
313+
var exemplarStart = TimeProvider.ToLocalDateTimeOffset(exemplar.Start);
314+
exemplars.Add(new ChartExemplar
315+
{
316+
Start = exemplarStart,
317+
Value = exemplar.Value,
318+
TraceId = exemplar.TraceId,
319+
SpanId = exemplar.SpanId,
320+
Span = span
321+
});
322+
}
323+
}
324+
}
325+
250326
private static ulong CountBuckets(HistogramValue histogramValue)
251327
{
252328
ulong value = 0ul;
@@ -287,11 +363,12 @@ private static ulong CountBuckets(HistogramValue histogramValue)
287363
return explicitBounds[explicitBounds.Length - 1];
288364
}
289365

290-
private (List<ChartTrace> Y, List<DateTimeOffset> X) CalculateChartValues(List<DimensionScope> dimensions, int pointCount, bool tickUpdate, DateTimeOffset inProgressDataTime, string yLabel)
366+
private (List<ChartTrace> Y, List<DateTimeOffset> X, List<ChartExemplar> Exemplars) CalculateChartValues(List<DimensionScope> dimensions, int pointCount, bool tickUpdate, DateTimeOffset inProgressDataTime, string yLabel)
291367
{
292368
var pointDuration = Duration / pointCount;
293369
var yValues = new List<double?>();
294370
var xValues = new List<DateTimeOffset>();
371+
var exemplars = new List<ChartExemplar>();
295372
var startDate = _currentDataStartTime;
296373
DateTimeOffset? firstPointEndTime = null;
297374

@@ -305,7 +382,7 @@ private static ulong CountBuckets(HistogramValue histogramValue)
305382

306383
xValues.Add(TimeProvider.ToLocalDateTimeOffset(end));
307384

308-
if (TryCalculatePoint(dimensions, start, end, out var tickPointValue))
385+
if (TryCalculatePoint(dimensions, start, end, exemplars, out var tickPointValue))
309386
{
310387
yValues.Add(tickPointValue);
311388
}
@@ -318,7 +395,7 @@ private static ulong CountBuckets(HistogramValue histogramValue)
318395
yValues.Reverse();
319396
xValues.Reverse();
320397

321-
if (tickUpdate && TryCalculatePoint(dimensions, firstPointEndTime!.Value, inProgressDataTime, out var inProgressPointValue))
398+
if (tickUpdate && TryCalculatePoint(dimensions, firstPointEndTime!.Value, inProgressDataTime, exemplars, out var inProgressPointValue))
322399
{
323400
yValues.Add(inProgressPointValue);
324401
xValues.Add(TimeProvider.ToLocalDateTimeOffset(inProgressDataTime));
@@ -343,10 +420,10 @@ private static ulong CountBuckets(HistogramValue histogramValue)
343420
}
344421
}
345422

346-
return ([trace], xValues);
423+
return ([trace], xValues, exemplars);
347424
}
348425

349-
private static bool TryCalculatePoint(List<DimensionScope> dimensions, DateTimeOffset start, DateTimeOffset end, out double pointValue)
426+
private bool TryCalculatePoint(List<DimensionScope> dimensions, DateTimeOffset start, DateTimeOffset end, List<ChartExemplar> exemplars, out double pointValue)
350427
{
351428
var hasValue = false;
352429
pointValue = 0d;
@@ -371,6 +448,8 @@ private static bool TryCalculatePoint(List<DimensionScope> dimensions, DateTimeO
371448
dimensionValue = Math.Max(value, dimensionValue);
372449
hasValue = true;
373450
}
451+
452+
AddExemplars(exemplars, metric);
374453
}
375454

376455
pointValue += dimensionValue;
@@ -406,16 +485,29 @@ private async Task UpdateChart(bool tickUpdate, DateTimeOffset inProgressDataTim
406485

407486
List<ChartTrace> traces;
408487
List<DateTimeOffset> xValues;
488+
List<ChartExemplar> exemplars;
409489
if (InstrumentViewModel.Instrument?.Type != OtlpInstrumentType.Histogram || InstrumentViewModel.ShowCount)
410490
{
411-
(traces, xValues) = CalculateChartValues(InstrumentViewModel.MatchedDimensions, GraphPointCount, tickUpdate, inProgressDataTime, unit);
491+
(traces, xValues, exemplars) = CalculateChartValues(InstrumentViewModel.MatchedDimensions, GraphPointCount, tickUpdate, inProgressDataTime, unit);
492+
493+
// TODO: Exemplars on non-histogram charts doesn't work well. Don't display for now.
494+
exemplars.Clear();
412495
}
413496
else
414497
{
415-
(traces, xValues) = CalculateHistogramValues(InstrumentViewModel.MatchedDimensions, GraphPointCount, tickUpdate, inProgressDataTime, unit);
498+
(traces, xValues, exemplars) = CalculateHistogramValues(InstrumentViewModel.MatchedDimensions, GraphPointCount, tickUpdate, inProgressDataTime, unit);
416499
}
417500

418-
await OnChartUpdated(traces, xValues, tickUpdate, inProgressDataTime);
501+
// Replace cache for next update.
502+
_currentCache = _newCache;
503+
_newCache = new Dictionary<SpanKey, OtlpSpan>();
504+
505+
await OnChartUpdated(traces, xValues, exemplars, tickUpdate, inProgressDataTime);
506+
}
507+
508+
protected OtlpSpan? GetSpan(string traceId, string spanId)
509+
{
510+
return MetricsHelpers.GetSpan(TelemetryRepository, traceId, spanId);
419511
}
420512

421513
private DateTimeOffset GetCurrentDataTime()
@@ -425,8 +517,10 @@ private DateTimeOffset GetCurrentDataTime()
425517

426518
private string GetDisplayedUnit(OtlpInstrument instrument)
427519
{
428-
return InstrumentUnitResolver.ResolveDisplayedUnit(instrument);
520+
return InstrumentUnitResolver.ResolveDisplayedUnit(instrument, titleCase: true, pluralize: true);
429521
}
430522

431-
protected abstract Task OnChartUpdated(List<ChartTrace> traces, List<DateTimeOffset> xValues, bool tickUpdate, DateTimeOffset inProgressDataTime);
523+
protected abstract Task OnChartUpdated(List<ChartTrace> traces, List<DateTimeOffset> xValues, List<ChartExemplar> exemplars, bool tickUpdate, DateTimeOffset inProgressDataTime);
524+
525+
protected virtual bool ReadyForData() => true;
432526
}

src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ else
2525
Label="@Loc[nameof(ControlsStrings.ChartContainerGraphTab)]"
2626
Icon="@(new Icons.Regular.Size24.DataArea())">
2727
<div class="metrics-chart-container metric-tab">
28-
<PlotlyChart InstrumentViewModel="_instrumentViewModel" Duration="Duration"/>
28+
<PlotlyChart InstrumentViewModel="_instrumentViewModel" Duration="Duration" Applications="Applications"/>
2929
<ChartFilters InstrumentViewModel="_instrumentViewModel" Instrument="_instrument" ViewModel="_viewModel"/>
3030
</div>
3131
</FluentTab>
@@ -34,7 +34,7 @@ else
3434
Label="@Loc[nameof(ControlsStrings.ChartContainerTableTab)]"
3535
Icon="@(new Icons.Regular.Size24.Table())">
3636
<div class="metric-tab">
37-
<MetricTable InstrumentViewModel="_instrumentViewModel" Duration="Duration"/>
37+
<MetricTable InstrumentViewModel="_instrumentViewModel" Duration="Duration" Applications="Applications" />
3838
<ChartFilters InstrumentViewModel="_instrumentViewModel" Instrument="_instrument" ViewModel="_viewModel"/>
3939
</div>
4040
</FluentTab>
@@ -48,4 +48,7 @@ else
4848

4949
[Parameter, EditorRequired]
5050
public required Func<Metrics.MetricViewKind, Task> OnViewChangedAsync { get; set; }
51+
52+
[Parameter]
53+
public required List<OtlpApplication> Applications { get; set; }
5154
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics;
5+
using Aspire.Dashboard.Otlp.Model;
6+
7+
namespace Aspire.Dashboard.Components.Controls.Chart;
8+
9+
[DebuggerDisplay("Start = {Start}, Value = {Value}, TraceId = {TraceId}, SpanId = {SpanId}")]
10+
public class ChartExemplar
11+
{
12+
public required DateTimeOffset Start { get; init; }
13+
public required double Value { get; init; }
14+
public required string TraceId { get; init; }
15+
public required string SpanId { get; init; }
16+
public required OtlpSpan? Span { get; init; }
17+
}

src/Aspire.Dashboard/Components/Controls/Chart/ChartTrace.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
namespace Aspire.Dashboard.Components.Controls.Chart;

0 commit comments

Comments
 (0)