如何在 .NET Core 中使用结构化参数记录异常

Nik*_*ker 6 .net c# logging exception structured-data

我找不到一致的方法来记录 .NET Core 微服务中的异常。信息消息日志记录准则很简单(使用 Microsoft.Extension.Logging):

_logger.LogInformation($"Reading file {path}..."); // bad
_logger.LogInformation("Reading file {Path}...", path); // good
Run Code Online (Sandbox Code Playgroud)

第二种变体的好处是结构化信息:通过使用巧妙的日志事件路由器(例如带有 RenderedCompactJsonFormatter 的 Serilog),路径被写入日志事件的单独属性。

错误记录会让事情变得更糟。要求很明显:

  1. 错误作为异常来实现。
  2. 错误会记录在处理该错误的 catch 块中。
  3. 每个错误都以结构化形式记录。

所以,我希望错误报告看起来像

throw new MyException("Failed to read file {Path}", path);
Run Code Online (Sandbox Code Playgroud)

和错误记录 - 就像

catch(MyException e)
{
  _logger.LogError(e, "Processing error");
}
Run Code Online (Sandbox Code Playgroud)

这里的 LogError 方法记录了完整的错误描述,但它不是结构化的:路径没有添加为属性。我尝试让 MyException 保存消息模板和参数,但这种方法有两个问题:

  1. 如何基于带有命名参数的模板呈现异常消息?
  2. 当在 catch 块中处理异常时,可以处理参数。

请告诉我你是如何处理这个问题的。

Viv*_*ndi 2

.NET 中的异常不支持这样的结构化参数。由于您使用的是自定义异常,因此您可以将此功能添加到您自己的异常类中。

例如:

public class MyException : Exception
{
    public object[] Props { get; }

    public MyException()
    {
    }

    public MyException(string message)
        : base(message)
    {
    }

    // Add custom "props" parameter to your exception class
    public MyException(string message, params object[] props)
        : base(message)
    {
        Props = props;
    }

    public MyException(string message, Exception inner)
        : base(message, inner)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

现在在您的代码中您可以执行以下操作:

try
{
    var file = "image.png";
    var path = "./my/path/";
    throw new MyException("Failed to read file '{file}' in path '{path}'", file, path);
}
catch (MyException e)
{
    _logger.LogError(e, e.Message, e.Props);
}
Run Code Online (Sandbox Code Playgroud)

如果您查看日志(我在这里使用 InvisionLog),那么您应该会看到它是结构化的。

我认为这是捕获异常并以结构化方式记录它们的唯一方法。

在此输入图像描述