首页 > 解决方案 > 使用 Simple Injector 为 Log4Net 实现 Logger 类

问题描述

我浏览了 Stack Overflow 以了解如何登录我的 C# 应用程序并保持特定于我的应用程序的使用要求。先前已回答的以下问题对我有所帮助:

这些实现似乎希望我将 log4net.ILog 传递给我的实现的构造函数或 log4net 的 LogImpl 的基本实现。但是,我在使用Simple Injector配置我的抽象记录器时遇到了麻烦。

正如我所看到的,我的实现运行得非常好,但我不知道可能存在哪些缺点,或者可能有其他方法来做到这一点。

到目前为止我得到了什么

源代码

简单注射器 DI 容器:

private SimpleInjector.Container container;

[SetUp]
public void SetUp()
{            
    // init log4net
    XmlConfigurator.Configure();
    container = new SimpleInjector.Container();
    container.RegisterConditional(
        typeof(ILogger),
        c => typeof(Log4netAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
        Lifestyle.Singleton,
        c => true);
}    

记录仪接口:

public interface ILogger
{
    void Log(LogEntry entry);
}

public class Log4netAdapter<T> : ILogger
{
    private readonly log4net.ILog Logger;

    public Log4netAdapter()
    {
        this.Logger = LogManager.GetLogger(typeof(T));
    }

    public void Log(LogEntry entry)
    {
        if (entry.Severity == LoggingEventType.Debug)
            Logger.Debug(entry.Message, entry.Exception);
        else if (entry.Severity == LoggingEventType.Information)
            Logger.Info(entry.Message, entry.Exception);
        else if (entry.Severity == LoggingEventType.Warning)
            Logger.Warn(entry.Message, entry.Exception);
        else if (entry.Severity == LoggingEventType.Error)
            Logger.Error(entry.Message, entry.Exception);
        else
            Logger.Fatal(entry.Message, entry.Exception);
    }
}

扩展ILogger

public static class LoggerExtensions
{
    public static void Log(this ILogger logger, string message)
    {
        logger.Log(new LogEntry(LoggingEventType.Information, message));
    }
            public static void Log(this ILogger logger, Exception exception)
    {
        logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception));
    }
}

我想知道的

我想知道这是否遵循 SOLID 原则,是否有更好的方法?如果有更好的方法,任何人都可以通过 C# 的示例向我提供它的原因。

我不喜欢什么

我的实现不允许我只调用我传递Ilogger给构造函数的任何类的特定方法。它要求我在ILogger界面上创建扩展,然后重定向到我的Log4netAdapter.

标签: c#dependency-injectionsimple-injector

解决方案


我想知道这是否遵循 SOLID 原则,是否有更好的方法?

这是否是 SOLID,很大程度上取决于应用程序的更广泛的上下文。例如,当您将您ILogger的系统注入大量类时,您很可能违反了单一职责原则开放/封闭原则。例如,请参阅此 q&a

虽然 SRP 和 OCP 的分析需要更多的上下文,但我们实际上可以在这里说一下 ISP 和 DIP:

  • ILogger接口定义了一个成员,因此遵循接口隔离原则,该原则指出抽象应该是狭窄的。
  • 您的应用程序代码不依赖于来自外部方的抽象,而是依赖于特定于应用程序的ILogger抽象,因此遵循依赖倒置原则,该原则指出抽象应该抽象的消费者拥有。

我不喜欢什么...它要求我在 ILogger 接口上创建扩展,然后重定向到我的 Log4netAdapter。

当我们应用依赖倒置原则(主要通过依赖注入)时,我们将依赖分为两个不同的组:

在一个类可以安全地直接依赖和调用任何稳定依赖的地方,您希望将任何可变依赖隐藏在抽象后面。

在您的问题的上下文中,Log4Net 的附加程序从您的应用程序代码的上下文中是Volatile Dependencies。这尤其是因为他们执行 I/O,或者正如DIPP&P所说:

  • “依赖项引入了为应用程序设置和配置运行时环境的要求。” 这意味着您必须配置 Log4net 以防止破坏您的应用程序。
  • “期望必须替换、包装、装饰或拦截类或模块”。Log4net 背后的整个想法是能够更改您想要写入日志信息的通道,因此我们当然希望能够替换行为,也用于测试目的。

这是Volatile Dependencies的两个特征,为了使我们的应用程序可维护和可测试,我们将Volatile Dependencies隐藏在抽象后面,因此是您的ILogger抽象。

然而,扩展方法ILogger不是Volatile Dependencies;它们是稳定的依赖关系。这是因为这些扩展方法内部的行为:

  • 不做任何 I/O
  • 是完全确定的
  • 不必更换(例如使用配置开关)

日志行为的 volatile 部分完全隐藏在ILogger接口后面,这允许您替换、模拟和拦截所有 volatile 行为。

因为扩展方法是稳定依赖,所以任何消费者都可以安全地依赖它们,而不会导致任何可维护性或可测试性问题。事实上,没有隐藏在抽象后面的日志记录行为的稳定部分有一些有趣的优势:

  • 它允许使用消费代码测试这些扩展方法
  • 它允许在代码或测试运行时验证输入数据。使用日志库,您经常会看到存在一些验证,但该验证存在于生产代码中,在您运行测试时会被模拟掉。

但是,这确实意味着您应该确保扩展方法保持稳定。它们不应该开始包含不确定或易变的行为。

它要求我在 ILogger 界面上创建扩展

扩展方法不是接口的一部分;它们是消费代码的一部分。这意味着您可以将扩展方法放在您喜欢的任何地方,甚至可以为代码库的不同部分使用不同的扩展方法(尽管可能不太可能用于日志记录)。


推荐阅读