Skip to content

Commit 6a148ec

Browse files
CopilotJamesNKilonatommy
authored
Don't add "unknown" to Blazor metrics tags when values are null (#63351)
* Initial plan * Implement conditional tag addition to remove "unknown" values from Blazor * Update src/Components/Components/src/ComponentsMetrics.cs * Improve readability by extracting exception type name to local variable --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: JamesNK <[email protected]> Co-authored-by: James Newton-King <[email protected]> Co-authored-by: ilonatommy <[email protected]>
1 parent 85846b0 commit 6a148ec

File tree

2 files changed

+196
-34
lines changed

2 files changed

+196
-34
lines changed

src/Components/Components/src/ComponentsMetrics.cs

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -69,63 +69,56 @@ public ComponentsMetrics(IMeterFactory meterFactory)
6969

7070
public void Navigation(string componentType, string route)
7171
{
72-
var tags = new TagList
73-
{
74-
{ "aspnetcore.components.type", componentType ?? "unknown" },
75-
{ "aspnetcore.components.route", route ?? "unknown" },
76-
};
72+
var tags = new TagList();
73+
AddComponentTypeTag(ref tags, componentType);
74+
AddRouteTag(ref tags, route);
7775

7876
_navigationCount.Add(1, tags);
7977
}
8078

8179
public async Task CaptureEventDuration(Task task, long startTimestamp, string? componentType, string? methodName, string? attributeName)
8280
{
83-
var tags = new TagList
84-
{
85-
{ "aspnetcore.components.type", componentType ?? "unknown" },
86-
{ "code.function.name", methodName ?? "unknown" },
87-
{ "aspnetcore.components.attribute.name", attributeName ?? "unknown" }
88-
};
81+
var tags = new TagList();
82+
AddComponentTypeTag(ref tags, componentType);
83+
AddMethodNameTag(ref tags, methodName);
84+
AddAttributeNameTag(ref tags, attributeName);
8985

9086
try
9187
{
9288
await task;
9389
}
9490
catch (Exception ex)
9591
{
96-
tags.Add("error.type", ex.GetType().FullName ?? "unknown");
92+
AddErrorTag(ref tags, ex);
9793
}
9894
var duration = Stopwatch.GetElapsedTime(startTimestamp);
9995
_eventDuration.Record(duration.TotalSeconds, tags);
10096
}
10197

10298
public void FailEventSync(Exception ex, long startTimestamp, string? componentType, string? methodName, string? attributeName)
10399
{
104-
var tags = new TagList
105-
{
106-
{ "aspnetcore.components.type", componentType ?? "unknown" },
107-
{ "code.function.name", methodName ?? "unknown" },
108-
{ "aspnetcore.components.attribute.name", attributeName ?? "unknown" },
109-
{ "error.type", ex.GetType().FullName ?? "unknown" }
110-
};
100+
var tags = new TagList();
101+
AddComponentTypeTag(ref tags, componentType);
102+
AddMethodNameTag(ref tags, methodName);
103+
AddAttributeNameTag(ref tags, attributeName);
104+
AddErrorTag(ref tags, ex);
105+
111106
var duration = Stopwatch.GetElapsedTime(startTimestamp);
112107
_eventDuration.Record(duration.TotalSeconds, tags);
113108
}
114109

115110
public async Task CaptureParametersDuration(Task task, long startTimestamp, string? componentType)
116111
{
117-
var tags = new TagList
118-
{
119-
{ "aspnetcore.components.type", componentType ?? "unknown" },
120-
};
112+
var tags = new TagList();
113+
AddComponentTypeTag(ref tags, componentType);
121114

122115
try
123116
{
124117
await task;
125118
}
126119
catch(Exception ex)
127120
{
128-
tags.Add("error.type", ex.GetType().FullName ?? "unknown");
121+
AddErrorTag(ref tags, ex);
129122
}
130123
var duration = Stopwatch.GetElapsedTime(startTimestamp);
131124
_parametersDuration.Record(duration.TotalSeconds, tags);
@@ -134,11 +127,10 @@ public async Task CaptureParametersDuration(Task task, long startTimestamp, stri
134127
public void FailParametersSync(Exception ex, long startTimestamp, string? componentType)
135128
{
136129
var duration = Stopwatch.GetElapsedTime(startTimestamp);
137-
var tags = new TagList
138-
{
139-
{ "aspnetcore.components.type", componentType ?? "unknown" },
140-
{ "error.type", ex.GetType().FullName ?? "unknown" }
141-
};
130+
var tags = new TagList();
131+
AddComponentTypeTag(ref tags, componentType);
132+
AddErrorTag(ref tags, ex);
133+
142134
_parametersDuration.Record(duration.TotalSeconds, tags);
143135
}
144136

@@ -152,7 +144,7 @@ public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffL
152144
}
153145
catch (Exception ex)
154146
{
155-
tags.Add("error.type", ex.GetType().FullName ?? "unknown");
147+
AddErrorTag(ref tags, ex);
156148
}
157149
var duration = Stopwatch.GetElapsedTime(startTimestamp);
158150
_batchDuration.Record(duration.TotalSeconds, tags);
@@ -162,10 +154,9 @@ public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffL
162154
public void FailBatchSync(Exception ex, long startTimestamp)
163155
{
164156
var duration = Stopwatch.GetElapsedTime(startTimestamp);
165-
var tags = new TagList
166-
{
167-
{ "error.type", ex.GetType().FullName ?? "unknown" }
168-
};
157+
var tags = new TagList();
158+
AddErrorTag(ref tags, ex);
159+
169160
_batchDuration.Record(duration.TotalSeconds, tags);
170161
}
171162

@@ -174,4 +165,45 @@ public void Dispose()
174165
_meter.Dispose();
175166
_lifeCycleMeter.Dispose();
176167
}
168+
169+
private static void AddComponentTypeTag(ref TagList tags, string? componentType)
170+
{
171+
if (componentType != null)
172+
{
173+
tags.Add("aspnetcore.components.type", componentType);
174+
}
175+
}
176+
177+
private static void AddRouteTag(ref TagList tags, string? route)
178+
{
179+
if (route != null)
180+
{
181+
tags.Add("aspnetcore.components.route", route);
182+
}
183+
}
184+
185+
private static void AddMethodNameTag(ref TagList tags, string? methodName)
186+
{
187+
if (methodName != null)
188+
{
189+
tags.Add("code.function.name", methodName);
190+
}
191+
}
192+
193+
private static void AddAttributeNameTag(ref TagList tags, string? attributeName)
194+
{
195+
if (attributeName != null)
196+
{
197+
tags.Add("aspnetcore.components.attribute.name", attributeName);
198+
}
199+
}
200+
201+
private static void AddErrorTag(ref TagList tags, Exception? exception)
202+
{
203+
var errorType = exception?.GetType().FullName;
204+
if (errorType is not null)
205+
{
206+
tags.Add("error.type", errorType);
207+
}
208+
}
177209
}

src/Components/Components/test/ComponentsMetricsTest.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,4 +400,134 @@ public void Dispose_DisposesAllMeters()
400400
// Assert - MeterFactory.Create was called twice in constructor
401401
Assert.Equal(2, _meterFactory.Meters.Count);
402402
}
403+
404+
[Fact]
405+
public void Navigation_WithNullValues_OmitsTags()
406+
{
407+
// Arrange
408+
var componentsMetrics = new ComponentsMetrics(_meterFactory);
409+
using var navigationCounter = new MetricCollector<long>(_meterFactory,
410+
ComponentsMetrics.MeterName, "aspnetcore.components.navigate");
411+
412+
// Act
413+
componentsMetrics.Navigation(null, null);
414+
415+
// Assert
416+
var measurements = navigationCounter.GetMeasurementSnapshot();
417+
418+
Assert.Single(measurements);
419+
Assert.Equal(1, measurements[0].Value);
420+
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
421+
Assert.DoesNotContain("aspnetcore.components.route", measurements[0].Tags);
422+
}
423+
424+
[Fact]
425+
public async Task CaptureEventDuration_WithNullValues_OmitsTags()
426+
{
427+
// Arrange
428+
var componentsMetrics = new ComponentsMetrics(_meterFactory);
429+
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
430+
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
431+
432+
// Act
433+
var startTimestamp = Stopwatch.GetTimestamp();
434+
await componentsMetrics.CaptureEventDuration(Task.CompletedTask, startTimestamp, null, null, null);
435+
436+
// Assert
437+
var measurements = eventDurationHistogram.GetMeasurementSnapshot();
438+
439+
Assert.Single(measurements);
440+
Assert.True(measurements[0].Value >= 0);
441+
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
442+
Assert.DoesNotContain("code.function.name", measurements[0].Tags);
443+
Assert.DoesNotContain("aspnetcore.components.attribute.name", measurements[0].Tags);
444+
Assert.DoesNotContain("error.type", measurements[0].Tags);
445+
}
446+
447+
[Fact]
448+
public void FailEventSync_WithNullValues_OmitsTags()
449+
{
450+
// Arrange
451+
var componentsMetrics = new ComponentsMetrics(_meterFactory);
452+
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
453+
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
454+
var exception = new InvalidOperationException();
455+
456+
// Act
457+
var startTimestamp = Stopwatch.GetTimestamp();
458+
componentsMetrics.FailEventSync(exception, startTimestamp, null, null, null);
459+
460+
// Assert
461+
var measurements = eventDurationHistogram.GetMeasurementSnapshot();
462+
463+
Assert.Single(measurements);
464+
Assert.True(measurements[0].Value >= 0);
465+
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
466+
Assert.DoesNotContain("code.function.name", measurements[0].Tags);
467+
Assert.DoesNotContain("aspnetcore.components.attribute.name", measurements[0].Tags);
468+
Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags));
469+
}
470+
471+
[Fact]
472+
public async Task CaptureParametersDuration_WithNullValues_OmitsTags()
473+
{
474+
// Arrange
475+
var componentsMetrics = new ComponentsMetrics(_meterFactory);
476+
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
477+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
478+
479+
// Act
480+
var startTimestamp = Stopwatch.GetTimestamp();
481+
await componentsMetrics.CaptureParametersDuration(Task.CompletedTask, startTimestamp, null);
482+
483+
// Assert
484+
var measurements = parametersDurationHistogram.GetMeasurementSnapshot();
485+
486+
Assert.Single(measurements);
487+
Assert.True(measurements[0].Value >= 0);
488+
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
489+
Assert.DoesNotContain("error.type", measurements[0].Tags);
490+
}
491+
492+
[Fact]
493+
public void FailParametersSync_WithNullValues_OmitsTags()
494+
{
495+
// Arrange
496+
var componentsMetrics = new ComponentsMetrics(_meterFactory);
497+
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
498+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
499+
var exception = new InvalidOperationException();
500+
501+
// Act
502+
var startTimestamp = Stopwatch.GetTimestamp();
503+
componentsMetrics.FailParametersSync(exception, startTimestamp, null);
504+
505+
// Assert
506+
var measurements = parametersDurationHistogram.GetMeasurementSnapshot();
507+
508+
Assert.Single(measurements);
509+
Assert.True(measurements[0].Value >= 0);
510+
Assert.DoesNotContain("aspnetcore.components.type", measurements[0].Tags);
511+
Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags));
512+
}
513+
514+
[Fact]
515+
public async Task CaptureBatchDuration_WithoutException_OmitsErrorTag()
516+
{
517+
// Arrange
518+
var componentsMetrics = new ComponentsMetrics(_meterFactory);
519+
using var batchDurationHistogram = new MetricCollector<double>(_meterFactory,
520+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration");
521+
522+
// Act
523+
var startTimestamp = Stopwatch.GetTimestamp();
524+
await componentsMetrics.CaptureBatchDuration(Task.CompletedTask, startTimestamp, 25);
525+
526+
// Assert
527+
var measurements = batchDurationHistogram.GetMeasurementSnapshot();
528+
529+
Assert.Single(measurements);
530+
Assert.True(measurements[0].Value >= 0);
531+
Assert.DoesNotContain("error.type", measurements[0].Tags);
532+
}
403533
}

0 commit comments

Comments
 (0)