阅读Steven's回答here之后,它让我想到了切换日志记录系统以及可能的PITA。接口的简单性是我最喜欢的,它是原始的,没有其他项目可以引入,除了你为编写事件而编写的代码。我修改了原始代码,以便我可以在LogEntry的context参数中传入类名:
public interface ILogger
{
void Log(LogEntry entry);
}
public enum LoggingEventType { Debug, Information, Warning, Error, Fatal };
public class LogEntry
{
public readonly LoggingEventType Severity;
public readonly string Message;
public readonly Exception Exception;
public readonly Type Context;
public LogEntry(LoggingEventType severity,
string message,
Exception exception = null,
Type context = null)
{
if (message == null) throw new ArgumentNullException("message");
if (message == string.Empty) throw new ArgumentException("empty", "message");
this.Severity = severity;
this.Message = message;
this.Exception = exception;
this.Context = context;
}
}
问题#1:传入Type / context param会出现什么问题吗?
这个post也为编写基于Log4net的适配器提供了一些启示,并且是我的NLog适配器的基础,尽管我没有使用ILogger的构造函数注入。
class NLogAdapter : ILogger
{
public void Log(LogEntry entry)
{
NLog.Logger log;
if (entry.Context != null)
{
log = NLog.LogManager.GetLogger(entry.Context.GetType().Namespace);
}
else
{
log = NLog.LogManager.GetLogger("DefaultLogger");
}
switch (entry.Severity)
{
case LoggingEventType.Debug:
log.Debug(entry.Exception, entry.Message);
break;
case LoggingEventType.Information:
log.Info(entry.Exception, entry.Message);
break;
case LoggingEventType.Warning:
log.Warn(entry.Exception, entry.Message);
break;
case LoggingEventType.Error:
log.Error(entry.Exception, entry.Message);
break;
case LoggingEventType.Fatal:
log.Fatal(entry.Exception, entry.Message);
break;
default:
throw new ArgumentOutOfRangeException(nameof(entry));
}
}
}
问题2:我对每次调用使用日志管理器有点不确定,这是获取NLog Logger实例的最正确方法吗?您可以给我任何其他建议吗?
注意:此适配器可能是DI容器中的单例,也可以用于/制作静态类。
谢谢你, 斯蒂芬
答案 0 :(得分:2)
我对每次调用使用日志管理器有点不确定,这是获取NLog Logger实例的最正确方法吗?您可以给我任何其他建议吗?
更典型的设计(和高性能)设计是一种创建通用实现的设计,并注入一个封闭的通用单例实现,其泛型参数等于它注入的类,如下所示answer:
class NLogAdapter<T> : ILogger
{
private static readonly NLog.Logger log =
NLog.LogManager.GetLogger(typeof(T).FullName);
}
这不仅可以防止您在每次调用时解析NLog记录器,还可以防止您必须将上下文传递给LogEntry。
将其注入消费者,看起来像这样:
new ProductController(new NLogAdapter<ProductController>())
new HomeController(new NLogAdapter<HomeController>())
如果您使用的是DI容器,则取决于您使用的容器,必须如何配置。例如,使用Simple Injector,可以按如下方式进行上下文注册:
container.RegisterConditional(typeof(ILogger),
c => typeof(NLogAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
当日志记录适配器是单例时,意味着开销减少到最小。
问题#1:传入Type / context param会出现什么问题吗?
在制作记录器实现上下文时,无需在记录期间传递上下文这样的上下文信息。
大警告:不要使ILogger
抽象泛型(在他们的日志库中作为Microsoft did),这会使消费者及其测试变得复杂。这将是一个严重的设计缺陷。