Skip to content

Commit c40323a

Browse files
authored
[blazor] Diagnostic tracing - feedback (#62286)
1 parent fe4f073 commit c40323a

19 files changed

+450
-246
lines changed

src/Components/Components/src/ComponentsActivitySource.cs

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

44
using System.Diagnostics;
5+
using Microsoft.AspNetCore.Components.Infrastructure;
56

67
namespace Microsoft.AspNetCore.Components;
78

@@ -11,115 +12,66 @@ namespace Microsoft.AspNetCore.Components;
1112
internal class ComponentsActivitySource
1213
{
1314
internal const string Name = "Microsoft.AspNetCore.Components";
14-
internal const string OnCircuitName = $"{Name}.CircuitStart";
1515
internal const string OnRouteName = $"{Name}.RouteChange";
1616
internal const string OnEventName = $"{Name}.HandleEvent";
1717

18-
private ActivityContext _httpContext;
19-
private ActivityContext _circuitContext;
20-
private string? _circuitId;
21-
private ActivityContext _routeContext;
22-
2318
private ActivitySource ActivitySource { get; } = new ActivitySource(Name);
19+
private ComponentsActivityLinkStore? _componentsActivityLinkStore;
2420

25-
public static ActivityContext CaptureHttpContext()
21+
public void Init(ComponentsActivityLinkStore store)
2622
{
27-
var parentActivity = Activity.Current;
28-
if (parentActivity is not null && parentActivity.OperationName == "Microsoft.AspNetCore.Hosting.HttpRequestIn" && parentActivity.Recorded)
29-
{
30-
return parentActivity.Context;
31-
}
32-
return default;
23+
_componentsActivityLinkStore = store;
3324
}
3425

35-
public Activity? StartCircuitActivity(string circuitId, ActivityContext httpContext)
26+
public ComponentsActivityHandle StartRouteActivity(string componentType, string route)
3627
{
37-
_circuitId = circuitId;
38-
39-
var activity = ActivitySource.CreateActivity(OnCircuitName, ActivityKind.Internal, parentId: null, null, null);
28+
var activity = ActivitySource.CreateActivity(OnRouteName, ActivityKind.Internal, parentId: null, null, null);
4029
if (activity is not null)
4130
{
42-
if (activity.IsAllDataRequested)
43-
{
44-
if (_circuitId != null)
45-
{
46-
activity.SetTag("aspnetcore.components.circuit.id", _circuitId);
47-
}
48-
if (httpContext != default)
49-
{
50-
activity.AddLink(new ActivityLink(httpContext));
51-
}
52-
}
53-
activity.DisplayName = $"Circuit {circuitId ?? ""}";
31+
var httpActivity = Activity.Current;
32+
activity.DisplayName = $"Route {route ?? "[unknown path]"} -> {componentType ?? "[unknown component]"}";
33+
Activity.Current = null; // do not inherit the parent activity
5434
activity.Start();
55-
_circuitContext = activity.Context;
56-
}
57-
return activity;
58-
}
5935

60-
public void FailCircuitActivity(Activity? activity, Exception ex)
61-
{
62-
_circuitContext = default;
63-
if (activity != null && !activity.IsStopped)
64-
{
65-
activity.SetTag("error.type", ex.GetType().FullName);
66-
activity.SetStatus(ActivityStatusCode.Error);
67-
activity.Stop();
68-
}
69-
}
70-
71-
public Activity? StartRouteActivity(string componentType, string route)
72-
{
73-
if (_httpContext == default)
74-
{
75-
_httpContext = CaptureHttpContext();
76-
}
77-
78-
var activity = ActivitySource.CreateActivity(OnRouteName, ActivityKind.Internal, parentId: null, null, null);
79-
if (activity is not null)
80-
{
8136
if (activity.IsAllDataRequested)
8237
{
83-
if (_circuitId != null)
84-
{
85-
activity.SetTag("aspnetcore.components.circuit.id", _circuitId);
86-
}
8738
if (componentType != null)
8839
{
8940
activity.SetTag("aspnetcore.components.type", componentType);
9041
}
9142
if (route != null)
9243
{
9344
activity.SetTag("aspnetcore.components.route", route);
94-
}
95-
if (_httpContext != default)
96-
{
97-
activity.AddLink(new ActivityLink(_httpContext));
98-
}
99-
if (_circuitContext != default)
100-
{
101-
activity.AddLink(new ActivityLink(_circuitContext));
45+
46+
// store self link
47+
_componentsActivityLinkStore!.SetActivityContext(ComponentsActivityLinkStore.Route, activity.Context,
48+
new KeyValuePair<string, object?>("aspnetcore.components.route", route));
10249
}
10350
}
10451

105-
activity.DisplayName = $"Route {route ?? "[unknown path]"} -> {componentType ?? "[unknown component]"}";
106-
activity.Start();
107-
_routeContext = activity.Context;
52+
return new ComponentsActivityHandle { Activity = activity, Previous = httpActivity };
10853
}
109-
return activity;
54+
return default;
55+
}
56+
57+
public void StopRouteActivity(ComponentsActivityHandle activityHandle, Exception? ex)
58+
{
59+
StopComponentActivity(ComponentsActivityLinkStore.Route, activityHandle, ex);
11060
}
11161

112-
public Activity? StartEventActivity(string? componentType, string? methodName, string? attributeName)
62+
public ComponentsActivityHandle StartEventActivity(string? componentType, string? methodName, string? attributeName)
11363
{
11464
var activity = ActivitySource.CreateActivity(OnEventName, ActivityKind.Internal, parentId: null, null, null);
65+
11566
if (activity is not null)
11667
{
68+
var previousActivity = Activity.Current;
69+
activity.DisplayName = $"Event {attributeName ?? "[unknown attribute]"} -> {componentType ?? "[unknown component]"}.{methodName ?? "[unknown method]"}";
70+
Activity.Current = null; // do not inherit the parent activity
71+
activity.Start();
72+
11773
if (activity.IsAllDataRequested)
11874
{
119-
if (_circuitId != null)
120-
{
121-
activity.SetTag("aspnetcore.components.circuit.id", _circuitId);
122-
}
12375
if (componentType != null)
12476
{
12577
activity.SetTag("aspnetcore.components.type", componentType);
@@ -132,46 +84,60 @@ public void FailCircuitActivity(Activity? activity, Exception ex)
13284
{
13385
activity.SetTag("aspnetcore.components.attribute.name", attributeName);
13486
}
135-
if (_httpContext != default)
136-
{
137-
activity.AddLink(new ActivityLink(_httpContext));
138-
}
139-
if (_circuitContext != default)
140-
{
141-
activity.AddLink(new ActivityLink(_circuitContext));
142-
}
143-
if (_routeContext != default)
144-
{
145-
activity.AddLink(new ActivityLink(_routeContext));
146-
}
14787
}
14888

149-
activity.DisplayName = $"Event {attributeName ?? "[unknown attribute]"} -> {componentType ?? "[unknown component]"}.{methodName ?? "[unknown method]"}";
150-
activity.Start();
89+
return new ComponentsActivityHandle { Activity = activity, Previous = previousActivity };
15190
}
152-
return activity;
91+
return default;
15392
}
15493

155-
public static void FailEventActivity(Activity? activity, Exception ex)
94+
public void StopEventActivity(ComponentsActivityHandle activityHandle, Exception? ex)
15695
{
157-
if (activity != null && !activity.IsStopped)
158-
{
159-
activity.SetTag("error.type", ex.GetType().FullName);
160-
activity.SetStatus(ActivityStatusCode.Error);
161-
activity.Stop();
162-
}
96+
StopComponentActivity(ComponentsActivityLinkStore.Event, activityHandle, ex);
16397
}
16498

165-
public static async Task CaptureEventStopAsync(Task task, Activity? activity)
99+
public async Task CaptureEventStopAsync(Task task, ComponentsActivityHandle activityHandle)
166100
{
167101
try
168102
{
169103
await task;
170-
activity?.Stop();
104+
StopEventActivity(activityHandle, null);
171105
}
172106
catch (Exception ex)
173107
{
174-
FailEventActivity(activity, ex);
108+
StopEventActivity(activityHandle, ex);
175109
}
176110
}
111+
112+
private void StopComponentActivity(string category, ComponentsActivityHandle activityHandle, Exception? ex)
113+
{
114+
var activity = activityHandle.Activity;
115+
if (activity != null && !activity.IsStopped)
116+
{
117+
if (ex != null)
118+
{
119+
activity.SetTag("error.type", ex.GetType().FullName);
120+
activity.SetStatus(ActivityStatusCode.Error);
121+
}
122+
if (activity.IsAllDataRequested)
123+
{
124+
_componentsActivityLinkStore!.AddActivityContexts(category, activity);
125+
}
126+
activity.Stop();
127+
128+
if (Activity.Current == null && activityHandle.Previous != null && !activityHandle.Previous.IsStopped)
129+
{
130+
Activity.Current = activityHandle.Previous;
131+
}
132+
}
133+
}
134+
}
135+
136+
/// <summary>
137+
/// Named tuple for restoring the previous activity after stopping the current one.
138+
/// </summary>
139+
internal struct ComponentsActivityHandle
140+
{
141+
public Activity? Previous;
142+
public Activity? Activity;
177143
}

src/Components/Components/src/Microsoft.AspNetCore.Components.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<Compile Include="$(SharedSourceRoot)Debugger\DictionaryDebugView.cs" LinkBase="Shared" />
2525
<Compile Include="$(SharedSourceRoot)UrlDecoder\UrlDecoder.cs" LinkBase="Shared" />
2626
<Compile Include="$(SharedSourceRoot)Metrics\MetricsConstants.cs" LinkBase="Shared" />
27+
<Compile Include="$(SharedSourceRoot)Components\ComponentsActivityLinkStore.cs" LinkBase="Shared" />
2728
</ItemGroup>
2829

2930
<Import Project="Microsoft.AspNetCore.Components.Routing.targets" />
@@ -79,7 +80,6 @@
7980

8081
<ItemGroup>
8182
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Web" />
82-
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Server" />
8383
<InternalsVisibleTo Include="Microsoft.AspNetCore.Blazor.Build.Tests" />
8484
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Authorization.Tests" />
8585
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Forms.Tests" />

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics.CodeAnalysis;
88
using System.Linq;
99
using Microsoft.AspNetCore.Components.HotReload;
10+
using Microsoft.AspNetCore.Components.Infrastructure;
1011
using Microsoft.AspNetCore.Components.Reflection;
1112
using Microsoft.AspNetCore.Components.Rendering;
1213
using Microsoft.Extensions.DependencyInjection;
@@ -15,6 +16,8 @@
1516

1617
namespace Microsoft.AspNetCore.Components.RenderTree;
1718

19+
using CategoryLink = Tuple<ActivityContext, KeyValuePair<string, object?>?>;
20+
1821
/// <summary>
1922
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
2023
/// of the Blazor framework. These types will change in a future release.
@@ -37,6 +40,8 @@ public abstract partial class Renderer : IDisposable, IAsyncDisposable
3740
private readonly ComponentFactory _componentFactory;
3841
private readonly ComponentsMetrics? _componentsMetrics;
3942
private readonly ComponentsActivitySource? _componentsActivitySource;
43+
private readonly object _activityLinksStore = new Dictionary<string, CategoryLink>(StringComparer.OrdinalIgnoreCase);
44+
internal object ActivityLinksStore => _activityLinksStore;
4045

4146
private Dictionary<int, ParameterView>? _rootComponentsLatestParameters;
4247
private Task? _ongoingQuiescenceTask;
@@ -96,6 +101,7 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory,
96101
_componentFactory = new ComponentFactory(componentActivator, this);
97102
_componentsMetrics = serviceProvider.GetService<ComponentsMetrics>();
98103
_componentsActivitySource = serviceProvider.GetService<ComponentsActivitySource>();
104+
_componentsActivitySource?.Init(new ComponentsActivityLinkStore(this));
99105

100106
ServiceProviderCascadingValueSuppliers = serviceProvider.GetService<ICascadingValueSupplier>() is null
101107
? Array.Empty<ICascadingValueSupplier>()
@@ -448,14 +454,14 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
448454
var (renderedByComponentId, callback, attributeName) = GetRequiredEventBindingEntry(eventHandlerId);
449455

450456
// collect trace
451-
Activity? activity = null;
457+
ComponentsActivityHandle activityHandle = default;
452458
string receiverName = null;
453459
string methodName = null;
454460
if (ComponentActivitySource != null)
455461
{
456462
receiverName ??= (callback.Receiver?.GetType() ?? callback.Delegate.Target?.GetType())?.FullName;
457463
methodName ??= callback.Delegate.Method?.Name;
458-
activity = ComponentActivitySource.StartEventActivity(receiverName, methodName, attributeName);
464+
activityHandle = ComponentActivitySource.StartEventActivity(receiverName, methodName, attributeName);
459465
}
460466

461467
var eventStartTimestamp = ComponentMetrics != null && ComponentMetrics.IsEventEnabled ? Stopwatch.GetTimestamp() : 0;
@@ -510,9 +516,9 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
510516
}
511517

512518
// stop activity/trace
513-
if (ComponentActivitySource != null && activity != null)
519+
if (ComponentActivitySource != null && activityHandle.Activity != null)
514520
{
515-
_ = ComponentsActivitySource.CaptureEventStopAsync(task, activity);
521+
_ = ComponentActivitySource.CaptureEventStopAsync(task, activityHandle);
516522
}
517523
}
518524
catch (Exception e)
@@ -524,9 +530,9 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
524530
ComponentMetrics.FailEventSync(e, eventStartTimestamp, receiverName, methodName, attributeName);
525531
}
526532

527-
if (ComponentActivitySource != null && activity != null)
533+
if (ComponentActivitySource != null && activityHandle.Activity != null)
528534
{
529-
ComponentsActivitySource.FailEventActivity(activity, e);
535+
ComponentActivitySource.StopEventActivity(activityHandle, e);
530536
}
531537

532538
HandleExceptionViaErrorBoundary(e, receiverComponentState);

0 commit comments

Comments
 (0)