【问题标题】:Implementing a Logger Class for Log4Net, using Simple Injector使用 Simple Injector 为 Log4Net 实现 Logger 类
【发布时间】:2020-01-02 17:37:48
【问题描述】:

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

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

在我看来,我的实现运行良好,但我不知道可能存在哪些缺点,或者可能有其他方法。

到目前为止我的收获

  • 我有一个需要void Log(LogEntry entry) 方法的ILogger 接口。
  • 一个适配器(我从上面引用的来源改编它)-public class Log4netAdapter<T> : ILogger
  • DI 容器 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-injection simple-injector


【解决方案1】:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2015-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    相关资源
    最近更新 更多