Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
140 changes: 56 additions & 84 deletions src/Components/Components/src/ComponentsActivitySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,79 +5,34 @@

namespace Microsoft.AspNetCore.Components;

internal struct ComponentsActivityWrapper
{
public Activity? Previous;
public Activity? Activity;
}

/// <summary>
/// This is instance scoped per renderer
/// </summary>
internal class ComponentsActivitySource
{
internal const string Name = "Microsoft.AspNetCore.Components";
internal const string OnCircuitName = $"{Name}.CircuitStart";
internal const string OnRouteName = $"{Name}.RouteChange";
internal const string OnEventName = $"{Name}.HandleEvent";

private ActivityContext _httpContext;
private ActivityContext _circuitContext;
private string? _circuitId;
private ActivityContext _routeContext;
internal ActivityContext _httpActivityContext;
internal ActivityContext _routeContext;
internal ActivityContext _circuitActivityContext;
internal string? _circuitId;

private ActivitySource ActivitySource { get; } = new ActivitySource(Name);

public static ActivityContext CaptureHttpContext()
{
var parentActivity = Activity.Current;
if (parentActivity is not null && parentActivity.OperationName == "Microsoft.AspNetCore.Hosting.HttpRequestIn" && parentActivity.Recorded)
{
return parentActivity.Context;
}
return default;
}

public Activity? StartCircuitActivity(string circuitId, ActivityContext httpContext)
{
_circuitId = circuitId;

var activity = ActivitySource.CreateActivity(OnCircuitName, ActivityKind.Internal, parentId: null, null, null);
if (activity is not null)
{
if (activity.IsAllDataRequested)
{
if (_circuitId != null)
{
activity.SetTag("aspnetcore.components.circuit.id", _circuitId);
}
if (httpContext != default)
{
activity.AddLink(new ActivityLink(httpContext));
}
}
activity.DisplayName = $"Circuit {circuitId ?? ""}";
activity.Start();
_circuitContext = activity.Context;
}
return activity;
}

public void FailCircuitActivity(Activity? activity, Exception ex)
{
_circuitContext = default;
if (activity != null && !activity.IsStopped)
{
activity.SetTag("error.type", ex.GetType().FullName);
activity.SetStatus(ActivityStatusCode.Error);
activity.Stop();
}
}

public Activity? StartRouteActivity(string componentType, string route)
public ComponentsActivityWrapper StartRouteActivity(string componentType, string route)
{
if (_httpContext == default)
{
_httpContext = CaptureHttpContext();
}

var activity = ActivitySource.CreateActivity(OnRouteName, ActivityKind.Internal, parentId: null, null, null);
if (activity is not null)
{
var previousActivity = Activity.Current;
if (activity.IsAllDataRequested)
{
if (_circuitId != null)
Expand All @@ -92,24 +47,22 @@ public void FailCircuitActivity(Activity? activity, Exception ex)
{
activity.SetTag("aspnetcore.components.route", route);
}
if (_httpContext != default)
{
activity.AddLink(new ActivityLink(_httpContext));
}
if (_circuitContext != default)
if (previousActivity != null)
{
activity.AddLink(new ActivityLink(_circuitContext));
activity.AddLink(new ActivityLink(previousActivity.Context));
}
}

activity.DisplayName = $"Route {route ?? "[unknown path]"} -> {componentType ?? "[unknown component]"}";
Activity.Current = null; // do not inherit the parent activity
activity.Start();
_routeContext = activity.Context;
return new ComponentsActivityWrapper { Activity = activity, Previous = previousActivity };
}
return activity;
return default;
}

public Activity? StartEventActivity(string? componentType, string? methodName, string? attributeName)
public ComponentsActivityWrapper StartEventActivity(string? componentType, string? methodName, string? attributeName)
{
var activity = ActivitySource.CreateActivity(OnEventName, ActivityKind.Internal, parentId: null, null, null);
if (activity is not null)
Expand All @@ -132,46 +85,65 @@ public void FailCircuitActivity(Activity? activity, Exception ex)
{
activity.SetTag("aspnetcore.components.attribute.name", attributeName);
}
if (_httpContext != default)
{
activity.AddLink(new ActivityLink(_httpContext));
}
if (_circuitContext != default)
{
activity.AddLink(new ActivityLink(_circuitContext));
}
if (_routeContext != default)
{
activity.AddLink(new ActivityLink(_routeContext));
}
}

activity.DisplayName = $"Event {attributeName ?? "[unknown attribute]"} -> {componentType ?? "[unknown component]"}.{methodName ?? "[unknown method]"}";
var previousActivity = Activity.Current;
Activity.Current = null; // do not inherit the parent activity
activity.Start();
return new ComponentsActivityWrapper { Activity = activity, Previous = previousActivity };
}
return activity;
return default;
}

public static void FailEventActivity(Activity? activity, Exception ex)
public void StopComponentActivity(ComponentsActivityWrapper wrapper, Exception? ex)
{
var activity = wrapper.Activity;
if (activity != null && !activity.IsStopped)
{
activity.SetTag("error.type", ex.GetType().FullName);
activity.SetStatus(ActivityStatusCode.Error);
if (activity.IsAllDataRequested)
{
if (_circuitId != null)
{
activity.SetTag("aspnetcore.components.circuit.id", _circuitId);
}
if (_httpActivityContext != default)
{
activity.AddLink(new ActivityLink(_httpActivityContext));
}
if (_circuitActivityContext != default)
{
activity.AddLink(new ActivityLink(_circuitActivityContext));
}
if (_routeContext != default && activity.Context != _routeContext)
{
activity.AddLink(new ActivityLink(_routeContext));
}
}
if (ex != null)
{
activity.SetTag("error.type", ex.GetType().FullName);
activity.SetStatus(ActivityStatusCode.Error);
}
activity.Stop();

if (Activity.Current == null && wrapper.Previous != null && !wrapper.Previous.IsStopped)
{
Activity.Current = wrapper.Previous;
}
}
}

public static async Task CaptureEventStopAsync(Task task, Activity? activity)
public async Task CaptureEventStopAsync(Task task, ComponentsActivityWrapper wrapper)
{
try
{
await task;
activity?.Stop();
StopComponentActivity(wrapper, null);
}
catch (Exception ex)
{
FailEventActivity(activity, ex);
StopComponentActivity(wrapper, ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Web" />
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Server" />
<InternalsVisibleTo Include="Microsoft.AspNetCore.Blazor.Build.Tests" />
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Authorization.Tests" />
<InternalsVisibleTo Include="Microsoft.AspNetCore.Components.Forms.Tests" />
Expand Down
28 changes: 20 additions & 8 deletions src/Components/Components/src/RenderTree/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ private static IComponentActivator GetComponentActivatorOrDefault(IServiceProvid
?? new DefaultComponentActivator(serviceProvider);
}

internal ActivityContext LinkActivityContexts(ActivityContext httpActivityContext, ActivityContext circuitActivityContext, string? circuitId)
{
if (ComponentActivitySource != null)
{
ComponentActivitySource._httpActivityContext = httpActivityContext;
ComponentActivitySource._circuitActivityContext = circuitActivityContext;
ComponentActivitySource._circuitId = circuitId;
return ComponentActivitySource._routeContext;
}
return default;
}

/// <summary>
/// Gets the <see cref="Components.Dispatcher" /> associated with this <see cref="Renderer" />.
/// </summary>
Expand Down Expand Up @@ -448,14 +460,14 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
var (renderedByComponentId, callback, attributeName) = GetRequiredEventBindingEntry(eventHandlerId);

// collect trace
Activity? activity = null;
ComponentsActivityWrapper wrapper = default;
string receiverName = null;
string methodName = null;
if (ComponentActivitySource != null)
{
receiverName ??= (callback.Receiver?.GetType() ?? callback.Delegate.Target?.GetType())?.FullName;
methodName ??= callback.Delegate.Method?.Name;
activity = ComponentActivitySource.StartEventActivity(receiverName, methodName, attributeName);
wrapper = ComponentActivitySource.StartEventActivity(receiverName, methodName, attributeName);
}

var eventStartTimestamp = ComponentMetrics != null && ComponentMetrics.IsEventEnabled ? Stopwatch.GetTimestamp() : 0;
Expand Down Expand Up @@ -506,13 +518,13 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
{
receiverName ??= (callback.Receiver?.GetType() ?? callback.Delegate.Target?.GetType())?.FullName;
methodName ??= callback.Delegate.Method?.Name;
_ = ComponentMetrics.CaptureEventDuration(task, eventStartTimestamp, receiverName, methodName, attributeName);
_ = ComponentMetrics.CaptureEventDuration(task, eventStartTimestamp, null, null, attributeName);
}

// stop activity/trace
if (ComponentActivitySource != null && activity != null)
if (ComponentActivitySource != null && wrapper.Activity != null)
{
_ = ComponentsActivitySource.CaptureEventStopAsync(task, activity);
_ = ComponentActivitySource.CaptureEventStopAsync(task, wrapper);
}
}
catch (Exception e)
Expand All @@ -521,12 +533,12 @@ public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo? fie
{
receiverName ??= (callback.Receiver?.GetType() ?? callback.Delegate.Target?.GetType())?.FullName;
methodName ??= callback.Delegate.Method?.Name;
ComponentMetrics.FailEventSync(e, eventStartTimestamp, receiverName, methodName, attributeName);
ComponentMetrics.FailEventSync(e, eventStartTimestamp, null, null, attributeName);
}

if (ComponentActivitySource != null && activity != null)
if (ComponentActivitySource != null && wrapper.Activity != null)
{
ComponentsActivitySource.FailEventActivity(activity, e);
ComponentActivitySource.StopComponentActivity(wrapper, e);
}

HandleExceptionViaErrorBoundary(e, receiverComponentState);
Expand Down
24 changes: 11 additions & 13 deletions src/Components/Components/src/Routing/Router.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

#nullable disable warnings

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Reflection.Metadata;
Expand Down Expand Up @@ -223,12 +222,12 @@ internal virtual void Refresh(bool isNavigationIntercepted)
var relativePath = NavigationManager.ToBaseRelativePath(_locationAbsolute.AsSpan());
var locationPathSpan = TrimQueryOrHash(relativePath);
var locationPath = $"/{locationPathSpan}";
Activity? activity;
ComponentsActivityWrapper activityWrapper;

// In order to avoid routing twice we check for RouteData
if (RoutingStateProvider?.RouteData is { } endpointRouteData)
{
activity = RecordDiagnostics(endpointRouteData.PageType.FullName, endpointRouteData.Template);
activityWrapper = RecordDiagnostics(endpointRouteData.PageType.FullName, endpointRouteData.Template);

// Other routers shouldn't provide RouteData, this is specific to our router component
// and must abide by our syntax and behaviors.
Expand All @@ -241,7 +240,7 @@ internal virtual void Refresh(bool isNavigationIntercepted)
endpointRouteData = RouteTable.ProcessParameters(endpointRouteData);
_renderHandle.Render(Found(endpointRouteData));

activity?.Stop();
_renderHandle.ComponentActivitySource?.StopComponentActivity(activityWrapper, null);
return;
}

Expand All @@ -258,7 +257,7 @@ internal virtual void Refresh(bool isNavigationIntercepted)
$"does not implement {typeof(IComponent).FullName}.");
}

activity = RecordDiagnostics(context.Handler.FullName, context.Entry.RoutePattern.RawText);
activityWrapper = RecordDiagnostics(context.Handler.FullName, context.Entry.RoutePattern.RawText);

Log.NavigatingToComponent(_logger, context.Handler, locationPath, _baseUri);

Expand All @@ -279,7 +278,7 @@ internal virtual void Refresh(bool isNavigationIntercepted)
{
if (!isNavigationIntercepted)
{
activity = RecordDiagnostics("NotFound", "NotFound");
activityWrapper = RecordDiagnostics("NotFound", "NotFound");

Log.DisplayingNotFound(_logger, locationPath, _baseUri);

Expand All @@ -290,30 +289,29 @@ internal virtual void Refresh(bool isNavigationIntercepted)
}
else
{
activity = RecordDiagnostics("External", "External");
activityWrapper = RecordDiagnostics("External", "External");

Log.NavigatingToExternalUri(_logger, _locationAbsolute, locationPath, _baseUri);
NavigationManager.NavigateTo(_locationAbsolute, forceLoad: true);
}
}
activity?.Stop();

_renderHandle.ComponentActivitySource?.StopComponentActivity(activityWrapper, null);
}

private Activity? RecordDiagnostics(string componentType, string template)
private ComponentsActivityWrapper RecordDiagnostics(string componentType, string template)
{
Activity? activity = null;
ComponentsActivityWrapper activityWrapper = default;
if (_renderHandle.ComponentActivitySource != null)
{
activity = _renderHandle.ComponentActivitySource.StartRouteActivity(componentType, template);
activityWrapper = _renderHandle.ComponentActivitySource.StartRouteActivity(componentType, template);
}

if (_renderHandle.ComponentMetrics != null && _renderHandle.ComponentMetrics.IsNavigationEnabled)
{
_renderHandle.ComponentMetrics.Navigation(componentType, template);
}

return activity;
return activityWrapper;
}

private static void DefaultNotFoundContent(RenderTreeBuilder builder)
Expand Down
Loading
Loading