有没有办法在 .net 核心 ILogger 接口中记录异常数据字典中的值?

Is there a way to log the values from the Data dictionary of an exception in .net core ILogger interface?

如果我记录一个异常,其中一些 key/value 对添加到数据,这些值不会被记录。在某些情况下,它们对于问题的诊断确实很有帮助,但我看不到任何配置方法。

例如,以下控制台应用程序:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.2" />
    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.2" />
  </ItemGroup>

</Project>
class Program
{
    static void Main(string[] args)
    {
        var exception = new Exception("Oops!");
        exception.Data.Add("useful-data-key", "useful-data-value");

        ILogger<Program> logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger<Program>();

        logger.LogError(exception, "An error occurred in the console.");

        Console.WriteLine("Logged");
        Console.Read();
    }
}

结果被记录到控制台:

fail: Console.Program[0]
      An error occurred in the console.
System.Exception: Oops!

数据字典中没有值的迹象。

如果我在 ASP.NET 核心应用程序中登录 Azure Application Insight,情况也是如此。

有什么方法可以让我在日志中输出该数据?

If I log an exception with some key/value pairs added to Data, those values do not get logged.

Is there any way I can get that data to output in the logs?

source code of ConsoleLogger中,我们可以发现它并没有从Exception.Data属性中提取用户定义的key/value对并将它们显示为输出。

为达到您的要求,您可以尝试实施和使用自定义控制台记录器,如下所示。

MyCustomConsoleLoggerProvider class

public class MyCustomConsoleLoggerProvider : ILoggerProvider
{
    public void Dispose()
    {
    }

    public ILogger CreateLogger(string categoryName)
        => new CustomConsoleLogger(categoryName);
}

CustomConsoleLogger class

internal class CustomConsoleLogger : ILogger
{
    private readonly string _categoryName;

    public CustomConsoleLogger(string categoryName)
        => _categoryName = categoryName;

    public void Log<TState>(
        LogLevel logLevel, EventId eventId, TState state, Exception exception,
        Func<TState, Exception, string> formatter
    )
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        StringBuilder logBuilder = new StringBuilder();

        logBuilder.Append($"{logLevel} {_categoryName}: {state}{(exception != null ? "\n" : string.Empty)}{exception}\n");


        if (exception.Data != null)
        {
            logBuilder.Append("Exception Data: ");

            exception.Data.Cast<DictionaryEntry>().ToList().ForEach((item) =>
            {
                string key = item.Key.ToString();
                string vl = item.Value.ToString();

                logBuilder.Append($"{key}:{vl}\n");
            });
        }

        Console.WriteLine(logBuilder.ToString());

    }

    public bool IsEnabled(LogLevel logLevel)
        => true;

    public IDisposable BeginScope<TState>(TState state)
        => null;
}

在控制台应用程序中

class Program
{
    static void Main(string[] args)
    {
        var exception = new Exception("Oops!");

        exception.Data.Add("useful-data-key1", "useful-data-value1");
        exception.Data.Add("useful-data-key2", "useful-data-value2");

        ILogger<Program> logger = LoggerFactory.Create(builder =>
            {
                //builder.AddConsole();
                builder.AddProvider(new MyCustomConsoleLoggerProvider());
            }).CreateLogger<Program>();

        logger.LogError(exception, "An error occurred in the console.");

        Console.WriteLine("Logged");

        Console.Read();
    }
}

测试结果