File: Telemetry\TelemetryErrorRecorder.cs
Web Access
Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Aspire.Dashboard.Telemetry;
 
/// <summary>
/// For recording errors to telemetry. Can be injected into components that handle errors but we still want to record to telemetry.
/// </summary>
public interface ITelemetryErrorRecorder
{
    /// <summary>
    /// Records an error, reporting distinct leaf exceptions for aggregates by type, message, and stack trace.
    /// </summary>
    void RecordError(string message, Exception exception, bool writeToLogging = false);
}
 
public sealed class TelemetryErrorRecorder : ITelemetryErrorRecorder
{
    private readonly DashboardTelemetryService _telemetryService;
    private readonly ILogger<TelemetryErrorRecorder> _logger;
 
    public TelemetryErrorRecorder(DashboardTelemetryService telemetryService, ILogger<TelemetryErrorRecorder> logger)
    {
        _telemetryService = telemetryService;
        _logger = logger;
    }
 
    public void RecordError(string message, Exception exception, bool writeToLogging = false)
    {
        if (writeToLogging)
        {
            _logger.LogError(exception, message);
        }
 
        // Blazor passes newly created disposal aggregates to its error handler without throwing them,
        // so only the child exceptions have the original stacks.
        // https://github.com/dotnet/aspnetcore/blob/v10.0.0/src/Components/Components/src/RenderTree/Renderer.cs#L1312
        if (exception is AggregateException aggregateException)
        {
            var innerExceptions = aggregateException.Flatten().InnerExceptions;
            if (innerExceptions.Count > 0)
            {
                foreach (var innerException in innerExceptions.DistinctBy(e => (e.GetType(), e.Message, e.StackTrace)))
                {
                    _telemetryService.RecordError(message, innerException, writeToLogging: false);
                }
 
                return;
            }
        }
 
        _telemetryService.RecordError(message, exception, writeToLogging: false);
    }
}