Skip to content

Commit b2101a8

Browse files
pavelsavaraJamesNK
andauthored
[blazor] Diagnostic metrics - OTEL names review (#62754)
Co-authored-by: James Newton-King <[email protected]>
1 parent 0ad6bfc commit b2101a8

File tree

3 files changed

+41
-51
lines changed

3 files changed

+41
-51
lines changed

src/Components/Components/src/ComponentsMetrics.cs

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal sealed class ComponentsMetrics : IDisposable
2020
private readonly Histogram<double> _eventDuration;
2121
private readonly Histogram<double> _parametersDuration;
2222
private readonly Histogram<double> _batchDuration;
23+
private readonly Histogram<int> _batchSize;
2324

2425
public bool IsNavigationEnabled => _navigationCount.Enabled;
2526

@@ -37,27 +38,33 @@ public ComponentsMetrics(IMeterFactory meterFactory)
3738
_lifeCycleMeter = meterFactory.Create(LifecycleMeterName);
3839

3940
_navigationCount = _meter.CreateCounter<long>(
40-
"aspnetcore.components.navigation",
41+
"aspnetcore.components.navigate",
4142
unit: "{route}",
4243
description: "Total number of route changes.");
4344

4445
_eventDuration = _meter.CreateHistogram(
45-
"aspnetcore.components.event_handler",
46+
"aspnetcore.components.handle_event.duration",
4647
unit: "s",
4748
description: "Duration of processing browser event. It includes business logic of the component but not affected child components.",
4849
advice: new InstrumentAdvice<double> { HistogramBucketBoundaries = MetricsConstants.ShortSecondsBucketBoundaries });
4950

5051
_parametersDuration = _lifeCycleMeter.CreateHistogram(
51-
"aspnetcore.components.update_parameters",
52+
"aspnetcore.components.update_parameters.duration",
5253
unit: "s",
5354
description: "Duration of processing component parameters. It includes business logic of the component.",
5455
advice: new InstrumentAdvice<double> { HistogramBucketBoundaries = MetricsConstants.BlazorRenderingSecondsBucketBoundaries });
5556

5657
_batchDuration = _lifeCycleMeter.CreateHistogram(
57-
"aspnetcore.components.render_diff",
58+
"aspnetcore.components.render_diff.duration",
5859
unit: "s",
5960
description: "Duration of rendering component tree and producing HTML diff. It includes business logic of the changed components.",
6061
advice: new InstrumentAdvice<double> { HistogramBucketBoundaries = MetricsConstants.BlazorRenderingSecondsBucketBoundaries });
62+
63+
_batchSize = _lifeCycleMeter.CreateHistogram(
64+
"aspnetcore.components.render_diff.size",
65+
unit: "{elements}",
66+
description: "Number of HTML elements modified during a rendering batch.",
67+
advice: new InstrumentAdvice<int> { HistogramBucketBoundaries = MetricsConstants.BlazorRenderingDiffLengthBucketBoundaries });
6168
}
6269

6370
public void Navigation(string componentType, string route)
@@ -137,10 +144,7 @@ public void FailParametersSync(Exception ex, long startTimestamp, string? compon
137144

138145
public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffLength)
139146
{
140-
var tags = new TagList
141-
{
142-
{ "aspnetcore.components.diff.length", BucketDiffLength(diffLength) }
143-
};
147+
var tags = new TagList();
144148

145149
try
146150
{
@@ -152,37 +156,19 @@ public async Task CaptureBatchDuration(Task task, long startTimestamp, int diffL
152156
}
153157
var duration = Stopwatch.GetElapsedTime(startTimestamp);
154158
_batchDuration.Record(duration.TotalSeconds, tags);
159+
_batchSize.Record(diffLength, tags);
155160
}
156161

157162
public void FailBatchSync(Exception ex, long startTimestamp)
158163
{
159164
var duration = Stopwatch.GetElapsedTime(startTimestamp);
160165
var tags = new TagList
161166
{
162-
{ "aspnetcore.components.diff.length", 0 },
163167
{ "error.type", ex.GetType().FullName ?? "unknown" }
164168
};
165169
_batchDuration.Record(duration.TotalSeconds, tags);
166170
}
167171

168-
private static int BucketDiffLength(int diffLength)
169-
{
170-
return diffLength switch
171-
{
172-
<= 1 => 1,
173-
<= 2 => 2,
174-
<= 5 => 5,
175-
<= 10 => 10,
176-
<= 20 => 20,
177-
<= 50 => 50,
178-
<= 100 => 100,
179-
<= 500 => 500,
180-
<= 1000 => 1000,
181-
<= 10000 => 10000,
182-
_ => 10001,
183-
};
184-
}
185-
186172
public void Dispose()
187173
{
188174
_meter.Dispose();

src/Components/Components/test/ComponentsMetricsTest.cs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public void Navigation_RecordsMetric()
3939
// Arrange
4040
var componentsMetrics = new ComponentsMetrics(_meterFactory);
4141
using var navigationCounter = new MetricCollector<long>(_meterFactory,
42-
ComponentsMetrics.MeterName, "aspnetcore.components.navigation");
42+
ComponentsMetrics.MeterName, "aspnetcore.components.navigate");
4343

4444
// Act
4545
componentsMetrics.Navigation("TestComponent", "/test-route");
@@ -61,7 +61,7 @@ public void IsNavigationEnabled_ReturnsCorrectState()
6161

6262
// Create a collector to ensure the meter is enabled
6363
using var navigationCounter = new MetricCollector<long>(_meterFactory,
64-
ComponentsMetrics.MeterName, "aspnetcore.components.navigation");
64+
ComponentsMetrics.MeterName, "aspnetcore.components.navigate");
6565

6666
// Act & Assert
6767
Assert.True(componentsMetrics.IsNavigationEnabled);
@@ -73,7 +73,7 @@ public async Task CaptureEventDuration_RecordsSuccessMetric()
7373
// Arrange
7474
var componentsMetrics = new ComponentsMetrics(_meterFactory);
7575
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
76-
ComponentsMetrics.MeterName, "aspnetcore.components.event_handler");
76+
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
7777

7878
// Act
7979
var startTimestamp = Stopwatch.GetTimestamp();
@@ -98,7 +98,7 @@ public async Task CaptureEventDuration_RecordsErrorMetric()
9898
// Arrange
9999
var componentsMetrics = new ComponentsMetrics(_meterFactory);
100100
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
101-
ComponentsMetrics.MeterName, "aspnetcore.components.event_handler");
101+
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
102102

103103
// Act
104104
var startTimestamp = Stopwatch.GetTimestamp();
@@ -123,7 +123,7 @@ public void FailEventSync_RecordsErrorMetric()
123123
// Arrange
124124
var componentsMetrics = new ComponentsMetrics(_meterFactory);
125125
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
126-
ComponentsMetrics.MeterName, "aspnetcore.components.event_handler");
126+
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
127127
var exception = new InvalidOperationException();
128128

129129
// Act
@@ -150,7 +150,7 @@ public void IsEventEnabled_ReturnsCorrectState()
150150

151151
// Create a collector to ensure the meter is enabled
152152
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
153-
ComponentsMetrics.MeterName, "aspnetcore.components.event_handler");
153+
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
154154

155155
// Act & Assert
156156
Assert.True(componentsMetrics.IsEventEnabled);
@@ -162,7 +162,7 @@ public async Task CaptureParametersDuration_RecordsSuccessMetric()
162162
// Arrange
163163
var componentsMetrics = new ComponentsMetrics(_meterFactory);
164164
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
165-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters");
165+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
166166

167167
// Act
168168
var startTimestamp = Stopwatch.GetTimestamp();
@@ -184,7 +184,7 @@ public async Task CaptureParametersDuration_RecordsErrorMetric()
184184
// Arrange
185185
var componentsMetrics = new ComponentsMetrics(_meterFactory);
186186
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
187-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters");
187+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
188188

189189
// Act
190190
var startTimestamp = Stopwatch.GetTimestamp();
@@ -207,7 +207,7 @@ public void FailParametersSync_RecordsErrorMetric()
207207
// Arrange
208208
var componentsMetrics = new ComponentsMetrics(_meterFactory);
209209
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
210-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters");
210+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
211211
var exception = new InvalidOperationException();
212212

213213
// Act
@@ -231,7 +231,7 @@ public void IsParametersEnabled_ReturnsCorrectState()
231231

232232
// Create a collector to ensure the meter is enabled
233233
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
234-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters");
234+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
235235

236236
// Act & Assert
237237
Assert.True(componentsMetrics.IsParametersEnabled);
@@ -243,7 +243,7 @@ public async Task CaptureBatchDuration_RecordsSuccessMetric()
243243
// Arrange
244244
var componentsMetrics = new ComponentsMetrics(_meterFactory);
245245
using var batchDurationHistogram = new MetricCollector<double>(_meterFactory,
246-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff");
246+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration");
247247

248248
// Act
249249
var startTimestamp = Stopwatch.GetTimestamp();
@@ -255,7 +255,6 @@ public async Task CaptureBatchDuration_RecordsSuccessMetric()
255255

256256
Assert.Single(measurements);
257257
Assert.True(measurements[0].Value > 0);
258-
Assert.Equal(50, Assert.Contains("aspnetcore.components.diff.length", measurements[0].Tags));
259258
Assert.DoesNotContain("error.type", measurements[0].Tags);
260259
}
261260

@@ -265,7 +264,7 @@ public async Task CaptureBatchDuration_RecordsErrorMetric()
265264
// Arrange
266265
var componentsMetrics = new ComponentsMetrics(_meterFactory);
267266
using var batchDurationHistogram = new MetricCollector<double>(_meterFactory,
268-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff");
267+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration");
269268

270269
// Act
271270
var startTimestamp = Stopwatch.GetTimestamp();
@@ -278,7 +277,6 @@ await componentsMetrics.CaptureBatchDuration(Task.FromException(new InvalidOpera
278277

279278
Assert.Single(measurements);
280279
Assert.True(measurements[0].Value > 0);
281-
Assert.Equal(50, Assert.Contains("aspnetcore.components.diff.length", measurements[0].Tags));
282280
Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags));
283281
}
284282

@@ -288,7 +286,7 @@ public void FailBatchSync_RecordsErrorMetric()
288286
// Arrange
289287
var componentsMetrics = new ComponentsMetrics(_meterFactory);
290288
using var batchDurationHistogram = new MetricCollector<double>(_meterFactory,
291-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff");
289+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration");
292290
var exception = new InvalidOperationException();
293291

294292
// Act
@@ -300,7 +298,6 @@ public void FailBatchSync_RecordsErrorMetric()
300298

301299
Assert.Single(measurements);
302300
Assert.True(measurements[0].Value > 0);
303-
Assert.Equal(0, Assert.Contains("aspnetcore.components.diff.length", measurements[0].Tags));
304301
Assert.Equal("System.InvalidOperationException", Assert.Contains("error.type", measurements[0].Tags));
305302
}
306303

@@ -312,7 +309,7 @@ public void IsBatchEnabled_ReturnsCorrectState()
312309

313310
// Create a collector to ensure the meter is enabled
314311
using var batchDurationHistogram = new MetricCollector<double>(_meterFactory,
315-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff");
312+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration");
316313

317314
// Act & Assert
318315
Assert.True(componentsMetrics.IsBatchEnabled);
@@ -324,13 +321,15 @@ public async Task ComponentLifecycle_RecordsAllMetricsCorrectly()
324321
// Arrange
325322
var componentsMetrics = new ComponentsMetrics(_meterFactory);
326323
using var navigationCounter = new MetricCollector<long>(_meterFactory,
327-
ComponentsMetrics.MeterName, "aspnetcore.components.navigation");
324+
ComponentsMetrics.MeterName, "aspnetcore.components.navigate");
328325
using var eventDurationHistogram = new MetricCollector<double>(_meterFactory,
329-
ComponentsMetrics.MeterName, "aspnetcore.components.event_handler");
326+
ComponentsMetrics.MeterName, "aspnetcore.components.handle_event.duration");
330327
using var parametersDurationHistogram = new MetricCollector<double>(_meterFactory,
331-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters");
328+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.update_parameters.duration");
332329
using var batchDurationHistogram = new MetricCollector<double>(_meterFactory,
333-
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff");
330+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.duration");
331+
using var batchSizeHistogram = new MetricCollector<int>(_meterFactory,
332+
ComponentsMetrics.LifecycleMeterName, "aspnetcore.components.render_diff.size");
334333

335334
// Act - Simulate a component lifecycle
336335
// 1. Navigation
@@ -357,11 +356,13 @@ await componentsMetrics.CaptureEventDuration(Task.CompletedTask, startTimestamp2
357356
var eventMeasurements = eventDurationHistogram.GetMeasurementSnapshot();
358357
var parametersMeasurements = parametersDurationHistogram.GetMeasurementSnapshot();
359358
var batchMeasurements = batchDurationHistogram.GetMeasurementSnapshot();
359+
var batchSizeMeasurements = batchSizeHistogram.GetMeasurementSnapshot();
360360

361361
Assert.Single(navigationMeasurements);
362362
Assert.Single(eventMeasurements);
363363
Assert.Single(parametersMeasurements);
364364
Assert.Single(batchMeasurements);
365+
Assert.Single(batchSizeMeasurements);
365366

366367
// Check navigation
367368
Assert.Equal(1, navigationMeasurements[0].Value);
@@ -380,7 +381,7 @@ await componentsMetrics.CaptureEventDuration(Task.CompletedTask, startTimestamp2
380381

381382
// Check batch duration
382383
Assert.True(batchMeasurements[0].Value > 0);
383-
Assert.Equal(20, Assert.Contains("aspnetcore.components.diff.length", batchMeasurements[0].Tags));
384+
Assert.True(batchSizeMeasurements[0].Value > 0);
384385
}
385386

386387
[Fact]

src/Shared/Metrics/MetricsConstants.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ internal static class MetricsConstants
1111
// Not based on a standard. Larger bucket sizes for longer lasting operations, e.g. HTTP connection duration. See https://github.com/open-telemetry/semantic-conventions/issues/336
1212
public static readonly IReadOnlyList<double> LongSecondsBucketBoundaries = [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 30, 60, 120, 300];
1313

14-
// For blazor rendering, which should be very fast.
14+
// For Blazor rendering, which should be very fast.
1515
public static readonly IReadOnlyList<double> BlazorRenderingSecondsBucketBoundaries = [0.000001, 0.00001, 0.0001, 0.001, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10];
1616

17-
// For blazor circuit sessions, which can last a long time.
17+
// For measuring the length of HTML diff in Blazor rendering.
18+
public static readonly IReadOnlyList<int> BlazorRenderingDiffLengthBucketBoundaries = [0, 1, 2, 4, 6, 8, 10, 20, 30, 40, 50, 100];
19+
20+
// For Blazor circuit sessions, which can last a long time.
1821
public static readonly IReadOnlyList<double> BlazorCircuitSecondsBucketBoundaries = [1, 3, 10, 30, 1 * 60, 3 * 60, 10 * 60, 30 * 60, 1 * 60 * 60, 3 * 60 * 60, 10 * 60 * 60, 24 * 60 * 60];
1922
}

0 commit comments

Comments
 (0)