使用 Simple Injector 为 Log4Net 实现一个记录器类



我已经浏览了堆栈溢出,以了解如何在 C# 应用程序中进行日志记录并保持特定于我的应用程序的使用要求。以前回答的以下问题对我有所帮助:

  • log4net 记录器包装器的实现和使用
  • 记录器包装器最佳实践
  • log4net 带 DI 简单喷油器

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

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

到目前为止我得到了什么

  • 我有一个需要void Log(LogEntry entry)方法的ILogger接口。
  • 适配器(我从上述参考来源改编了它) -public class Log4netAdapter<T> : ILogger
  • DI容器简单注射器具有以下注册:

源代码

简单的注射器 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

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

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

尽管对SRP和OCP的分析需要更多的背景信息,但我们实际上可以在这里谈谈ISP和DIP:

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

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

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

  • 稳定的依赖关系
  • 易失性依赖关系

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

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

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

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

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

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

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

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

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

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

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

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

最新更新