【问题标题】:Logging structure - How to structure when every single method has to be logged?日志记录结构 - 当必须记录每个方法时如何构建?
【发布时间】:2018-03-08 22:48:13
【问题描述】:

我对我们的日志记录看起来有多么臃肿有疑问。

我们在这样的结构中记录每一个方法:

namespace MyNamespace
{
  public class MyClass
  {
    private readonly ILogger _logger;

    public MyClass(ILogger logger)
    {
      _logger = logger;
    }

    public string AppendString(string originalText, string addedText)
    {
      using (new Tracer(_logger).TraceMethod(
        "MyNameSpace.MyClass.MyMethod()",
        () => new Dictionary<string, object>
        {
          {"originalText", originalText},
          {"addedText", addedText}
        }))
      {
        _logger.WriteInformation("Some extra info");
        return originalText + addedText;
      }
    }
  }

  public class Tracer : IDisposable
  {
    private readonly ILogger _logger;
    private string _methodName;

    public Tracer(ILogger logger)
    {
      _logger = logger;
    }

    public Tracer TraceMethod(string methodName, Func<IDictionary<string, object>> arguments)
    {
      _logger.WriteVerbose($"Entering method {methodName} with arguments {arguments}");
      _methodName = methodName;
      return this;
    }

    public void Dispose()
    {
      _logger.WriteVerbose($"Exiting method {_methodName}");
    }
  }
}

所以我们要记录:

  • 方法条目 - 总是

  • 方法退出 - 总是

  • 额外信息 - 适当时

但是,正如您所看到的,这个将 2 个字符串合并在一起的小逻辑变得非常臃肿。在每一种方法中看到这种膨胀是非常令人沮丧的。

我们研究了 Fody 和 Postsharp 等框架中的 Aspects。使用这些框架,我们将能够在所有方法上添加 [LogMethod] 属性,这将自动添加日志记录 OnMethodEntry 和 OnMethodExit。这看起来很有趣,但我们仍然会遇到一些问题。

  1. 我们如何才能在方法对方法的基础上记录我们认为合适的参数?这会有所不同,我们当然不想记录所有输入参数,因为它可能是一个巨大的 xml。我们还希望将这些参数记录在与“OnMethodEntry”日志相同的日志消息中。

  2. 我们仍然需要一个 ILogger 实例作为方法注入。 ILogger 的实例化添加了一个会话 ID。这个之前设置的 sessionId 很重要。有没有办法以不同的方式解决 ILogger?如果我们不将 ILogger 发送到构造函数,我们如何能够使用相同的 ILogger 实例在方法内部登录?

【问题讨论】:

  • 这不是一个答案,而是一个建议。为什么要重新发明轮子?您不能使用其中一种现有的记录器,例如 Serilog 吗?它是免费的,而且超级容易使用。

标签: c# logging design-patterns postsharp fody


【解决方案1】:

PostSharp Diagnostics 通过以下方式解决这些情况:

  1. 参数的“智能”格式化:

    一个。您可以实现custom formatter 并包含您想要的逻辑。但是,格式化程序只会接收参数值,而不是参数元数据。

    b.如果您的格式化逻辑需要依赖于参数元数据(例如,您定义了一个 custom 自定义属性),您可以实现我们所说的 custom logging back-end,然后您将覆盖 SetParameter method

  2. 使用注入的ILogger:

    一个。我真的不建议将记录器作为依赖项注入。尽管它是正统的,但与面向方面的解决方案一起使用是不切实际的。 PostSharp Diagnostics 在后台为每种类型创建一个 ILogger,而不是针对每个实例。

    b.如果您选择不注入 ILogger,PostSharp Diagnostics 允许您轻松混合自动日志记录和manual log records。手动记录将显示在正确的上下文中。

    c。也就是说,您可以创建一个 custom logging back-end(或适配器)来使用您的实例级 ILogger。你需要将你的记录器暴露给一个接口,比如 ILoggable。然后您的自定义适配器可以使用 ILoggable.ILogger。这没有什么困难。但是,它不能很好地与 2.b 结合,因为使用我们的框架发出的手动日志记录不包含有关 this 实例的信息(从 PostSharp 5.0 开始)。但是,您可以使用自己的 ILogger,而不是使用 PostSharp 的 Logger 类记录自定义记录。

【讨论】:

    【解决方案2】:

    考虑Decorator 模式,您可以在其中创建一个仅包含日志记录代码的LoggingMyClass,然后委托给真正的MyClass 来完成这项工作。这是一个简洁的模式,但是根据您需要装饰的类的数量,代码量会迅速增长。但是,它将允许您自定义所需的日志记录。

    减少代码量的另一种方法是使用 Castle 的 DynamicProxy 之类的东西进行拦截。这不是一种易于访问的技术,并且可能无法为您提供所需的定制。不过值得调查。

    如果我们不向构造函数发送 ILogger,我们如何能够使用相同的 ILogger 实例在方法内部登录?

    您可以考虑为记录器使用AmbientContext。但是,我在最新的book 中注意到,这种模式已经失宠了。

    【讨论】:

      【解决方案3】:

      您可以考虑使用方面和自定义注释来指定要记录的参数(如果有)和反射来处理注释的解决方案。我没有完全理解您的第二个问题,但在我看来,在注释处理器中以某种方式处理它更合适。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-24
        • 1970-01-01
        • 2011-12-06
        • 2018-12-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多