Skip to content

Commit 2557f08

Browse files
committed
- remove FeatureSwitchDefinition for now
- add tracing
1 parent 0bcd459 commit 2557f08

File tree

8 files changed

+134
-53
lines changed

8 files changed

+134
-53
lines changed

src/Components/Components/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ Microsoft.AspNetCore.Components.SupplyParameterFromPersistentComponentStateAttri
1515
Microsoft.Extensions.DependencyInjection.SupplyParameterFromPersistentComponentStateProviderServiceCollectionExtensions
1616
static Microsoft.AspNetCore.Components.Infrastructure.RegisterPersistentComponentStateServiceCollectionExtensions.AddPersistentServiceRegistration<TService>(Microsoft.Extensions.DependencyInjection.IServiceCollection! services, Microsoft.AspNetCore.Components.IComponentRenderMode! componentRenderMode) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
1717
static Microsoft.AspNetCore.Components.Infrastructure.RenderingMetricsServiceCollectionExtensions.AddRenderingMetrics(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
18+
static Microsoft.AspNetCore.Components.Infrastructure.RenderingMetricsServiceCollectionExtensions.AddRenderingTracing(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
1819
static Microsoft.Extensions.DependencyInjection.SupplyParameterFromPersistentComponentStateProviderServiceCollectionExtensions.AddSupplyValueFromPersistentComponentStateProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
1920
virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.SignalRendererToFinishRendering() -> void

src/Components/Components/src/RegisterRenderingMetricsServiceCollectionExtensions.cs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,25 @@ public static class RenderingMetricsServiceCollectionExtensions
2121
public static IServiceCollection AddRenderingMetrics(
2222
IServiceCollection services)
2323
{
24-
if (RenderingMetrics.IsMetricsSupported)
24+
// do not register IConfigureOptions<StartupValidatorOptions> multiple times
25+
if (!IsMeterFactoryRegistered(services))
2526
{
26-
// do not register IConfigureOptions<StartupValidatorOptions> multiple times
27-
if (!IsMeterFactoryRegistered(services))
28-
{
29-
services.AddMetrics();
30-
}
31-
services.TryAddSingleton<RenderingMetrics>();
27+
services.AddMetrics();
3228
}
29+
services.TryAddSingleton<RenderingMetrics>();
30+
31+
return services;
32+
}
33+
34+
/// <summary>
35+
/// Registers component rendering traces
36+
/// </summary>
37+
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
38+
/// <returns>The <see cref="IServiceCollection"/>.</returns>
39+
public static IServiceCollection AddRenderingTracing(
40+
IServiceCollection services)
41+
{
42+
services.TryAddSingleton<RenderingActivitySource>();
3343

3444
return services;
3545
}

src/Components/Components/src/RenderTree/Renderer.cs

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public abstract partial class Renderer : IDisposable, IAsyncDisposable
3636
private readonly ILogger _logger;
3737
private readonly ComponentFactory _componentFactory;
3838
private readonly RenderingMetrics? _renderingMetrics;
39+
private readonly RenderingActivitySource? _renderingActivitySource;
3940
private Dictionary<int, ParameterView>? _rootComponentsLatestParameters;
4041
private Task? _ongoingQuiescenceTask;
4142

@@ -93,17 +94,16 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory,
9394
// logger name in here as a string literal.
9495
_logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Components.RenderTree.Renderer");
9596
_componentFactory = new ComponentFactory(componentActivator, this);
96-
if (RenderingMetrics.IsMetricsSupported)
97-
{
98-
_renderingMetrics = serviceProvider.GetService<RenderingMetrics>();
99-
}
97+
_renderingMetrics = serviceProvider.GetService<RenderingMetrics>();
98+
_renderingActivitySource = serviceProvider.GetService<RenderingActivitySource>();
10099

101100
ServiceProviderCascadingValueSuppliers = serviceProvider.GetService<ICascadingValueSupplier>() is null
102101
? Array.Empty<ICascadingValueSupplier>()
103102
: serviceProvider.GetServices<ICascadingValueSupplier>().ToArray();
104103
}
105104

106-
internal RenderingMetrics? RenderingMetrics => RenderingMetrics.IsMetricsSupported ? _renderingMetrics : null;
105+
internal RenderingMetrics? RenderingMetrics => _renderingMetrics;
106+
internal RenderingActivitySource? RenderingActivitySource => _renderingActivitySource;
107107

108108
internal ICascadingValueSupplier[] ServiceProviderCascadingValueSuppliers { get; }
109109

@@ -440,15 +440,24 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
440440
{
441441
Dispatcher.AssertAccess();
442442

443-
var eventStartTimestamp = RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsEventDurationEnabled ? Stopwatch.GetTimestamp() : 0;
444-
445443
if (waitForQuiescence)
446444
{
447445
_pendingTasks ??= new();
448446
}
449447

450448
var (renderedByComponentId, callback, attributeName) = GetRequiredEventBindingEntry(eventHandlerId);
451449

450+
// collect trace
451+
Activity? activity = null;
452+
if (RenderingActivitySource != null)
453+
{
454+
var receiverName = (callback.Receiver?.GetType() ?? callback.Delegate.Target?.GetType())?.FullName;
455+
var methodName = callback.Delegate.Method?.Name;
456+
activity = RenderingActivitySource.StartEventActivity(receiverName, methodName, attributeName, null);
457+
}
458+
459+
var eventStartTimestamp = RenderingMetrics != null && RenderingMetrics.IsEventDurationEnabled ? Stopwatch.GetTimestamp() : 0;
460+
452461
// If this event attribute was rendered by a component that's since been disposed, don't dispatch the event at all.
453462
// This can occur because event handler disposal is deferred, so event handler IDs can outlive their components.
454463
// The reason the following check is based on "which component rendered this frame" and not on "which component
@@ -491,23 +500,36 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
491500
task = callback.InvokeAsync(eventArgs);
492501

493502
// collect metrics
494-
if (RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsEventDurationEnabled)
503+
if (RenderingMetrics != null && RenderingMetrics.IsEventDurationEnabled)
495504
{
496505
var receiverName = (callback.Receiver?.GetType() ?? callback.Delegate.Target?.GetType())?.FullName;
497-
RenderingMetrics.EventDurationSync(eventStartTimestamp, receiverName, attributeName);
498-
_ = RenderingMetrics.CaptureEventDurationAsync(task, eventStartTimestamp, receiverName, attributeName);
506+
var methodName = callback.Delegate.Method?.Name;
507+
RenderingMetrics.EventDurationSync(eventStartTimestamp, receiverName, methodName, attributeName);
508+
_ = RenderingMetrics.CaptureEventDurationAsync(task, eventStartTimestamp, receiverName, methodName, attributeName);
499509
}
500-
if (RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsEventExceptionEnabled)
510+
if (RenderingMetrics != null && RenderingMetrics.IsEventExceptionEnabled)
501511
{
502512
_ = RenderingMetrics.CaptureEventFailedAsync(task, callback, attributeName);
503513
}
514+
515+
// stop activity/trace
516+
if (RenderingActivitySource != null && activity != null)
517+
{
518+
_ = RenderingActivitySource.CaptureEventStopAsync(task, activity);
519+
}
504520
}
505521
catch (Exception e)
506522
{
507-
if (RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsEventExceptionEnabled)
523+
if (RenderingMetrics != null && RenderingMetrics.IsEventExceptionEnabled)
508524
{
509525
RenderingMetrics.EventFailed(e.GetType().FullName, callback, attributeName);
510526
}
527+
528+
if (RenderingActivitySource != null && activity != null)
529+
{
530+
RenderingActivitySource.FailEventActivity(activity, e);
531+
}
532+
511533
HandleExceptionViaErrorBoundary(e, receiverComponentState);
512534
return Task.CompletedTask;
513535
}
@@ -795,7 +817,7 @@ private void ProcessRenderQueue()
795817

796818
_isBatchInProgress = true;
797819
var updateDisplayTask = Task.CompletedTask;
798-
var batchStartTimestamp = RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsBatchDurationEnabled ? Stopwatch.GetTimestamp() : 0;
820+
var batchStartTimestamp = RenderingMetrics != null && RenderingMetrics.IsBatchDurationEnabled ? Stopwatch.GetTimestamp() : 0;
799821

800822
try
801823
{
@@ -828,18 +850,18 @@ private void ProcessRenderQueue()
828850
// if there is async work to be done.
829851
_ = InvokeRenderCompletedCalls(batch.UpdatedComponents, updateDisplayTask);
830852

831-
if (RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsBatchDurationEnabled)
853+
if (RenderingMetrics != null && RenderingMetrics.IsBatchDurationEnabled)
832854
{
833855
_renderingMetrics.BatchDuration(batchStartTimestamp, batch.UpdatedComponents.Count);
834856
}
835-
if (RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsBatchExceptionEnabled)
857+
if (RenderingMetrics != null && RenderingMetrics.IsBatchExceptionEnabled)
836858
{
837859
_ = _renderingMetrics.CaptureBatchFailedAsync(updateDisplayTask);
838860
}
839861
}
840862
catch (Exception e)
841863
{
842-
if (RenderingMetrics.IsMetricsSupported && RenderingMetrics != null && RenderingMetrics.IsBatchExceptionEnabled)
864+
if (RenderingMetrics != null && RenderingMetrics.IsBatchExceptionEnabled)
843865
{
844866
_renderingMetrics.BatchFailed(e.GetType().Name);
845867
}

src/Components/Components/src/Rendering/ComponentState.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public ComponentState(Renderer renderer, int componentId, IComponent component,
5353
_hasAnyCascadingParameterSubscriptions = AddCascadingParameterSubscriptions();
5454
}
5555

56-
if (RenderingMetrics.IsMetricsSupported && _renderer.RenderingMetrics != null && (_renderer.RenderingMetrics.IsDiffDurationEnabled || _renderer.RenderingMetrics.IsStateDurationEnabled || _renderer.RenderingMetrics.IsStateExceptionEnabled))
56+
if (_renderer.RenderingMetrics != null && (_renderer.RenderingMetrics.IsDiffDurationEnabled || _renderer.RenderingMetrics.IsStateDurationEnabled || _renderer.RenderingMetrics.IsStateExceptionEnabled))
5757
{
5858
_componentTypeName = component.GetType().FullName;
5959
}
@@ -108,7 +108,7 @@ internal void RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment re
108108

109109
_nextRenderTree.Clear();
110110

111-
var diffStartTimestamp = RenderingMetrics.IsMetricsSupported && _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsDiffDurationEnabled ? Stopwatch.GetTimestamp() : 0;
111+
var diffStartTimestamp = _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsDiffDurationEnabled ? Stopwatch.GetTimestamp() : 0;
112112
try
113113
{
114114
renderFragment(_nextRenderTree);
@@ -139,7 +139,7 @@ internal void RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment re
139139
batchBuilder.UpdatedComponentDiffs.Append(diff);
140140
batchBuilder.InvalidateParameterViews();
141141

142-
if (RenderingMetrics.IsMetricsSupported && _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsDiffDurationEnabled)
142+
if (_renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsDiffDurationEnabled)
143143
{
144144
_renderer.RenderingMetrics.DiffDuration(diffStartTimestamp, _componentTypeName, batchBuilder.EditsBuffer.Count - startCount);
145145
}
@@ -249,24 +249,24 @@ private void SupplyCombinedParameters(ParameterView directAndCascadingParameters
249249
Task setParametersAsyncTask;
250250
try
251251
{
252-
var stateStartTimestamp = RenderingMetrics.IsMetricsSupported && _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateDurationEnabled ? Stopwatch.GetTimestamp() : 0;
252+
var stateStartTimestamp = _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateDurationEnabled ? Stopwatch.GetTimestamp() : 0;
253253

254254
setParametersAsyncTask = Component.SetParametersAsync(directAndCascadingParameters);
255255

256256
// collect metrics
257-
if (RenderingMetrics.IsMetricsSupported && _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateDurationEnabled)
257+
if (_renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateDurationEnabled)
258258
{
259259
_renderer.RenderingMetrics.ParametersDurationSync(stateStartTimestamp, _componentTypeName);
260260
_ = _renderer.RenderingMetrics.CaptureParametersDurationAsync(setParametersAsyncTask, stateStartTimestamp, _componentTypeName);
261261
}
262-
if (RenderingMetrics.IsMetricsSupported && _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateExceptionEnabled)
262+
if (_renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateExceptionEnabled)
263263
{
264264
_ = _renderer.RenderingMetrics.CapturePropertiesFailedAsync(setParametersAsyncTask, _componentTypeName);
265265
}
266266
}
267267
catch (Exception ex)
268268
{
269-
if (RenderingMetrics.IsMetricsSupported && _renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateExceptionEnabled)
269+
if (_renderer.RenderingMetrics != null && _renderer.RenderingMetrics.IsStateExceptionEnabled)
270270
{
271271
_renderer.RenderingMetrics.PropertiesFailed(ex.GetType().FullName, _componentTypeName);
272272
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
6+
namespace Microsoft.AspNetCore.Components.Rendering;
7+
internal class RenderingActivitySource
8+
{
9+
internal const string Name = "Microsoft.AspNetCore.Components.Rendering";
10+
internal const string OnEventName = $"{Name}.OnEvent";
11+
12+
public ActivitySource ActivitySource { get; } = new ActivitySource(Name);
13+
14+
15+
public Activity? StartEventActivity(string? componentType, string? methodName, string? attributeName, Activity? linkedActivity)
16+
{
17+
IEnumerable<KeyValuePair<string, object?>> tags =
18+
[
19+
new("component.type", componentType ?? "unknown"),
20+
new("component.method", methodName ?? "unknown"),
21+
new("attribute.name", attributeName ?? "unknown"),
22+
];
23+
IEnumerable<ActivityLink>? links = (linkedActivity is not null) ? [new ActivityLink(linkedActivity.Context)] : null;
24+
25+
var activity = ActivitySource.CreateActivity(OnEventName, ActivityKind.Server, parentId: null, tags, links);
26+
if (activity is not null)
27+
{
28+
activity.DisplayName = $"{componentType ?? "unknown"}/{methodName ?? "unknown"}/{attributeName ?? "unknown"}";
29+
activity.Start();
30+
}
31+
return activity;
32+
}
33+
public static void FailEventActivity(Activity activity, Exception ex)
34+
{
35+
if (!activity.IsStopped)
36+
{
37+
activity.SetTag("error.type", ex.GetType().FullName);
38+
activity.SetStatus(ActivityStatusCode.Error);
39+
activity.Stop();
40+
}
41+
}
42+
43+
public static async Task CaptureEventStopAsync(Task task, Activity activity)
44+
{
45+
try
46+
{
47+
await task;
48+
activity.Stop();
49+
}
50+
catch (Exception ex)
51+
{
52+
FailEventActivity(activity, ex);
53+
}
54+
}
55+
}

src/Components/Components/src/Rendering/RenderingMetrics.cs

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5-
using System.Diagnostics.CodeAnalysis;
65
using System.Diagnostics.Metrics;
76
using Microsoft.AspNetCore.Http;
87

@@ -26,29 +25,19 @@ internal sealed class RenderingMetrics : IDisposable
2625
private readonly Histogram<double> _batchDuration;
2726
private readonly Counter<long> _batchException;
2827

29-
[FeatureSwitchDefinition("System.Diagnostics.Metrics.Meter.IsSupported")]
30-
public static bool IsMetricsSupported { get; } = InitializeIsMetricsSupported();
31-
private static bool InitializeIsMetricsSupported() => AppContext.TryGetSwitch("System.Diagnostics.Metrics.Meter.IsSupported", out bool isSupported) ? isSupported : true;
28+
public bool IsEventDurationEnabled => _eventSyncDuration.Enabled || _eventAsyncDuration.Enabled;
29+
public bool IsEventExceptionEnabled => _eventException.Enabled;
3230

33-
public bool IsEventDurationEnabled => IsMetricsSupported && (_eventSyncDuration.Enabled || _eventAsyncDuration.Enabled);
34-
public bool IsEventExceptionEnabled => IsMetricsSupported && _eventException.Enabled;
31+
public bool IsStateDurationEnabled => _parametersSyncDuration.Enabled || _parametersAsyncDuration.Enabled;
32+
public bool IsStateExceptionEnabled => _parametersException.Enabled;
3533

36-
public bool IsStateDurationEnabled => IsMetricsSupported && (_parametersSyncDuration.Enabled || _parametersAsyncDuration.Enabled);
37-
public bool IsStateExceptionEnabled => IsMetricsSupported && _parametersException.Enabled;
34+
public bool IsDiffDurationEnabled => _diffDuration.Enabled;
3835

39-
public bool IsDiffDurationEnabled => IsMetricsSupported && _diffDuration.Enabled;
40-
41-
public bool IsBatchDurationEnabled => IsMetricsSupported && _batchDuration.Enabled;
42-
public bool IsBatchExceptionEnabled => IsMetricsSupported && _batchException.Enabled;
36+
public bool IsBatchDurationEnabled => _batchDuration.Enabled;
37+
public bool IsBatchExceptionEnabled => _batchException.Enabled;
4338

4439
public RenderingMetrics(IMeterFactory meterFactory)
4540
{
46-
if (!IsMetricsSupported)
47-
{
48-
// TryAddSingleton prevents trimming constructors, so we trim constructor this way
49-
throw new NotSupportedException("Metrics are not supported in this environment.");
50-
}
51-
5241
Debug.Assert(meterFactory != null);
5342

5443
_meter = meterFactory.Create(MeterName);
@@ -105,19 +94,20 @@ public RenderingMetrics(IMeterFactory meterFactory)
10594
description: "Total number of exceptions during batch rendering.");
10695
}
10796

108-
public void EventDurationSync(long startTimestamp, string? componentType, string? attributeName)
97+
public void EventDurationSync(long startTimestamp, string? componentType, string? methodName, string? attributeName)
10998
{
11099
var tags = new TagList
111100
{
112101
{ "component.type", componentType ?? "unknown" },
102+
{ "component.method", methodName ?? "unknown" },
113103
{ "attribute.name", attributeName ?? "unknown"}
114104
};
115105

116106
var duration = Stopwatch.GetElapsedTime(startTimestamp);
117107
_eventSyncDuration.Record(duration.TotalSeconds, tags);
118108
}
119109

120-
public async Task CaptureEventDurationAsync(Task task, long startTimestamp, string? componentType, string? attributeName)
110+
public async Task CaptureEventDurationAsync(Task task, long startTimestamp, string? componentType, string? methodName, string? attributeName)
121111
{
122112
try
123113
{
@@ -126,6 +116,7 @@ public async Task CaptureEventDurationAsync(Task task, long startTimestamp, stri
126116
var tags = new TagList
127117
{
128118
{ "component.type", componentType ?? "unknown" },
119+
{ "component.method", methodName ?? "unknown" },
129120
{ "attribute.name", attributeName ?? "unknown" }
130121
};
131122

0 commit comments

Comments
 (0)