File: ComponentsActivitySource.cs
Web Access
Project: src\src\Components\Components\src\Microsoft.AspNetCore.Components.csproj (Microsoft.AspNetCore.Components)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
 
namespace Microsoft.AspNetCore.Components;
 
/// <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;
 
    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)
    {
        if (_httpContext == default)
        {
            _httpContext = CaptureHttpContext();
        }
 
        var activity = ActivitySource.CreateActivity(OnRouteName, 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 (componentType != null)
                {
                    activity.SetTag("aspnetcore.components.type", componentType);
                }
                if (route != null)
                {
                    activity.SetTag("aspnetcore.components.route", route);
                }
                if (_httpContext != default)
                {
                    activity.AddLink(new ActivityLink(_httpContext));
                }
                if (_circuitContext != default)
                {
                    activity.AddLink(new ActivityLink(_circuitContext));
                }
            }
 
            activity.DisplayName = $"Route {route ?? "[unknown path]"} -> {componentType ?? "[unknown component]"}";
            activity.Start();
            _routeContext = activity.Context;
        }
        return activity;
    }
 
    public Activity? StartEventActivity(string? componentType, string? methodName, string? attributeName)
    {
        var activity = ActivitySource.CreateActivity(OnEventName, 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 (componentType != null)
                {
                    activity.SetTag("aspnetcore.components.type", componentType);
                }
                if (methodName != null)
                {
                    activity.SetTag("aspnetcore.components.method", methodName);
                }
                if (attributeName != null)
                {
                    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]"}";
            activity.Start();
        }
        return activity;
    }
 
    public static void FailEventActivity(Activity? activity, Exception ex)
    {
        if (activity != null && !activity.IsStopped)
        {
            activity.SetTag("error.type", ex.GetType().FullName);
            activity.SetStatus(ActivityStatusCode.Error);
            activity.Stop();
        }
    }
 
    public static async Task CaptureEventStopAsync(Task task, Activity? activity)
    {
        try
        {
            await task;
            activity?.Stop();
        }
        catch (Exception ex)
        {
            FailEventActivity(activity, ex);
        }
    }
}