Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
99 changes: 65 additions & 34 deletions src/Components/Components/src/ComponentsMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,63 +69,56 @@ public ComponentsMetrics(IMeterFactory meterFactory)

public void Navigation(string componentType, string route)
{
var tags = new TagList
{
{ "aspnetcore.components.type", componentType ?? "unknown" },
{ "aspnetcore.components.route", route ?? "unknown" },
};
var tags = new TagList();
AddComponentTypeTag(ref tags, componentType);
AddRouteTag(ref tags, route);

_navigationCount.Add(1, tags);
}

public async Task CaptureEventDuration(Task task, long startTimestamp, string? componentType, string? methodName, string? attributeName)
{
var tags = new TagList
{
{ "aspnetcore.components.type", componentType ?? "unknown" },
{ "code.function.name", methodName ?? "unknown" },
{ "aspnetcore.components.attribute.name", attributeName ?? "unknown" }
};
var tags = new TagList();
AddComponentTypeTag(ref tags, componentType);
AddMethodNameTag(ref tags, methodName);
AddAttributeNameTag(ref tags, attributeName);

try
{
await task;
}
catch (Exception ex)
{
tags.Add("error.type", ex.GetType().FullName ?? "unknown");
AddErrorTag(ref tags, ex);
}
var duration = Stopwatch.GetElapsedTime(startTimestamp);
_eventDuration.Record(duration.TotalSeconds, tags);
}

public void FailEventSync(Exception ex, long startTimestamp, string? componentType, string? methodName, string? attributeName)
{
var tags = new TagList
{
{ "aspnetcore.components.type", componentType ?? "unknown" },
{ "code.function.name", methodName ?? "unknown" },
{ "aspnetcore.components.attribute.name", attributeName ?? "unknown" },
{ "error.type", ex.GetType().FullName ?? "unknown" }
};
var tags = new TagList();
AddComponentTypeTag(ref tags, componentType);
AddMethodNameTag(ref tags, methodName);
AddAttributeNameTag(ref tags, attributeName);
AddErrorTag(ref tags, ex);

var duration = Stopwatch.GetElapsedTime(startTimestamp);
_eventDuration.Record(duration.TotalSeconds, tags);
}

public async Task CaptureParametersDuration(Task task, long startTimestamp, string? componentType)
{
var tags = new TagList
{
{ "aspnetcore.components.type", componentType ?? "unknown" },
};
var tags = new TagList();
AddComponentTypeTag(ref tags, componentType);

try
{
await task;
}
catch(Exception ex)
{
tags.Add("error.type", ex.GetType().FullName ?? "unknown");
AddErrorTag(ref tags, ex);
}
var duration = Stopwatch.GetElapsedTime(startTimestamp);
_parametersDuration.Record(duration.TotalSeconds, tags);
Expand All @@ -134,11 +127,10 @@ public async Task CaptureParametersDuration(Task task, long startTimestamp, stri
public void FailParametersSync(Exception ex, long startTimestamp, string? componentType)
{
var duration = Stopwatch.GetElapsedTime(startTimestamp);
var tags = new TagList
{
{ "aspnetcore.components.type", componentType ?? "unknown" },
{ "error.type", ex.GetType().FullName ?? "unknown" }
};
var tags = new TagList();
AddComponentTypeTag(ref tags, componentType);
AddErrorTag(ref tags, ex);

_parametersDuration.Record(duration.TotalSeconds, tags);
}

Expand All @@ -152,7 +144,7 @@ public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffL
}
catch (Exception ex)
{
tags.Add("error.type", ex.GetType().FullName ?? "unknown");
AddErrorTag(ref tags, ex);
}
var duration = Stopwatch.GetElapsedTime(startTimestamp);
_batchDuration.Record(duration.TotalSeconds, tags);
Expand All @@ -162,10 +154,9 @@ public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffL
public void FailBatchSync(Exception ex, long startTimestamp)
{
var duration = Stopwatch.GetElapsedTime(startTimestamp);
var tags = new TagList
{
{ "error.type", ex.GetType().FullName ?? "unknown" }
};
var tags = new TagList();
AddErrorTag(ref tags, ex);

_batchDuration.Record(duration.TotalSeconds, tags);
}

Expand All @@ -174,4 +165,44 @@ public void Dispose()
_meter.Dispose();
_lifeCycleMeter.Dispose();
}

private static void AddComponentTypeTag(ref TagList tags, string? componentType)
{
if (componentType != null)
{
tags.Add("aspnetcore.components.type", componentType);
}
}

private static void AddRouteTag(ref TagList tags, string? route)
{
if (route != null)
{
tags.Add("aspnetcore.components.route", route);
}
}

private static void AddMethodNameTag(ref TagList tags, string? methodName)
{
if (methodName != null)
{
tags.Add("code.function.name", methodName);
}
}

private static void AddAttributeNameTag(ref TagList tags, string? attributeName)
{
if (attributeName != null)
{
tags.Add("aspnetcore.components.attribute.name", attributeName);
}
}

private static void AddErrorTag(ref TagList tags, Exception? exception)
{
if (exception?.GetType().FullName != null)
{
tags.Add("error.type", exception.GetType().FullName);
}
}
}
130 changes: 130 additions & 0 deletions src/Components/Components/test/ComponentsMetricsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,134 @@ public void Dispose_DisposesAllMeters()
// Assert - MeterFactory.Create was called twice in constructor
Assert.Equal(2, _meterFactory.Meters.Count);
}

[Fact]
public void Navigation_WithNullValues_OmitsTags()
{
// Arrange
var componentsMetrics = new ComponentsMetrics(_meterFactory);
using var navigationCounter = new MetricCollector<long>(_meterFactory,
ComponentsMetrics.MeterName, "aspnetcore.components.navigate");

// Act
componentsMetrics.Navigation(null, null);

// Assert
var measurements = navigationCounter.GetMeasurementSnapshot();

Assert.Single(measurements);
Assert.Equal(1, measurements[0].Value);
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
Assert.DoesNotContain("aspnetcore.components.route", measurements[0].Tags);
}

[Fact]
public async Task CaptureEventDuration_WithNullValues_OmitsTags()
{
// Arrange
var componentsMetrics = new ComponentsMetrics(_meterFactory);
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");

// Act
var startTimestamp = Stopwatch.GetTimestamp();
await componentsMetrics.CaptureEventDuration(Task.CompletedTask, startTimestamp, null, null, null);

// Assert
var measurements = eventDurationHistogram.GetMeasurementSnapshot();

Assert.Single(measurements);
Assert.True(measurements[0].Value >= 0);
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
Assert.DoesNotContain("code.function.name", measurements[0].Tags);
Assert.DoesNotContain("aspnetcore.components.attribute.name", measurements[0].Tags);
Assert.DoesNotContain("error.type", measurements[0].Tags);
}

[Fact]
public void FailEventSync_WithNullValues_OmitsTags()
{
// Arrange
var componentsMetrics = new ComponentsMetrics(_meterFactory);
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
var exception = new InvalidOperationException();

// Act
var startTimestamp = Stopwatch.GetTimestamp();
componentsMetrics.FailEventSync(exception, startTimestamp, null, null, null);

// Assert
var measurements = eventDurationHistogram.GetMeasurementSnapshot();

Assert.Single(measurements);
Assert.True(measurements[0].Value >= 0);
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
Assert.DoesNotContain("code.function.name", measurements[0].Tags);
Assert.DoesNotContain("aspnetcore.components.attribute.name", measurements[0].Tags);
Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags));
}

[Fact]
public async Task CaptureParametersDuration_WithNullValues_OmitsTags()
{
// Arrange
var componentsMetrics = new ComponentsMetrics(_meterFactory);
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");

// Act
var startTimestamp = Stopwatch.GetTimestamp();
await componentsMetrics.CaptureParametersDuration(Task.CompletedTask, startTimestamp, null);

// Assert
var measurements = parametersDurationHistogram.GetMeasurementSnapshot();

Assert.Single(measurements);
Assert.True(measurements[0].Value >= 0);
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
Assert.DoesNotContain("error.type", measurements[0].Tags);
}

[Fact]
public void FailParametersSync_WithNullValues_OmitsTags()
{
// Arrange
var componentsMetrics = new ComponentsMetrics(_meterFactory);
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
var exception = new InvalidOperationException();

// Act
var startTimestamp = Stopwatch.GetTimestamp();
componentsMetrics.FailParametersSync(exception, startTimestamp, null);

// Assert
var measurements = parametersDurationHistogram.GetMeasurementSnapshot();

Assert.Single(measurements);
Assert.True(measurements[0].Value >= 0);
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags));
}

[Fact]
public async Task CaptureBatchDuration_WithoutException_OmitsErrorTag()
{
// Arrange
var componentsMetrics = new ComponentsMetrics(_meterFactory);
using var batchDurationHistogram = new MetricCollector<double>(_meterFactory,
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration");

// Act
var startTimestamp = Stopwatch.GetTimestamp();
await componentsMetrics.CaptureBatchDuration(Task.CompletedTask, startTimestamp, 25);

// Assert
var measurements = batchDurationHistogram.GetMeasurementSnapshot();

Assert.Single(measurements);
Assert.True(measurements[0].Value >= 0);
Assert.DoesNotContain("error.type", measurements[0].Tags);
}
}
Loading