使用 Simple Injector 为 Log4Net 实现一个 Logger Class

Implementing a Logger Class for Log4Net, using Simple Injector

我搜索了 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 的任何 class 调用特定方法。它要求我在 ILogger 界面上创建扩展,然后重定向到我的 Log4netAdapter

I want to know if this follows SOLID principles and is there a better way?

这是否可靠,在很大程度上取决于应用程序的更广泛上下文。例如,当您将 ILogger 注入系统中的大量 class 时,您可能违反了 单一职责原则 Open/Closed原理。例如,参见 this q&a

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

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

What I dont like ... It requires me to have Extensions created on the ILogger interface, which then redirects to my Log4netAdapter.

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

如果 class 可以安全地直接依赖和调用任何 稳定依赖,您希望隐藏任何 易变依赖抽象背后。

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

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

这是 易失性依赖关系 的两个特征,为了使我们的应用程序可维护和可测试,我们将 易失性依赖关系 隐藏在抽象之后,因此你的 ILogger 抽象。

但是,ILogger 上的扩展方法不是 易失性依赖关系;它们是 稳定的依赖项 。这是因为这些扩展方法中的行为:

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

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

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

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

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

It requires me to have Extensions created on the ILogger interface

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