Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.InteropServices;
using Aspire.Dashboard.Extensions;
using Aspire.Dashboard.Model;
using Aspire.Dashboard.Model.Otlp;
Expand Down Expand Up @@ -314,14 +315,9 @@ private async Task OnShowPropertiesAsync(SpanWaterfallViewModel viewModel, strin

private SpanLinkViewModel CreateLinkViewModel(string traceId, string spanId, KeyValuePair<string, string>[] attributes, Dictionary<string, OtlpTrace> traceCache)
{
if (!traceCache.TryGetValue(traceId, out var trace))
{
trace = TelemetryRepository.GetTrace(traceId);
if (trace != null)
{
traceCache[traceId] = trace;
}
}
ref var trace = ref CollectionsMarshal.GetValueRefOrAddDefault(traceCache, traceId, out _);
// Adds to dictionary if not present.
trace ??= TelemetryRepository.GetTrace(traceId);

var linkSpan = trace?.Spans.FirstOrDefault(s => s.SpanId == spanId);

Expand Down
34 changes: 17 additions & 17 deletions src/Aspire.Dashboard/Otlp/Model/OtlpApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Aspire.Dashboard.Otlp.Storage;
using Google.Protobuf.Collections;
using OpenTelemetry.Proto.Common.V1;
Expand Down Expand Up @@ -52,21 +53,20 @@ public void AddMetrics(AddContext context, RepeatedField<ScopeMetrics> scopeMetr
try
{
var instrumentKey = new OtlpInstrumentKey(sm.Scope.Name, metric.Name);
if (!_instruments.TryGetValue(instrumentKey, out var instrument))
ref var instrument = ref CollectionsMarshal.GetValueRefOrAddDefault(_instruments, instrumentKey, out _);
// Adds to dictionary if not present.
instrument ??= new OtlpInstrument
{
_instruments.Add(instrumentKey, instrument = new OtlpInstrument
Summary = new OtlpInstrumentSummary
{
Summary = new OtlpInstrumentSummary
{
Name = metric.Name,
Description = metric.Description,
Unit = metric.Unit,
Type = MapMetricType(metric.DataCase),
Parent = GetMeter(sm.Scope)
},
Context = Context
});
}
Name = metric.Name,
Description = metric.Description,
Unit = metric.Unit,
Type = MapMetricType(metric.DataCase),
Parent = GetMeter(sm.Scope)
},
Context = Context
};

instrument.AddMetrics(metric, ref tempAttributes);
}
Expand Down Expand Up @@ -97,10 +97,10 @@ private static OtlpInstrumentType MapMetricType(Metric.DataOneofCase data)

private OtlpMeter GetMeter(InstrumentationScope scope)
{
if (!_meters.TryGetValue(scope.Name, out var meter))
{
_meters.Add(scope.Name, meter = new OtlpMeter(scope, Context));
}
ref var meter = ref CollectionsMarshal.GetValueRefOrAddDefault(_meters, scope.Name, out _);
// Adds to dictionary if not present.
meter ??= new OtlpMeter(scope, Context);

return meter;
}

Expand Down
15 changes: 10 additions & 5 deletions src/Aspire.Dashboard/Otlp/Model/OtlpInstrument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Aspire.Dashboard.Otlp.Model.MetricValues;
using Google.Protobuf.Collections;
using OpenTelemetry.Proto.Common.V1;
Expand Down Expand Up @@ -74,26 +75,30 @@ private DimensionScope FindScope(RepeatedField<KeyValue> attributes, ref KeyValu

var comparableAttributes = tempAttributes.AsMemory(0, copyCount);

// Can't use CollectionsMarshal.GetValueRefOrAddDefault here because comparableAttributes is a view over mutable data.
// Need to add dimensions using durable attributes instance after scope is created.
if (!Dimensions.TryGetValue(comparableAttributes, out var dimension))
{
dimension = AddDimensionScope(comparableAttributes);
dimension = CreateDimensionScope(comparableAttributes);
Dimensions.Add(dimension.Attributes, dimension);
}
return dimension;
}

private DimensionScope AddDimensionScope(Memory<KeyValuePair<string, string>> comparableAttributes)
private DimensionScope CreateDimensionScope(Memory<KeyValuePair<string, string>> comparableAttributes)
{
var isFirst = Dimensions.Count == 0;
var durableAttributes = comparableAttributes.ToArray();
var dimension = new DimensionScope(Context.Options.MaxMetricsCount, durableAttributes);
Dimensions.Add(durableAttributes, dimension);

var keys = KnownAttributeValues.Keys.Union(durableAttributes.Select(a => a.Key)).Distinct();
foreach (var key in keys)
{
if (!KnownAttributeValues.TryGetValue(key, out var values))
ref var values = ref CollectionsMarshal.GetValueRefOrAddDefault(KnownAttributeValues, key, out _);
// Adds to dictionary if not present.
if (values == null)
{
KnownAttributeValues.Add(key, values = new List<string?>());
values = new List<string?>();

// If the key is new and there are already dimensions, add an empty value because there are dimensions without this key.
if (!isFirst)
Expand Down
76 changes: 35 additions & 41 deletions src/Aspire.Dashboard/Otlp/Storage/TelemetryRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Aspire.Dashboard.Otlp.Model.MetricValues;
using Google.Protobuf.Collections;
using Microsoft.Extensions.Options;
using OpenTelemetry.Proto.Common.V1;
using OpenTelemetry.Proto.Logs.V1;
using OpenTelemetry.Proto.Metrics.V1;
using OpenTelemetry.Proto.Resource.V1;
Expand Down Expand Up @@ -285,6 +286,28 @@ public void AddLogs(AddContext context, RepeatedField<ResourceLogs> resourceLogs
RaiseSubscriptionChanged(_logSubscriptions);
}

private bool TryAddScope(Dictionary<string, OtlpScope> scopes, InstrumentationScope? scope, [NotNullWhen(true)] out OtlpScope? s)
{
try
{
// The instrumentation scope information for the spans in this message.
// Semantically when InstrumentationScope isn't set, it is equivalent with
// an empty instrumentation scope name (unknown).
var name = scope?.Name ?? string.Empty;
ref var scopeRef = ref CollectionsMarshal.GetValueRefOrAddDefault(scopes, name, out _);
// Adds to dictionary if not present.
scopeRef ??= (scope != null) ? new OtlpScope(scope, _otlpContext) : OtlpScope.Empty;
s = scopeRef;
return true;
}
catch (Exception ex)
{
_otlpContext.Logger.LogInformation(ex, "Error adding scope.");
s = null;
return false;
}
}

public void AddLogsCore(AddContext context, OtlpApplicationView applicationView, RepeatedField<ScopeLogs> scopeLogs)
{
_logsLock.EnterWriteLock();
Expand All @@ -293,23 +316,9 @@ public void AddLogsCore(AddContext context, OtlpApplicationView applicationView,
{
foreach (var sl in scopeLogs)
{
OtlpScope? scope;
try
{
// The instrumentation scope information for the spans in this message.
// Semantically when InstrumentationScope isn't set, it is equivalent with
// an empty instrumentation scope name (unknown).
var name = sl.Scope?.Name ?? string.Empty;
if (!_logScopes.TryGetValue(name, out scope))
{
scope = (sl.Scope != null) ? new OtlpScope(sl.Scope, _otlpContext) : OtlpScope.Empty;
_logScopes.Add(name, scope);
}
}
catch (Exception ex)
if (!TryAddScope(_logScopes, sl.Scope, out var scope))
{
context.FailureCount += sl.LogRecords.Count;
_otlpContext.Logger.LogInformation(ex, "Error adding scope.");
continue;
}

Expand Down Expand Up @@ -343,14 +352,9 @@ public void AddLogsCore(AddContext context, OtlpApplicationView applicationView,
{
if (!_logSubscriptions.Any(s => s.SubscriptionType == SubscriptionType.Read && (s.ApplicationKey == applicationView.ApplicationKey || s.ApplicationKey == null)))
{
if (_applicationUnviewedErrorLogs.TryGetValue(applicationView.ApplicationKey, out var count))
{
_applicationUnviewedErrorLogs[applicationView.ApplicationKey] = ++count;
}
else
{
_applicationUnviewedErrorLogs.Add(applicationView.ApplicationKey, 1);
}
ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(_applicationUnviewedErrorLogs, applicationView.ApplicationKey, out _);
// Adds to dictionary if not present.
count++;
}
}

Expand Down Expand Up @@ -577,6 +581,7 @@ public Dictionary<string, int> GetTraceFieldValues(string attributeName)
if (value != null)
{
ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(attributesValues, value, out _);
// Adds to dictionary if not present.
count++;
}
}
Expand Down Expand Up @@ -604,6 +609,7 @@ public Dictionary<string, int> GetLogsFieldValues(string attributeName)
if (value != null)
{
ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(attributesValues, value, out _);
// Adds to dictionary if not present.
count++;
}
}
Expand Down Expand Up @@ -770,23 +776,9 @@ internal void AddTracesCore(AddContext context, OtlpApplicationView applicationV
{
foreach (var scopeSpan in scopeSpans)
{
OtlpScope? scope;
try
{
// The instrumentation scope information for the spans in this message.
// Semantically when InstrumentationScope isn't set, it is equivalent with
// an empty instrumentation scope name (unknown).
var name = scopeSpan.Scope?.Name ?? string.Empty;
if (!_traceScopes.TryGetValue(name, out scope))
{
scope = (scopeSpan.Scope != null) ? new OtlpScope(scopeSpan.Scope, _otlpContext) : OtlpScope.Empty;
_traceScopes.Add(name, scope);
}
}
catch (Exception ex)
if (!TryAddScope(_traceScopes, scopeSpan.Scope, out var scope))
{
context.FailureCount += scopeSpan.Spans.Count;
_otlpContext.Logger.LogInformation(ex, "Error adding scope.");
continue;
}

Expand Down Expand Up @@ -1095,13 +1087,15 @@ public List<OtlpInstrumentSummary> GetInstrumentsSummaries(ApplicationKey key)

foreach (var knownAttributeValues in instrument.KnownAttributeValues)
{
if (allKnownAttributes.TryGetValue(knownAttributeValues.Key, out var values))
ref var values = ref CollectionsMarshal.GetValueRefOrAddDefault(allKnownAttributes, knownAttributeValues.Key, out _);
// Adds to dictionary if not present.
if (values != null)
{
allKnownAttributes[knownAttributeValues.Key] = values.Union(knownAttributeValues.Value).ToList();
values = values.Union(knownAttributeValues.Value).ToList();
}
else
{
allKnownAttributes[knownAttributeValues.Key] = knownAttributeValues.Value.ToList();
values = knownAttributeValues.Value.ToList();
}
}
}
Expand Down