c# - 使用 Simple Injector 为 Log4Net 实现 Logger 类
问题描述
我浏览了 Stack Overflow 以了解如何登录我的 C# 应用程序并保持特定于我的应用程序的使用要求。先前已回答的以下问题对我有所帮助:
这些实现似乎希望我将 log4net.ILog 传递给我的实现的构造函数或 log4net 的 LogImpl 的基本实现。但是,我在使用Simple Injector配置我的抽象记录器时遇到了麻烦。
正如我所看到的,我的实现运行得非常好,但我不知道可能存在哪些缺点,或者可能有其他方法来做到这一点。
到目前为止我得到了什么
- 我有一个
ILogger
需要void Log(LogEntry entry)
方法的接口。 - 一个适配器(我从上面引用的来源改编它) -
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
.
解决方案
我想知道这是否遵循 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 界面上创建扩展
扩展方法不是接口的一部分;它们是消费代码的一部分。这意味着您可以将扩展方法放在您喜欢的任何地方,甚至可以为代码库的不同部分使用不同的扩展方法(尽管可能不太可能用于日志记录)。
推荐阅读
- python - 在使用 rabbitmq 服务器在 django 频道层中添加一个组时?rabbitmq服务器连接不上
- javascript - 加载页面以禁用复选框(使用jquery)后,如何使javascript文件在mvc中工作?
- portforwarding - 为什么端口转发在华为路由器中不起作用
- objective-c - NSURLSession dataTaskWithRequest 返回 nil 数据
- c - 为什么 strlen() 函数给出错误的值
- c# - 尝试为用户“chuacw”查找角色时出错。可能的原因是无效的客户端 cookie 或用户重命名
- php - 生成所有案例NUMBER LETTER NUMBER - php
- c++ - 存储可变参数和回调以供以后使用
- loops - 如何在此 AHK 脚本上设置开关?
- python - Pyplot: Drawing figure in a custom scale (both x and y)