File: ExceptionSummarizer.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.Diagnostics.ExceptionSummarization\Microsoft.Extensions.Diagnostics.ExceptionSummarization.csproj (Microsoft.Extensions.Diagnostics.ExceptionSummarization)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using Microsoft.Shared.Diagnostics;
using Microsoft.Shared.Text;
 
namespace Microsoft.Extensions.Diagnostics.ExceptionSummarization;
 
internal sealed class ExceptionSummarizer : IExceptionSummarizer
{
    private const string DefaultDescription = "Unknown";
    private readonly FrozenDictionary<Type, IExceptionSummaryProvider> _exceptionTypesToProviders;
 
    public ExceptionSummarizer(IEnumerable<IExceptionSummaryProvider> providers)
    {
        var exceptionTypesToProvidersBuilder = new Dictionary<Type, IExceptionSummaryProvider>();
        foreach (var exceptionSummaryProvider in providers)
        {
            foreach (var exceptionType in exceptionSummaryProvider.SupportedExceptionTypes)
            {
                exceptionTypesToProvidersBuilder.Add(exceptionType, exceptionSummaryProvider);
            }
        }
 
        _exceptionTypesToProviders = exceptionTypesToProvidersBuilder.ToFrozenDictionary();
    }
 
    public ExceptionSummary Summarize(Exception exception)
    {
        _ = Throw.IfNull(exception);
 
        var exceptionType = exception.GetType();
        var exceptionTypeName = exceptionType.Name;
 
        // find a match for the exception type or a base type thereof
        var type = exceptionType;
        while (type != null)
        {
            if (_exceptionTypesToProviders.TryGetValue(type, out var exceptionSummaryProvider))
            {
                return BuildSummary(exception, exceptionSummaryProvider, exceptionTypeName);
            }
 
            type = type.BaseType;
        }
 
        // Let's see if we get lucky with the inner exception
        if (exception.InnerException != null)
        {
            var innerExceptionType = exception.InnerException.GetType();
            if (_exceptionTypesToProviders.TryGetValue(innerExceptionType, out var innerExceptionSummaryProvider))
            {
                return BuildSummary(
                    exception.InnerException,
                    innerExceptionSummaryProvider,
                    $"{exceptionTypeName}->{innerExceptionType.Name}");
            }
        }
 
        // Now let's see if we can get something from Exception HResult
        var hresult = exception.HResult;
        var exceptionDescription = exception.InnerException != null
            ? exception.InnerException.GetType().Name
            : DefaultDescription;
        if (hresult != default)
        {
            return new ExceptionSummary(
                exceptionTypeName,
                exceptionDescription,
                hresult.ToInvariantString());
        }
 
        // final recourse, generate a default message
        return new ExceptionSummary(
            exceptionTypeName,
            exceptionDescription,
            DefaultDescription);
    }
 
    private static ExceptionSummary BuildSummary(
        Exception exception,
        IExceptionSummaryProvider exceptionSummaryProvider,
        string exceptionType)
    {
        var descriptionIndex = exceptionSummaryProvider.Describe(exception, out var additionalDetails);
 
        if (descriptionIndex >= exceptionSummaryProvider.Descriptions.Count || descriptionIndex < 0)
        {
            return new ExceptionSummary(
                exceptionType,
                DefaultDescription,
                $"Exception summary provider {exceptionSummaryProvider.GetType().Name} returned invalid short description index {descriptionIndex}");
        }
 
        return new ExceptionSummary(exceptionType, exceptionSummaryProvider.Descriptions[descriptionIndex], additionalDetails ?? DefaultDescription);
    }
}