【问题标题】:Two message templates required when structured logging exception message text结构化日志记录异常消息文本时需要两个消息模板
【发布时间】:2023-01-12 12:12:18
【问题描述】:

我怎样才能避免这种模式?我希望捕获非法状态,例如在下面的人为示例中找到的状态。记录结构化消息,然后抛出包含相同消息的异常。

public async Task<int> DoSomeWork(int numerator, int denominator)
{
  if (denominator == 0)
  {
    Logger.LogError("The division : {Numerator}/{Denominator} is invalid as the denominator is equal to zero", numerator, denominator);

    throw new ApplicationException($"The division : {numerator}/{denominator} is invalid as the denominator is equal to zero.");

  }

  //Yes the solution must work with async methods
  await Task.Delay(TimeSpan.FromSeconds(1));

  //this would have thrown a DivideByZeroException
  return (numerator / denominator);

}

我的代码中到处都是上述模式,这看起来很疯狂,但我找不到替代方案。

我想要结构化日志记录的好处,我还希望我的异常消息与日志消息保持一致。但是我不想像上面看到的那样复制我的错误消息模板字符串。

【问题讨论】:

  • 在外部范围内捕获异常并将其记录下来。无论如何,您可能应该这样做,如果您这样做,您目前最终会记录两次。
  • 如果你的重点是结构化的日志记录(因此您可以将分子和分母显式地作为值)您可以派生一个特定的自定义异常,它将两个值作为字段并在外部范围内显式地处理它,Jeroen 提到了这一点。
  • @JeroenMostert 了解,但在外部范围内捕获它时,我不再拥有所需的变量,因此创建结构化日志消息。
  • 如果您使用的是 C#10,那么您应该阅读以下文章:habr.com/en/post/591171
  • @Fildor 你是对的,这没有任何意义;虽然这是无关的。我已经编辑了代码以消除这种干扰。感谢您指出。

标签: c# .net exception error-messaging structured-logging


【解决方案1】:

一种方法是添加一个允许提供 args 集合的自定义异常,该集合又可以与结构化日志记录一起使用。还可以添加日志操作的委托,以便任何处理异常的操作都可以调用提供 ILogger 实例的操作。

public abstract class BaseStructuredLoggingException : Exception
{
    private readonly object[] _args;
        
    protected BaseStructuredLoggingException(string message, params object[] args)
        : base(message)
    {
        _args = args;
    }
        
    public Action<ILogger<T>> LogAction<T>()
    {
        return l => l.LogError(this, Message, _args);
    }
}
    
public sealed class DivideException : BaseStructuredLoggingException
{
    public DivideException(string message, params object[] args) 
        : base(message, args) 
    { }
}

然后在任何处理异常的类中

private void HandleException(Exception ex)
{
    if (ex is BaseStructuredLoggingException exception)
    {
        var log = exception.LogAction<ErrorHandler>();
        log(_logger);
    }
    else
    {
        _logger.LogError(ex, ex.Message);
    }
}

最后是您的应用程序代码

public async Task<int> DoSomeWork(int numerator, int denominator)
{
  if (denominator == 0)
  {
    throw new DivideException("The division : {Numerator}/{Denominator} is invalid as the denominator is equal to zero", numerator, denominator);
  }

  //Yes the solution must work with async methods
  await Task.Delay(TimeSpan.FromSeconds(1));

  //this would have thrown a DivideByZeroException
  return (numerator / denominator);
}

【讨论】:

    猜你喜欢
    • 2021-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    相关资源
    最近更新 更多