39

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

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

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

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

// 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!");
    }
}
4

6 回答 6

34

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

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

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

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

于 2012-09-26T18:49:31.840 回答
27

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

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

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

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

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

并且鉴于 IoC 容器使这种注入成为孩子的游戏,在我看来,使用 IoC 来注入记录器并不比这样做更有效。

于 2013-10-28T23:07:07.500 回答
4

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

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

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

于 2012-09-26T15:02:04.533 回答
3

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

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

于 2012-09-26T16:56:57.060 回答
0

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

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

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

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

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

于 2020-07-22T00:53:06.937 回答
0

不是在任何情况下都可以应用的通用答案,但 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
{
    // ...
}
于 2021-12-06T14:13:18.303 回答