【问题标题】:Should logging infrastructure be injected when using IoC/DI if logging facade is used?如果使用日志门面,在使用 IoC/DI 时是否应该注入日志基础设施?
【发布时间】:2012-09-17 12:02:58
【问题描述】:

我使用 Autofac 作为我的 IoC,并且从我读过的关于 DI 主题的所有内容中,我教会了使用“构造函数注入”来显式公开类依赖项...... 但是,我也在使用 Log4Net 的日志外观(Common.Logging),并创建了 Autofac 模块来注入它。 现在,在我想要进行一些日志记录的每个类中,我都有额外的构造函数参数(参见示例 #1)......

我想知道在使用日志记录外观时是否需要记录 DI? 我知道通过构造函数签名显式暴露依赖项是一个很好的架构。 但是在日志外观的情况下,我相信以下是正确的:

  • 我仍然可以随时“换掉”日志框架
  • 恕我直言,类并不真正依赖于 Logger。如果未配置日志记录,则使用 NullLogger。这几乎是“如果你需要它就可以”与“除非你提供它否则它将无法工作”的交易......(参见示例 #2)

那么,其他人怎么看?注入日志外观是不是矫枉过正? 关于这个主题有一些 similar questions,但更笼统地说 (infrastructure) - 我主要对日志记录感兴趣....

// IoC "way"
public class MyController : BaseController
{
    private readonly ILog _logger;

    public MyController(ILog logger)
    {
        _logger = logger;
    }

    public IList<Customers> Get()
    {
        _logger.Debug("I am injected via constructor using some IoC!");
    }
}

// just use the logger "way"
public class MyController : BaseController
{
    private static readonly ILog Logger = LogManager.GetCurrentClassLogger();

    public IList<Customers> Get()
    {
        Logger.Debug("Done! I can use it!");
    }
}

【问题讨论】:

    标签: .net logging dependency-injection inversion-of-control


    【解决方案1】:

    现在,在我想做一些日志记录的每一堂课中,我都有额外的 构造函数参数

    如果您需要在系统中记录许多类,则说明您的设计有问题。要么你记录太多,要么你违反了单一职责原则,因为记录是一个横切关注点,你不应该用像记录这样的横切关注点来混淆你的类。

    半年前我回答了一个(完全不同的)问题,我的回答适用于您的问题。请阅读this

    【讨论】:

    • +1 链接的帖子...但是,为了在记录时采用您的方法,是使用装饰器(除了 DI)来实现它,这是另一个必须是的抽象实施只是为了做,好吧:) 记录...真的值得吗?根据我看到的讨论,意见的分歧主要基于天气,人们认为日志记录是“必须具有”的依赖项与“您可以使用的基础设施功能”......至少可以说是有趣的讨论......
    • @zam6ak:如果值得,那取决于你。但是,当您已经在实践这种架构时,将日志记录作为横切关注点添加将是一件轻而易举的事。而且不要忘记,如果你不喜欢这样的 SOLID 设计,你可以随时使用拦截来装饰你的服务,并使用日志拦截器。这使您不必在服务上使用通用接口。
    • 我喜欢并致力于练习 SOLID,但并不总是倾向于使用 CQRS(或类似的命令模式)来实现它。您对拦截器/过滤器提出了一个很好的观点。正如我在下面的其中一个 cmets 中提到的,我利用 Web API 过滤器来记录未处理的异常,因此我的控制器操作不会被 try/catch(Exception e) { log(e);} 逻辑...
    • 这些模式只是应用 SOLID 原则的结果。即使您不练习 CQRS,这些模式也适用。我就是这样做的。我将这种模式用于我构建的所有应用程序,但我从未练习过 CQRS。这种指挥模型使整个世界充满了新的可能性。以this article about writing highly maintainable WCF services 为例。尽管需要做更多的工作,但您可以使用 Web API 服务来做同样的事情。我对此进行了实验,并计划就此写一篇文章。
    • -1 作者说“在我想做一些日志记录的每个班级”,而不是“我想在几乎每个班级都做日志记录”
    【解决方案2】:

    我个人认为这是矫枉过正。

    理想情况下,日志记录的开销应该非常低,因此每个类单个静态记录器实例似乎是一个更好的解决方案。保持较低的开销有助于将调试级别的日志记录用于检测。

    【讨论】:

    • 静态类是自动化测试人员的噩梦。
    【解决方案3】:

    日志记录只是基础设施。注入它是矫枉过正的。我个人什至不使用抽象层。我使用库直接提供的静态类。我的动机是我不太可能在当前项目中切换日志库(但可能会切换到下一个项目)。

    但是,您在示例中使用了控制器。为什么需要登录它们?控制器只是视图和模型(业务逻辑)之间的适配器。应该不用登录了。

    您通常只登录包含业务逻辑的类,并且在顶层能够记录未处理的异常。这些是很难调试的情况,因此也是需要记录的地方。

    必须在其他地方登录表明您需要重构以正确封装您的业务逻辑。

    【讨论】:

    • controller 只是我的问题的一个例子。就个人而言,我有一个 web api 全局过滤器,它可以捕获所有未处理的异常,记录它们并执行正确的 Request.CreateErrorResponse()。
    • 创建了一个简单解释的博文:blog.gauffin.org/2012/10/when-is-a-logger-a-dependency
    • 我认为应该注入日志记录,因为它会创建对配置文件的依赖并妨碍自动化测试。
    • @Vijay:对不起,什么?如果找不到配置,任何主要的日志库都不会影响您的应用程序。它只会停止记录。所以不,你错了。
    • @jgauffin,你是对的。忘记了 :) 但是,有更干净的代码会很好!
    【解决方案4】:

    这可能是一个较旧的帖子,但我想插话。

    认为日志系统的 IoC 过于矫枉过正的想法是短视的。

    日志是一种机制,应用程序开发人员可以通过它与其他系统(事件日志、平面文件、数据库等)进行通信,而这些东西现在都是您的应用程序所依赖的外部资源。

    如果我的单元测试代码现在锁定到特定记录器,我应该如何对组件的日志记录进行单元测试?分布式系统通常使用记录器来集中源,而不是文件系统上的平面文件。

    对我来说,注入记录器与注入数据库连接 API 或外部 Web 服务没有什么不同。它是应用程序需要的资源,因此应该注入,因此您可以测试组件对所述资源的使用情况模拟所述依赖关系的能力,以独立于日志记录接收者测试我的组件的输出,对于强大至关重要单元测试。

    鉴于 IoC 容器使这种注入成为小菜一碟,在我看来,使用 IoC 来注入记录器并不是比这样做更有效的方法。

    【讨论】:

    • 一些 的情况下记录 一个真正的依赖。例如,一些数据库系统维护用于在发生故障时恢复数据的日志。如果没有此功能,数据库将无法再提供 ACID 保证,因此无法运行。
    • 同意这一点。但是,我会问这是否真的在记录。日志记录不是数据库事务日志。数据库恢复不是软件的责任。数据库恢复是数据库的责任。日志记录是开发人员输出有关软件中业务操作状态的消息的一种方式,以及该业务操作是如何成功或不成功处理的。如果我们可以接受这作为日志记录的定义,那么我之前的观点就成立了。
    • 这里的考虑是域与基础设施。如果日志出现在您的高级设计中,那就是域问题。如果需要测试,因为它构成了接口的一部分。应测试和注入域代码
    • 与其说它不是“真正的记录”,也许我们需要一些更细微的词汇。例如,根据我的经验,我遇到过诊断日志记录和遥测(往往不是域级别的,并且没有注入),以及审计日志(往往是域级别的,并且是注入的)。传统的日志框架是面向诊断日志记录,并且对日志记录操作失败时抛出异常等行为的支持非常有限。
    【解决方案5】:

    在许多应用程序中,由于整个应用程序的体系结构都需要日志记录,因此到处注入日志会使您的代码变得混乱和复杂。对Logger 对象的静态引用很好,只要您的Logger 具有某种Attach(ILoggerService) 机制,您可以在其中为运行环境附加日志服务。然后,您的静态 Logger 将它们存储在一个集合中,以便在执行日志记录命令时使用。

    在运行时引导应用程序时,附加应用程序所需的日志记录目标(文件、数据库、Web 服务等)。

    执行自动化测试时,您可以通过不附加任何内容(默认情况下)来选择不使用日志记录。如果您想分析被测系统的日志记录,请在设置测试时附上您自己的 ILoggerService instance/subsitute/mock。

    像这样的静态Logger 应该具有最小的性能问题并且不会产生意外后果,同时通过绕过依赖注入的需要来避免代码混乱。

    必须声明,您不应该对架构中的每个对象都采用这种静态对象方法;日志记录是一种特殊情况。通常在您的代码库中使用静态对象会使维护和测试变得困难,应该避免。

    【讨论】:

      【解决方案6】:

      不是任何情况下都可以应用的通用答案,但 C# 中还有第三种选择:如果日志记录是可选的,则可以将它们转换为事件。

      public class MyController : BaseController
      {
          public event Action<string> DebugMessage;
      
          public IList<Customers> Get()
          {
              DebugMessage?.Invoke("Done! I can use it!");
          }
      }
      

      这将实际日志记录的负担转移到作曲家(并且 Setter 注入会导致时间耦合),但是,如果您正在开发小型库并希望避免依赖于 System.Diagnostics.Debug,这可能值得考虑。

      您可能想引入一些界面以使注册更容易:

      // Collect this interface with your favorite auto-registering DI container
      public interface IDebugMessageEmitter
      {
          event Action<string> DebugMessage;
      }
      
      public class MyController : BaseController, IInformationEmitter
      {
          // ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多